Get sum of last 5 rows for each unique id - sum

This is the query I'm using currently
SELECT SUM(score) as score FROM (SELECT p2.score FROM performance_buzz as p2 WHERE p2.player_id = 922 ORDER BY p2.id DESC LIMIT 5) as performance_buzz
But in the above query I need to pass player_id manually and I don't want to do that. I want to do this with the mysql way because I want to use this query as subquery for getting last 5 rows sum of score for each player
SELECT performance_buzz.id, performance_buzz.score as last_score, performance_buzz.name
FROM `performance_buzz`
LEFT JOIN performance_buzz m2 ON (performance_buzz.name = m2.name AND performance_buzz.id < m2.id)
WHERE m2.id IS NULL
GROUP BY performance_buzz.name
ORDER BY performance_buzz.id DESC

should be like this:
select player_id, sum(score) score
from
(
SELECT
#row_number:=CASE
WHEN #player_id = player_id THEN #row_number + 1
ELSE 1
END AS rn,
#player_id:=player_id as player_id,
score
FROM
performance_buzz,(SELECT #player_id:=0,#row_number:=0) as t
order by player_id, id desc
) a
where rn <= 5
group by player_id

Related

select value based on max of other column

I have a few questions about a table I'm trying to make in Postgres.
The following table is my input:
id
area
count
function
1
100
20
living
1
200
30
industry
2
400
10
living
2
400
10
industry
2
400
20
education
3
150
1
industry
3
150
1
education
I want to group by id and get the dominant function based on max area. With summing up the rows for area and count. When area is equal it should be based on max count, when area and count is equal it should be based on prior function (i still have to decide if education is prior to industry or vice versa). So the result should be:
id
area
count
function
1
300
50
industry
2
1200
40
education
3
300
2
industry
I tried a lot of things and maybe it's easy, but i don't get it. Can someone help to get the right SQL?
One method uses row_number() and conditional aggregation:
select id, sum(area), sum(count),
max(function) over (filter where seqnum = 1) as function
from (select t.*,
row_number() over (partition by id order by area desc) as seqnum
from t
) t
group by id;
Another method uses ``distinct on`:
select id, sum(area) over (partition by id) as area,
sum(count) over (partition by id) as count,
function
from t
order by id, area desc;
Use a scalar sub-query for "function".
select t.id, sum(t.area), sum(t.count),
(
select "function"
from the_table
where id = t.id
order by area desc, count desc, "function" desc
limit 1
) as "function"
from the_table as t
group by t.id order by t.id;
SQL Fiddle
you can use sum as window function:
select distinct on (t.id)
id,
sum(area) over (partition by id) as area,
sum(count) over (partition by id) as count,
( select function from tbl_test where tbl_test.id = t.id order by count desc limit 1 ) as function
from tbl_test t
This is how you get the function for each group based on id:
select id, function
from yourtable yt1
left join yourtable yt2
on yt1.id = yt2.id and yt1.area < yt2.area
where yt2.area.id is null;
(we ensure that no yt2 exists that would be of the same id but of higher areay)
This would work nicely, but you might have several max areas with different values. To cope with this isue, let's ensure that exactly one is chosen:
select id, max(function) as function
from yourtable yt1
left join yourtable yt2
on yt1.id = yt2.id and yt1.area < yt2.area
where yt2.area.id is null
group by id;
Now, let's join this to our main table;
select yourtable.id, sum(yourtable.area), sum(yourtable.count), t.function
from yourtable
join (
select id, max(function) as function
from yourtable yt1
left join yourtable yt2
on yt1.id = yt2.id and yt1.area < yt2.area
where yt2.area.id is null
group by id
) t
on yourtable.id = t.id
group by yourtable.id;

Select by greatest sum, but without the sum in the result

I need to select the top score of all combined attempts by a player and I need to use a WITH clause.
create table scorecard(
id integer primary key,
player_name varchar(20));
create table scores(
id integer references scorecard,
attempt integer,
score numeric
primary key(id, attempt));
Sample Data for scorecard:
id player_name
1 Bob
2 Steve
3 Joe
4 Rob
Sample data for scores:
id attempt score
1 1 50
1 2 45
2 1 10
2 2 20
3 1 40
3 2 35
4 1 0
4 2 95
The results would simply look like this:
player_name
Bob
Rob
But would only be Bob if Rob had scored less than 95 total. I've gotten so far as to have the name and the total scores that they got in two columns using this:
select scorecard.player_name, sum(scores.score)
from scorecard
left join scores
on scorecard.id= scores.id
group by scorecard.name
order by sum(scores.score) desc;
But how do I just get the names of the highest score (or scores if tied).
And remember, it should be using a WITH clause.
Who ever told you to "use a WITH clause" was missing a more efficient solution. To just get the (possibly multiple) winners:
SELECT c.player_name
FROM scorecard c
JOIN (
SELECT id, rank() OVER (ORDER BY sum(score) DESC) AS rnk
FROM scores
GROUP BY 1
) s USING (id)
WHERE s.rnk = 1;
A plain subquery is typically faster than a CTE. If you must use a WITH clause:
WITH top_score AS (
SELECT id, rank() OVER (ORDER BY sum(score) DESC) AS rnk
FROM scores
GROUP BY 1
)
SELECT c.player_name
FROM scorecard c
JOIN top_score s USING (id)
WHERE s.rnk = 1;
SQL Fiddle.
You could add a final ORDER BY c.player_name to get a stable sort order, but that's not requested.
The key feature of the query is that you can run a window function like rank() over the result of an aggregate function. Related:
Postgres window function and group by exception
Get the distinct sum of a joined table column
Can try something like follows.
With (SELECT id, sum(score) as sum_scores
FROM scores
group by id) as sumScoresTable,
With (SELECT max(score) as max_scores
FROM scores
group by id) as maxScoresTable
select player_name
FROM scorecard
WHERE scorecard.id in (SELECT sumScoresTable.id
from sumScoresTable
where sumScoresTable.score = (select maxScoresTable.score from maxScoresTable)
Try this code:
WITH CTE AS (
SELECT ID, RANK() OVER(ORDER BY SumScore DESC) As R
FROM (
SELECT ID, SUM(score) AS SumScore
FROM scores
GROUP BY ID )
)
SELECT player_name
FROM scorecard
WHERE ID IN (SELECT ID FROM CTE WHERE R = 1)

SELECT a single field by ordered value

Consider the following two tables:
student_id score date
-------------------------
1 10 05-01-2013
2 100 05-15-2013
2 60 05-01-2012
2 95 05-14-2013
3 15 05-01-2011
3 40 05-01-2012
class_id student_id
----------------------------
1 1
1 2
2 3
I want to get unique class_ids where the score is above a certain threshold for at least one student, ordered by the latest score.
So for instance, if I wanted to get a list of classes where the score was > 80, i would get class_id 1 as a result, since student 2's latest score was above > 80.
How would I go about this in t-sql?
Are you asking for this?
SELECT DISTINCT
t2.[class_ID]
FROM
t1
JOIN t2
ON t2.[student_id] = t1.[student_id]
WHERE
t1.[score] > 80
Edit based on your date requirement, then you could use row_number() to get the result:
select c.class_id
from class_student c
inner join
(
select student_id,
score,
date,
row_number() over(partition by student_id order by date desc) rn
from student_score
) s
on c.student_id = s.student_id
where s.rn = 1
and s.score >80;
See SQL Fiddle with Demo
Or you can use a WHERE EXISTS:
select c.class_id
from class_student c
where exists (select 1
from student_score s
where c.student_id = s.student_id
and s.score > 80
and s.[date] = (select max(date)
from student_score s1
where s.student_id = s1.student_id));
See SQL Fiddle with Demo
select distinct(class_id) from table2 where student_id in
(select distinct(student_id) from table1 where score > thresholdScore)
This should do the trick:
SELECT DISTINCT
CS.Class_ID
FROM
dbo.ClassStudent CS
CROSS APPLY (
SELECT TOP 1 *
FROM dbo.StudentScore S
WHERE CS.Student_ID = S.Student_ID
ORDER BY S.Date DESC
) L
WHERE
L.Score > 80
;
And here's another way:
WITH LastScore AS (
SELECT TOP 1 WITH TIES
FROM dbo.StudentScore
ORDER BY Row_Number() OVER (PARTITION BY Student_ID ORDER BY Date DESC)
)
SELECT DISTINCT
CS.Class_ID
FROM
dbo.ClassStudent CS
WHERE
EXISTS (
SELECT *
FROM LastScore L
WHERE
CS.Student_ID = L.Student_ID
AND L.Score > 80
)
;
Depending on the data and the indexes, these two queries could have very different performance characteristics. It is worth trying several to see if one stands out as superior to the others.
It seems like there could be some version of the query where the engine would stop looking as soon as it finds just one student with the requisite score, but I am not sure at this moment how to accomplish that.

How to get the second best value

Assume we have two tables:
Players(player_id int)
GameScores(player_id int, session_id int, score int)
How can we query the second highest-score session for every player?
For example if
Players
1
2
GameScores
(player_id, session_id, score)
1 1 10
1 2 20
1 3 40
2 4 10
2 5 20
Then result would be
(player_id, session_id)
1, 2
2, 4
Can you try this
SELECT GameScores.player_id, GameScores.session_id
FROM (
SELECT player_id,MAX(score) as SecondScore
FROM GameScores g
WHERE score < (SELECT Max(Score) FROM gameScore where gameScore.player_id = g.player_id)
GROUP BY player_id
) x
INNER JOIN GameScores ON x.player_id = gamescore.player_id
AND x.SecondScore = gamescore.score
This is the query that select the second high score for each player
SELECT player_id,MAX(score) as SecondScore
FROM GameScores g
WHERE score < (SELECT Max(Score) FROM gameScore where gameScore.player_id = g.player_id)
GROUP BY player_id
You can't group by session in this query. So that's why you need to put this in a subquery and join it to gamescore to get the session_id
Here is code snippet for Oracle sql
select tbl.player_id,tbl.session_id from
(select p.player_id,g.session_id,g.score,rank() over (partition by p.player_id order by score desc) rnk from players p,
gamescores g
where p.player_id = g.player_id) tbl
where tbl.rnk = 2;
select player_id, first(session_id) as session_id
from
GameScores inner join (
select player_id, max(score) as secondscore
from
GameScores left join (
select player_id, max(score) as firstscore
from GameScores
group by player_id
) as NotThisOnes on GameScores.player_id = NotThisOnes.player_id
and GameScores.score = NotThisOnes.firstscore
where NotThisOnes.player_id is null
group by player_id
) as thisare on GameScores.player_id = thisare.player_id
and GameScores.score = thisare.secondscore
group by player_id
I took a different approach... I am not sure if this is better than other answers, but i wanted to solve it this way:
SELECT
GameScores.player_id,
GameScores.session_id,
GameScores.score
FROM
GameScores
WHERE
GameScores.score=
(select max(score) from GameScores GameScores_2
where GameScores.player_id = GameScores_2.Player_ID
and GameScores_2.Score<
(select max(score) from GameScores GameScores_1
where GameScores_1.player_id = GameScores.player_id));

How to query for rows that have highest column value among rows that have same value for one of the columns

I have UserScores Table with data like this:
Id userId Score
1 1 10
2 2 5
3 1 5
I would like to have a query or SQL block that can give me the following output
Id userId Score
3 1 5
2 2 5
That is, I would like to pick rows that are unique by 'user id' that belonging to the highest 'id' column value.
Another solution that would work on SQL Server 2000 (same as INNER JOIN above, but slightly faster) is:
SELECT id, userId, Score
FROM UserScores
WHERE id in (SELECT MAX(id)
FROM UserScores
GROUP BY userId
)
ORDER BY userId
Use:
WITH summary AS (
SELECT t.id,
t.userid,
t.score,
ROW_NUMBER() OVER (PARTITION BY t.userid ORDER BY t.id DESC, t.score DESC) AS rank
FROM USERSCORES sc)
SELECT s.id,
s.userid,
s.score
FROM summary s
WHERE s.rank = 1
How about
SELECT MAX(Id), userId, Score
FROM table
GROUP BY UserId
SELECT U2.id, U2.userId, U2.score
FROM UserScores U2
INNER JOIN (
SELECT U1.userId, MAX(U1.Id) MaxId
FROM UserScores U1
GROUP BY U1.userId
) U3
ON U2.id = U3.MaxId and U2.userId = U3.userId
ORDER BY U2.userId