Top 5 records from each week [duplicate] - mysql

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

Related

Who to the number of users who have had one transaction per day?

Here is my query:
select count(1) from
(select count(1) num, user_id from pos_transactions pt
where date(created_at) <= '2020-6-21'
group by user_id
having num = 1) x
It gives me the number of users who have had 1 transaction until 2020-6-21. Now I want to group it also per date(created_at). I mean, I want to get a list of dates (such as 2020-6-21, 2020-6-22 etc ..) plus the number of users who have had 1 transaction in that date (day).
Any idea how can I do that?
EDIT: The result of query above is correct, the issue is, it's manually now. I mean, I have to increase 2020-6-21 by hand. I want to make it automatically. In other words, I want a list of all dates (from 2020-6-21 til now) contains the number of users who have had 1 transaction until that date.
If you want the number of users who had one transaction on each day, then you need to aggregate by the date as well:
select dte, count(*)
from (select date(created_at) as dte, user_id
from pos_transactions pt
where date(created_at) <= '2020-6-21'
group by dte, user_id
having count(*) = 1
) du
group by dte;

Use COUNT(), ORDER BY and WHERE user = ? simultaneously [duplicate]

This question already has answers here:
Rank function in MySQL
(13 answers)
Closed 7 years ago.
I have a table with highscores. When I read them I order them by score DESC.
scores
id name score
i.e.
SELECT name, score FROM scores ORDER BY score DESC
Now I would like to know the rank of a person. I am trying to find a way to combine this without having to loop through all the highscores. This is what I thought of, but I know this will not work. Any ideas?
SELECT COUNT(id), name, score FROM scores WHERE name = ? ORDER BY score DESC
Should I use WHERE?
You could count everyone with a higher score in a subquery:
select coalesce((select count(1) from scores b where b.score > a.score),0) + 1 Rank
, Name
, Score
from Scores a
where name = 'Sarah'
SQL Fiddle: http://sqlfiddle.com/#!9/ff0133/3

Progression Series generation so that Show n candidates having highest score and n+5 candidates having second highest score and so on

have a table which have 3 columns. Id is unique and score can be duplicate in that table and table have more than thousand of entries in it.
Id Name Score
Problem statement:-
I have to show list in such manner such that
List of 5:candidate of having highest score then List of 10: candidate of having second highest score then List of 15: candidate of having third highest score
and so on..(Each incremented by 5)
select #mem_count:=#mem_count+5 as group_member_count, scores.score
from (select distinct score from scores_table order by score desc) scores,
(select #mem_count:=0) sess
The query returns list of scores plus member count for each score. That would be subquery to be used in the next statement.
select
sc.*,
#group_n:=if(sc.score!=#group,0,#group_n+1) number_in_group,
#group:=sc.score
from
(SELECT #group_n:=0, #group:=-1) row,
scores_table sc
join (the_subquery) sub on sc.score=sub.score
having number_in_group<sub.group_member_count
Here we define 2 session variables group (to keep current score) and group_n to add numbers in group for each row.
Thus we expect to kind fo group by score and number all rows for each score. Then the having willleave only members less than group_member_count
Use this
SELECT x.*
FROM my_table x
JOIN my_table y
ON y.marks = x.marks
AND y.id <= x.id
GROUP
BY x.marks
, x.id
HAVING COUNT(0) <= (MAX(101-x.marks)*2)
ORDER
BY marks DESC,id;

Mysql return matching users in subqueries

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

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