MySQL query loads forever - mysql

I have a table with 1v1 matches like this:
match_number|winner_id|loser_id
------------+---------+--------
1 | 1 | 2
2 | 2 | 3
3 | 1 | 2
4 | 1 | 4
5 | 4 | 1
and I would like to get something like this:
player|matches_won|matches_lost
------+-----------+------------
1 | 3 | 1
2 | 1 | 2
3 | 0 | 1
4 | 1 | 1
My MySQL Query looks like this
SELECT win_matches."winner_id" player, COUNT(win_matches."winner_id") matches_won, COUNT(lost_matches."loser_id") matches_lost FROM `matches` win_matches
JOIN `matches` lost_matches ON win_matches."winner_id" = lost_matches."winner_id"
I don't know what I did wrong, but the query just loads forever and doesn't return anything

You want to unpivot and then aggregate:
select player_id, sum(is_win), sum(is_loss)
from ((select winner_id as player_id 1 as is_win, 0 as is_loss
from t
) union all
(select loser_id, 0, 1
from t
)
) wl
group by player_id;
Your query is simply not correct. The two counts will produce the same same value -- COUNT(<expression>) returns the number of non-NULL rows for that expression. Your two counts return the same thing.
The reason it is taking forever is because of the Cartesian product problem. If a player has 10 wins and 10 losses, then your query produces 100 rows -- and this gets worse for players who have played more often. Processing all those additional rows takes time.
If you have a separate players table, then correlated subqueries may be the fastest method:
select p.*,
(select count(*) from t where t.winner_id = p.player_id) as num_wins,
(select count(*) from t where t.loser_id = p.player_id) as num_loses
from players p;
However, this requires two indexes for performance on (winner_id) and (loser_id). Note these are separate indexes, not a single compound index.

You are joining the same table twice.
Both the alias win_matches and lost_matches are on the table matches, causing your loop.
You probably don't need separate tables for win and losses, and could do both in the same table by writing one or zero in a column for each.
I don't to change your model too much and make it difficult to understand, so here is a slight modification and what it could look like:
SELECT m."player_id" player,
SUM(m."win") matches_won,
SUM(m."loss") matches_lost
FROM `matches` m
GROUP BY player_id
Without a join, all in the same table with win and loss columns. It looked to me like you wanted to know the number of win and loss per player, which you can do with a group by player and a sum/count.

Related

Counting all the wins in two columns using two columns?

Let's take this table for an example...
m_tid | m_tid2 | m_hteam_score | m_ateam_score
2 5 69 30
5 2 0 5
I'm bad at custom making tables, sorry...
So let's take this data, now m_tid and m_tid2 are columns for TID's that are in a separate table of their own.
Now what I want to do, is collect the score for team id2 (or team id1) of all the scores... How would I count two columns for whether or not the team is on m_tid and m_tid2
I don't have a query made, but I wouldn't know how I would go about making a query for this anyways. :(
The expected results would be something like this
m_tid | m_tid_score | m_tid2 | m_tidscore2
5 35 2 69
If you want to get the total score for each team, here is one method using correlated subqueries:
select t.*,
(coalesce((select sum(s.m_hteam_score) from scores s where s.m_tid = t.tid), 0) +
coalesce((select sum(s.m_ateam_score) from scores s where s.m_tid2 = t.tid), 0)
) as totalscore
from teams t;
Here's another option using conditional aggregation:
select o.id, sum(case when y.m_tid = o.id then y.m_hteam_score
when y.m_tid2 = o.id then y.m_ateam_score
else 0 end) score
from othertable o
join yourtable y on o.id in (y.m_tid, y.m_tid2)
group by o.id

SQL Limit to number and if equal

Can I limit rows that I displaying depending on if number in row equal to show all equal rows. For example I have database that contains football statistics, and I want to display first five scorers in one season but ib some seasons some scorers on third place have same number of goals and my query doesnt display it all beacuse i limited to sql clause limit to 5. Is there a way to tell in some clause to show all rows that are equal number of goals?
For example list of goalscorers
Goalscorer1 7 goals
Goalscorer2 6
Goalscorer3 4
Goalscorer4 3
Goalscorer5 3
...and there are goalscorers 6 and 7 with also 3 goals that are not displayed
You need to decide what is the controlling factor, limit that factor in a subquery, then list the facts related to it. Here the controlling factor is the number of goals, and the related facts are the players who kicked that number of goals.
see this SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE Table1
(`Player` varchar(20), `Goals` int)
;
INSERT INTO Table1
(`Player`, `Goals`)
VALUES
('Goalscorer1', 7),
('Goalscorer2', 6),
('Goalscorer3', 4),
('Goalscorer6', 3),
('Goalscorer7', 3),
('Goalscorer4', 3),
('Goalscorer5', 3),
('Goalscorer11', 1)
;
Query 1:
select
t.*
from table1 t
inner join (
select distinct Goals from table1
order by goals DESC
limit 4
) sq on t.Goals = sq.Goals
Results:
| Player | Goals |
|-------------|-------|
| Goalscorer1 | 7 |
| Goalscorer2 | 6 |
| Goalscorer3 | 4 |
| Goalscorer6 | 3 |
| Goalscorer7 | 3 |
| Goalscorer4 | 3 |
| Goalscorer5 | 3 |
My re-worked version of the pastebin query referenced below:
SELECT
t.*
FROM jos_playerstats t
INNER JOIN (
SELECT DISTINCT
jos_playerstats.goals
FROM jos_playerstats
WHERE jos_playerstats.idSeason = '".$urlID."'
ORDER BY
jos_playerstats.goals DESC
LIMIT 4
) sq ON t.goals = sq.goals
WHERE t.idSeason = '".$urlID."'
The subquery MUST ONLY contain a distinct list of goals, no other columns at all. Also: Please don't get into the habit of adding distinct into every query because performance will get worse. Only use "select distinct" when you truly need it and this should be rare and usually associated with one or very few columns.

Mysql count with case when statement

Consider:
SELECT(count(c.id),
case when(count(c.id) = 0)
then 'loser'
when(count(c.id) BETWEEN 1 AND 4)
then 'almostaloser'
when(count(c.id) >= 5)
then 'notaloser'
end as status,
...
When all is said and done, the query as a whole produces a set of results that look similar to this:
Count | status
--------|-------------
2 | almostaloser //total count is between 2 and 4
--------|-------------
0 | loser // loser because total count = 0
--------|-------------
3 | almostaloser //again, total count between 2 and 4
--------|-------------
What I would like to achieve:
a method to reatain the information from the above table, but add a third column that will give a total count of each status, something like
select count(c.id)
case when(count(c.id) = 0 )
then loser as status AND count how many of the total count does this apply to
results would look similar to:
Count | status |total_of each status |
--------|-------------|---------------------|
2 | almostaloser| 2 |
--------|-------------|---------------------|
0 | loser | 1 |
--------|-------------|---------------------|
3 | almostaloser| 2 |
--------|-------------|----------------------
I've been told this could be achieved using a derived table, but i've not yet been able to get them both, only one or the other.
This can be achieved with this query (you must place your original query as subquery in two places):
SELECT t1.*, t2.total_of_each_status
FROM (
-- put here your query --
) t1
INNER JOIN (
SELECT status, count(*) AS total_of_each_status
FROM (
-- put here your query --
) t2
GROUP BY status
) t2 ON t2.status = t1.status

how to find duplicate count without counting original

I need to count the number of duplicate emails in a mysql database, but without counting the first one (considered the original). In this table, the query result should be the single value "3" (2 duplicate x#q.com plus 1 duplicate f#q.com).
TABLE
ID | Name | Email
1 | Mike | x#q.com
2 | Peter | p#q.com
3 | Mike | x#q.com
4 | Mike | x#q.com
5 | Frank | f#q.com
6 | Jim | f#q.com
My current query produces not one number, but multiple rows, one per email address regardless of how many duplicates of this email are in the table:
SELECT value, count(lds1.leadid) FROM leads_form_element lds1 LEFT JOIN leads lds2 ON lds1.leadID = lds2.leadID
WHERE lds2.typesID = "31" AND lds1.formElementID = '97'
GROUP BY lds1.value HAVING ( COUNT(lds1.value) > 1 )
It's not one query so I'm not sure if it would work in your case, but you could do one query to select the total number of rows, a second query to select distinct email addresses, and subtract the two. This would give you the total number of duplicates...
select count(*) from someTable;
select count(distinct Email) from someTable;
In fact, I don't know if this will work, but you could try doing it all in one query:
select (count(*)-(count(distinct Email))) from someTable
Like I said, untested, but let me know if it works for you.
Try doing a group by in a sub query and then summing up. Something like:
select sum(tot)
from
(
select email, count(1)-1 as tot
from table
group by email
having count(1) > 1
)

Group by - Overriding default behaviour of deciding row under each group in result

Extending further from this question Query to find top rated article in each category -
Consider the same table -
id | category_id | rating
---+-------------+-------
1 | 1 | 10
2 | 1 | 8
3 | 2 | 7
4 | 3 | 5
5 | 3 | 2
6 | 3 | 6
There is a table articles, with fields id, rating (an integer from 1-10), and category_id (an integer representing to which category it belongs). And if I have the same goal to get the top rated articles in each query (this should be the result):-
Desired Result
id | category_id | rating
---+-------------+-------
1 | 1 | 10
3 | 2 | 7
6 | 3 | 6
Extension of original question
But, running the following query -
SELECT id, category_id, max( rating ) AS max_rating
FROM `articles`
GROUP BY category_id
results into the following where everything, except the id field, is as desired. I know how to do this with a subquery - as answered in the same question - Using subquery.
id category_id max_rating
1 1 10
3 2 7
4 3 6
In generic terms
Excluding the grouped column (category_id) and the evaluated columns (columns returning results of aggregate function like SUM(), MAX() etc. - in this case max_rating), the values returned in the other fields are simply the first row under every grouped result set (grouped by category_id in this case). E.g. the record with id =1 is the first one in the table under category_id 1 (id 1 and 2 under category_id 1) so it is returned.
I am just wondering is it not possible to somehow overcome this default behavior to return rows based on conditions? If mysql can perform calculation for every grouped result set (does MAX() counting etc) then why can't it return the row corresponding to the maximum rating. Is it not possible to do this in a single query without a subquery? This looks to me like a frequent requirement.
Update
I could not figure out what I want from Naktibalda's solution too. And just to mention again, I know how to do this using a subquery, as again answered by OMG Ponies.
Use:
SELECT x.id,
x.category_id,
x.rating
FROM YOUR_TABLE x
JOIN (SELECT t.category_id,
MAX(t.rating) AS max_rating
FROM YOUR_TABLE t
GROUP BY t.category_id) y ON y.category_id = x.category_id
AND y.max_rating = x.rating