Mysql return matching users in subqueries - mysql

I'm looking for a way to write one query to compare the results of multiple mysql subqueries, and return users that are in each query.
I have a that contains fantasy football stats for players. To simplify, in this case there are 3 columns I'm using: player, points, year.
I'm looking to run a query that returns a list of players who finished in the top 50 (based on points) in both 2010 and 2011.
I've done lots of searching around on playing with subqueries, doing joins on one table, etc but am still coming up at a loss on how to approach this.

You can do something like this:
SELECT a.player
FROM (SELECT player FROM players WHERE Year = 2010 ORDER BY points DESC LIMIT 50) a
JOIN
(SELECT player FROM players WHERE Year = 2011 ORDER BY points DESC LIMIT 50) b
ON a.player = b.player

Here is an example. I assumed that you calculate top50 based on sum of points and you have several entries for each player in each year.
select y2010.player
from (
select player, sum from (
select st1.player player, sum(st1.points) sum from stats st1 where st1.year = 2010 group by st1.player order by sum desc
) t1 limit 50 offset 0
) y2010, (
select player, sum from (
select st1.player player, sum(st1.points) sum from stats st1 where st1.year = 2011 group by st1.player order by sum desc
) t1 limit 50 offset 0
) y2011
where y2010.player = y2011.player

You can use a UNION ALL, this will get you the Top 50 in both years and put them in the same result set, no joining required:
(
select player, year, points
from players
where year = 2010
order by points desc
limit 50
)
union all
(
select player, year, points
from players
where year = 2011
order by points desc
limit 50
);

It's slightly ambiguous whether you want:
all players who finished in the top 50 in 2010, as well as all players who finished in the top 50 in 2011:
SELECT *
FROM scores
WHERE year = 2010
AND points >= (SELECT MIN(points) FROM (
SELECT points
FROM scores
WHERE year = 2010
ORDER BY points DESC
LIMIT 50
) t)
UNION ALL
SELECT *
FROM scores
WHERE year = 2011
AND points >= (SELECT MIN(points) FROM (
SELECT points
FROM scores
WHERE year = 2011
ORDER BY points DESC
LIMIT 50
) t)
all players who finished in the top 50 in both 2010 and 2011, in which case you'll need to further group the results:
SELECT player
FROM (
-- query as above
) t
GROUP BY player
HAVING COUNT(DISTINCT year) = 2

Related

mysql select greatest value from multiple sum

I am using the following results table
Results Table
__________________________________________
player_a| player_b | player_c | year
________|________ |__________|___________|
100 150 150 2015
100 -50 -50 2015
100 350 250 2014
200 350 250 2014
What i would like to do is get the sum for each player per year.
Using the following Select i get the desired results.
SELECT (
SELECT SUM( player_a )
FROM `results`
WHERE year =2015
) AS player_a_2015, (
SELECT SUM( player_a)
FROM `results`
WHERE year =2014
) AS player_a_2014
How can i get the maximum sum result only ?
ie For player_a should be 300
SELECT SUM(player_a) AS spa
FROM results
GROUP BY year
ORDER BY spa DESC
LIMIT 1
You may wanna use,
SELECT MAX(SUM_A),YEAR FROM (
SELECT SUM( player_a ) as SUM_A, YEAR
FROM results
GROUP BY YEAR
) A_SCORE;
This will do it in one go(obviously for one player).
This should do it.
Make sure you wrap your OR condition in parenthesis so it evaluates them both.
SELECT SUM(player_a) AS sum_score
FROM results
WHERE (year = '2015' OR year = '2014')
EDIT
If you're wanting the maximum score by year, and then to sum that - this should be OK.
SELECT MAX(max_sum_score) AS max_sum_score
FROM ( SELECT year
, SUM(player_a) AS max_sum_score
FROM results
WHERE (year = '2015' OR year = '2014')
GROUP BY year
) a

Top 5 records from each week [duplicate]

This question already has answers here:
Using LIMIT within GROUP BY to get N results per group?
(14 answers)
Closed 8 years ago.
I have a table where I record multiple scores from users daily. I'm trying to create a query where I get the distinct top 5 weekly winners for each week that passes...
Is it that I need to do a sub query grouping results in both max score and date week? or do I need to do 2 sub queries one for the date another for max score then use the outer query to group?
Well the table structure would be:
NAME,
SCORE,
DATE
I came up with this
SELECT *
FROM `highscores`
WHERE id IN ((SELECT id
FROM highscores
WHERE WEEK(date) IN (SELECT DISTINCT WEEK(date)
FROM highscores)
ORDER BY score DESC))
GROUP BY email
ORDER BY date, score DESC
But apparently I can't use LIMIT in sub-queries
I think this should work for you. It should also bring back ties, in the event there are any (let's say the 5th highest score for one week were a tie between 2 people, this would bring them both back for that week, and so you'd have 6 rows for that week).
select *
from highscores x
where x.score >=
(select max(e.score)
from highscores e
where week(e.date) = week(x.date)
and e.score <
(select max(d.score)
from highscores d
where week(d.date) = week(x.date)
and d.score <
(select max(c.score)
from highscores c
where week(c.date) = week(x.date)
and c.score <
(select max(b.score)
from highscores b
where week(b.date) = week(x.date)
and b.score <
(select max(a.score)
from highscores a
where week(a.date) = week(x.date))))))
order by date, score desc

SQL query to return the three highest values for one column "grouped" by another column

Let's say I have a table like this:
Player Score
A 5
B 4
A 3
B 2
A 1
B 1
A 2
B 3
A 4
B 5
I need an SQL query that will return the three highest scores per player in descending order "grouped" by player i.e.
Player Score
A 5
A 4
A 3
B 5
B 4
B 3
Very grateful for any pointers.
This is old-fashioned (read: basic sql) way of producing top-n per group. You might join the table to itself on group condition (here it is player) and pick records with higher score on right side; if there are three or less such records, the row is one of top n rows per group.
select player.player, player.score
from Player
left join Player p2
on p2.player = player.player
and p2.score > player.score
group by player.player, player.score
having count(distinct p2.score) < 3
order by 1, 2 desc
Alternative version you might check, using not exists:
select player, score
from player
where not exists
(
select p2.player
from Player p2
where p2.player = player.player
and p2.score > player.score
group by p2.player
having count(distinct p2.score) > 3
)
order by 1, 2 desc
This two versions differ in presentation of ties - while first one returns one row (by nature of group by) and needs to be joined back to original table to show all records, second one works directly from original table showing all data and ties at once.
You can find Demo at Sql Fiddle.
in SQL server:
select p.player, p.score
from PS p
where p.score in (select top 3 score from PS
where player = p.player order by score desc)
order by p.player asc, p.score desc
in MySql:
select p.player, p.score
from PS p
where p.score in (select score from PS
where player = p.player order by score desc limit 3)
order by p.player asc, p.score desc
I think what you are looking for can be found here:
http://www.sql-ex.ru/help/select16.php
Basically, the best solution uses the RANK function. Here is the example code from the site:
SELECT maker, model, type FROM
(
SELECT maker, model, type, RANK() OVER(PARTITION BY type ORDER BY model) num
FROM Product
) X
WHERE num <= 3
You would just need to modify the Partition By section to order by your score in descending order.
EDIT
Based upon the information that you will be using MySQL, you will need to make some modifications to the above query (which works with Microsoft SQL). You need to replace the RANK function with your own RANK implementation. It isn't that hard. Complete instructions can be found here:
http://thinkdiff.net/mysql/how-to-get-rank-using-mysql-query/
That will show you how to implement a counter that can give you a rank number.
Depending on what DBMS you use, you may be able to use row_number in some form
In SQL Server 2008 you can use
create table #player
( Player char, Score int )
insert into #player (Player, Score) Values
('A',5),('B',4),('A',3),('B',2),('A',1),('B',1),('A',2),('B',3),('A',4),('B',5)
select * from #player
select Player, Score from
(
select *, ROW_NUMBER() over(partition by Player order by Score desc) as rowNo
from #player
) as tmp
where tmp.rowNo <= 3
drop table #player

Get User Rank Based on Sum with Pagination

There are several rank posts out there but I have yet to see one dealing with when the results are paginated and when the ranking criteria (in this case: points) is equal to the previous user. I have tried a few of the pre-existing examples but none have worked.
I have a table called "users" with the column "id". I also have a table called "points" with the columns "user_id" and "amount".
I need:
1.) Users with duplicate sum of points to have the same rank
Points Table
user_id amount
1 10
2 20
1 5
3 20
3 -5
4 5
Rank should be
rank user_id total
1 2 20
2 1 15
2 3 15
3 4 5
2.) Needs to maintain the ranking from one page to another so the rank has to be gathered in the query and not the resulting PHP.
3.) Display ALL users not just ones with rows in the points table because some users have 0 points and I want to display them last.
Right now I'm just listing the users in order of their points but their rank is not gathered because it wasn't working.
$getfanspoints = mysql_query("SELECT DISTINCT id,
(SELECT SUM(amount) AS points FROM points WHERE points.user_id = users.id) AS points
FROM users
ORDER BY points DESC LIMIT $offset, $fans_limit", $conn);
I've read these solutions and none have worked.
[Roland's Blog][1]
[How to get rank based on SUM's][2]
[MySQL, get users rank][3]
[How to get rank using mysql query][4]
and a few others whose link I can't find right now.
Any suggestions?
[EDIT]
I used ypercube's bottom answer.
SELECT COUNT(*) AS rank
, t.user_id
, t.total
FROM
( SELECT user_id
, SUM(amount) AS total
FROM points
GROUP BY user_id
) AS t
JOIN
( SELECT DISTINCT
SUM(amount) AS total
FROM points
GROUP BY user_id
) AS dt
ON
t.total <= dt.total
GROUP BY t.user_id
ORDER BY rank
, user_id
But the above may be really slow with a big table and points awarded often. It might be really better to have just this and calculate the ranks in your application code:
SELECT users.id AS user_id
, SUM(amount) AS total
FROM
users
LEFT JOIN
points
ON points.user_id = users.id
GROUP BY users.id
ORDER BY total DESC
, user_id
This will work, too (edited, to work with the users table and with OFFSET):
SELECT *
FROM
( SELECT
#rank := #rank + (#t <> total) AS rank
, user_id
, #t := total AS total
FROM
( SELECT users.id AS user_id
, COALESCE(SUM(amount),0) AS total
FROM users
LEFT JOIN points
ON users.id = points.user_id
GROUP BY users.id
) AS o
CROSS JOIN
( SELECT #rank := 0, #t := -999999
) AS dummy
ORDER BY total DESC
, user_id
) tmp
LIMIT x OFFSET y

Selecting sum of single column depending on two columns on same table

I have a table very similar to the one below. p1 and p2 on the table refer to id of player on an another table.
id score p1 p2 date
-- ----- -- -- ----
1 12 1 2 2011.10.21
2 23 3 4 2011.10.22
3 21 1 3 2011.10.23
4 35 5 1 2011.10.24
5 11 2 3 2011.10.25
What I want to do is the get the player id (p1 or p2) with highest score. My solution is something like select sum(score) but I can't form a query because a player may appear in both p1 or p2 columns.
Also a bigger problem is when I want to sort scores from highest to lowest. I dont know what to do. How can I sum and sort a score if I need to group to separate columns? The result I want is similar to this output:
pID score times_played
--- ----- ------------
1 68 3
3 55 3
5 35 1
2 23 2
4 23 1
Is my database design flawed? If there is a more intelligent way I'd like to know. Should I need seperate single queries so I can merge them on PHP or something?
Any help would be appreciated.
Cheers.
PS: I couldnt think a nice subject. Feel free to edit.
You can put the players in one column as so:
select id, score, p1 as player, date from yourtable
union all
select id, score, p2 as player, date from yourtable
You now have players in one column. You can do this to get the score sum for all players
select sum(score), player from (
select id, score, p1 as player, date from yourtable
union all
select id, score, p2 as player, date from yourtable
) group by player
Now, you say that you also want to know how many times the player played and sort them in descending order:
select sum(score), player, count(*) as timesPlayed from (
select id, score, p1 as player, date from yourtable
union all
select id, score, p2 as player, date from yourtable
) group by player order by sum(score) desc
Try this to get players with highest score (disregarding ties)
select id,p1,p2
from table t1
join (select max(score) as MaxS) xx on xx.MaxS = t1.Score
limit 1
To get player total score, try this
select Player as pID,Sum(tot) as Score, count(*) as TimesPlayed
from
(
select p1 as Player,sum(score) as Tot
from table
group by p1
union all
select p2,sum(score)
from table
group by p2
) xx
Group by xx.Player
order by Score desc
Alternatively to using UNION (ALL) on the table, you could try something like this:
SELECT
CASE p.PlayerNumber WHEN 1 THEN t.p1 ELSE t.p2 END AS pID,
SUM(t.score) AS score,
COUNT(*) AS times_played
FROM atable t
CROSS JOIN (SELECT 1 AS PlayerNumber UNION ALL SELECT 2) p
GROUP BY
pID /* this is probably MySQL-specific; most, if not all, other major
database systems would require repeating the entire pID expression here, i.e.
GROUP BY
CASE p.PlayerNumber WHEN 1 THEN t.p1 ELSE t.p2 END
*/
ORDER BY
score DESC,
times_played DESC /* this is based on your result set;
you might want to omit it or change it to ASC */
UPDATE, in an answer to a question in the comments: joining the result set to the user table:
SELECT
`user`.*, /* you should probably specify
the necessary columns explicitly */
totals.score,
totals.times_played
FROM `user` u
INNER JOIN (
SELECT
CASE p.PlayerNumber WHEN 1 THEN t.p1 ELSE t.p2 END AS pID,
SUM(t.score) AS score,
COUNT(*) AS times_played
FROM atable t
CROSS JOIN (SELECT 1 AS PlayerNumber UNION ALL SELECT 2) p
GROUP BY
pID
) totals ON user.id = totals.pID
ORDER BY
totals.score DESC,
totals.times_played DESC