Get highest highscore entries between given dates - sql

There is a scores_score table which contains following columns:
id, player_name, value, created_at
I have to fetch N (100) best scores where:
player_name must be unique across results
only best score for given player_name should be returned
results have to be filtered by date range
Lets say I have following data:
id player_name value date
1 A 400 2016-09-10
2 B 200 2016-09-12
3 C 400 2016-09-15
4 C 500 2016-09-14
5 B 100 2016-09-20
6 A 6000 2015-01-01
7 B 1200 2016-09-29
And want to get best players with their scores between 2016-09-01 and 2016-09-20. I should get the:
id player_name value date
4 C 500 2016-09-14
1 A 400 2016-09-10
2 B 200 2016-09-12
This is my approach to solve it, but there is an issue in nested SELECT as it fetches the best score of the player overall not within date ranges.
SELECT b.*, a.*
FROM (SELECT player_name, max(value) AS max_value
FROM scores_score
GROUP BY player_name
ORDER BY max(value) DESC) a
INNER JOIN scores_score b ON a.player_name = b.player_name AND a.max_value = b.value
WHERE CAST(b.created_at AS DATE) >= %(date_border)s
ORDER BY b.value DESC
LIMIT 100

distinct on
select *
from (
select distinct on (player_name) *
from scores_score
where date between '2016-09-01' and '2016-09-20'
order by player_name, value desc
) s
order by value desc
limit 100

This is going to work and will provide you with expected output. Use row_number() window function to mark highest score for each player between dates (rn = 1) and then order the result set by value descending and finally limit the output to 100 highest.
select
id, player_name, value, created_at
from (
select
id, player_name, value, created_at,
row_number() over (partition by player_name order by value desc, id) as rn
from scores_score
where created_at between '2016-09-01' and '2016-09-20'
) ranks
where rn = 1
order by value desc
limit 100
Note that additional column id for sorting within row_number function is to resolve ties (even though it assigns only one value per row within partition) that would involve the same player having two rows with equal values that are within given date. This would get older record and if they differ with created_at date you would see a difference in the output :-)

This one is a little cumbersome but should work. First select just the players and values within your date range (a). Then select the max score by player (b). Then join the id and date (c):
SELECT c.id, c.player_name, c.value, c.date
FROM
scores_score c
INNER JOIN
(SELECT player_name, max(value)
FROM
(SELECT player_name, value
FROM scores_score
WHERE date BETWEEN '2016-09-01' AND '2016-09-20') a
GROUP BY player_name) b
ON c.player_name = b.player_name
AND c.value = b.value
ORDER BY value
LIMIT 100
Tested here: http://sqlfiddle.com/#!9/10db42/6

Related

How can I get both the rows that fit into a date range, and the single most recent row prior to the given date range per ID?

I want to provide a query a date range, for example 11/01/2021 - 11/30/2021. From there I want to get all relevant rows that fall within that date range, which is pretty straightforward - just a simple select with a where clause based on the to and from dates. But after this, I want to get the most recent row (if it exists) prior to the date range, only for the foreign key IDs that appear within the date range.
So as an example,
ID
FK ID
Date
1
101
09/05/2021
2
101
10/29/2021
3
101
11/05/2021
4
201
11/20/2021
5
301
08/13/2021
6
401
11/01/2021
7
401
11/23/2021
If I were to input a date range of 11/01/2021 - 11/30/2021, I would expect the following results
ID
FK ID
Date
2
101
10/29/2021
3
101
11/05/2021
4
201
11/20/2021
6
401
11/01/2021
7
401
11/23/2021
My first thought was a UNION, where the second query grabs the max date prior to the date range and unions with the first query which has all the values in the given date range. But I think this would fail with values like ID 5 in the table above - because there's no restriction on the second query that it would need the same FK ID to exist in the first table. Maybe I'm just not thinking straight but I can't think of any way to restrict a union in this way?
You can use a cte to select rows within the range and add more rows relative to this cte
with cte as (
SELECT *
FROM tb
WHERE [Date] >= '11/1/2021' AND [Date] <= '11/30/2021'
)
select *
from cte
union all
select *
from (
select top(1) with ties *
from tb
where exists (select 1 from cte where cte.fk_id = tb.fk_id)
and [Date] < '11/1/2021'
order by row_number() over(partition by fk_id order by [date] desc)
) t
order by id;
db<>fiddle
The following query produces the output you want
WITH cte AS(
SELECT ID,FK_ID,[DATE]
FROM tb
WHERE [DATe] >= '11/1/2021' AND [Date] <= '11/30/2021')
SELECT *
FROM cte
UNION ALL
SELECT ID,FK_ID,[DATE]
FROM
(SELECT *,
CASE WHEN LEAD([Date]) over(ORDER BY ID) >= '11/1/2021' AND LEAD([Date]) over(ORDER BY ID) <= '11/30/2021' THEN 1 ELSE 0 END AS rnk
FROM tb) t
WHERE rnk = 1 AND [DATe] < '11/1/2021' AND FK_ID IN (SELECT FK_ID FROM cte)
ORDER BY ID
demo in db<>fiddle

SQL MIN(value) matching row in PostgreSQL

I have a following tables:
TABLE A:
ID ID NAME PRICE CODE
00001 B 1000 1
00002 A 2000 1
00003 C 3000 1
Here is the SQL I use:
Select Min (ID),
Min (ID NAME),
Sum(PRICE)
From A
GROUP BY CODE
Here is what I get:
ID ID NAME PRICE
00001 A 6000
As you can see, ID NAME don't match up with the min row value. I need them to match up.
I would like the query to return the following
ID ID NAME PRICE
00001 B 6000
What SQL can I use to get that result?
If you want one row, use limit or fetch first 1 row only:
select a.*
from a
order by a.price asc
fetch first 1 row only;
If, for some reason, you want the sum() of all prices, then you can use window functions:
select a.*, sum(a.price) over () as sum_prices
from a
order by a.price asc
fetch first 1 row only;
You can use row_number() function :
select min(id), max(case when seq = 1 then id_name end) as id_name, sum(price) as price, code
from (select t.*, row_number() over (partition by code order by id) seq
from table t
) t
group by code;
you can also use sub-query
select t1.*,t2.* from
(select ID,Name from t where ID= (select min(ID) from t)
) as t1
cross join (select sum(Price) as total from t) as t2
https://dbfiddle.uk/?rdbms=postgres_10&fiddle=a496232b552390a641c0e5c0fae791d1
id name total
1 B 6000

SQL retrieve recent record

I want to retrieve TOPIC 1 SCORES with the most recent score (excluding null) (sorted by date) for each detailsID, (there are only detailsID 2 and 3 here, therefore only two results should return)
What about getting rid of Topic 1 Scores in GROUP BYdetailsID,Topic 1 Scores ?
Use a subquery to get the max and then join to it.
SELECT a.detailsID,`Topic 1 Scores`, a.Date
FROM Information.scores AS a
JOIN (SELECT detailsID, MAX(Date) "MaxDate"
FROM Information.scores
WHERE `Topic 1 Scores` IS NOT NULL
GROUP BY detailsID) Maxes
ON a.detailsID = Maxes.detailsID
AND a.Date = Maxes.MaxDate
WHERE `Topic 1 Scores` IS NOT NULL
Assuming SQL Server:
SELECT
ROW_NUMBER() OVER (PARTITION BY detailsID ORDER BY Date DESC) AS RowNumber,
detailsID, Date, Topic 1 Scores
FROM
Information.scores
Try doing
SELECT detailsID,`Topic 1 Scores`, MAX(Date) as "Date" GROUP BY "Date"

Grouping values by a column and getting min value of another with another column

My table structure is as follows
userId period pcount
a 01/03 100
a 02/03 150
a 03/03 200
b 02/03 250
...and so on..
I wish to get the row which has the minimum pcount for each userid, so the result for userid a ought to be as below..
a 01/03 100
If I did not want the period value, I could have grouped the table by userId and selected userId and MIN of pcount.
But, since I need the period as well, what should I do..
Though I'm using SQL Server on azure,it might change and so would appreciate standard ansi sql query.
Thanks
In general these kind of requests are handled through ROW_NUMBER
Select * From
(
select row_number() Over(Partition by userId Order by pcount asc) as rn,*
From yourtable
) A
Where Rn = 1
Another approach using INNER JOIN works with most of the DBMS
SELECT A.*
FROM yourtable A
INNER JOIN (SELECT userId,
Min(pcount) AS M_pcount
FROM yourtable
GROUP BY userId) B
ON A.userId = B.userId
AND A.pcount = B.M_pcount

SQL: order by two columns and get the firsts rows with equal values in 2-nd column

I have a table sorted by 1, 2 columns. And I need to get the first row from the top and all succeeding rows while their values of 2-nd column is the same as value of the first row.
F.e I have data sample:
select * from sample
order by ID desc, date desc
ID Date
--- ----
45 NULL
44 NULL
40 01/01/10
35 NULL
32 04/05/08
I need to get the first two rows (with id in (45, 44)), because 2-nd row have Date = NULL.
If I'd had data sample:
ID Date
--- ----
45 NULL
44 NULL
40 NULL
35 NULL
32 04/05/08
I will need to get the first 4 rows (with id in (45, 44, 40, 35)).
I can't make query to resolve my issue. I considered about using row_number() and rank(), but I can't adapt they for me purpose.
Thanks a lot for any help!
Based on your description, you can do something like this:
with t as (<your query here>)
select t
from t cross join
(select t.*
from t
order by id desc
limit 1
) tt
order by (case when t.date = tt.date or t.date is null and t2.date is null then 1 else 2 end),
t.id desc;
Well, I concocted something like this, but it doesnt look elegantly.
select *
from (
select *,
sum(rank_date) over (partition by rank_date order by ID desc) as sm
from (
select *
,rank() over(order by DATE desc nulls first) rank_date
,row_number() over(order by ID desc) rank_id
from sample
) ss
) s
where sm = row_number