How to get the second best value - sql

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));

Related

Selecting sum of first and last rows in related table

In my table I have these three tables:
Teams:
Id GUID UNIQUE PRIMARY
Name NVARCHAR
Participants:
Id GUID UNIQUE PRIMARY
FirstName NVARCHAR
[....]
TeamId GUID
ParticipantData:
Id GUID UNIQUE PRIMARY
Weight FLOAT
Date DATETIME
ParticipantId GUID
TeamId GUID
What I need is an SQL query that gives me all columns from Teams AND:
The sum of the first (order by Date) entries in ParticipantData of participants in the team (TeamId)
The sum of the last (ordered by Date) entries in ParticipantData of participants in the team (TeamId)
Explanation:
I have lots of participants (team members) reporting their weight with some interval (Weight + Date). What I'm trying to accomplish is to calculate the weight loss of all the team members.
On 2019-01-03 Participant 1 reports Weight 78
On 2019-01-06 Participant 1 reports Weight 75
On 2019-01-04 Participant 2 reports Weight 86
On 2019-01-07 Participant 2 reports Weight 83
I need the query to get SumOfFirstWeights (78 + 86) = 164
I need the query to get SumOfLastWeights (75 + 83) = 158
Which gives me a weight loss of 6.
I've tried many combinations of:
Select *,
(SELECT TOP (1) Sum(Weight)
FROM ParticipantData WHERE (TeamId = Teams.Id)
GROUP BY ParticipantId
)
ORDER BY Date As SumOfFirstWeights
From Teams
Your problem is some kind of greatest/lowest per group, moreover, you want a sum of these values.
select t.id, t.name, sum(t1.weight), sum(t2.weight)
from teams t
left join
(
select pd.teamid, pd.participantid, pd.weight
from ParticipantData pd
join
(
select teamid, participantid, min(date) min_date
from ParticipantData
group by teamid, participantid
) t on pd.teamid = t.teamid and
pd.participantid = t.participantid and
pd.date = t.min_date
) t1 on t.id = t1.teamid
left join
(
select pd.teamid, pd.participantid, pd.weight
from ParticipantData pd
join
(
select teamid, participantid, max(date) max_date
from ParticipantData
group by teamid, participantid
) t on pd.teamid = t.teamid and
pd.participantid = t.participantid and
pd.date = t.max_date
) t2 on t1.teamid = t2.teamid and
t1.participantid = t2.participantid
group by t.id, t.name
You want to find the first and last row per team. You can use analytic functions for this. I'm using ROW_NUMBER which gives me exactly one record, even if a participant has two entries on the same day.
with pd as
(
select
participantid,
weight,
row_number() over (partition by participantid order by date) as one_is_first,
row_number() over (partition by participantid order by date desc) as one_is_last
from participantdata
)
, team_weights as
(
select
p.teamid,
sum(case when pd.one_is_first = 1 then weight end) as first_weight,
sum(case when pd.one_is_last = 1 then weight end) as last_weight,
sum(case when pd.one_is_last = 1 then weight end) -
sum(case when pd.one_is_first = 1 then weight end) as difference
from pd
join participant p on p.id = pd.participantid
where (pd.one_is_first = 1 or pd.one_is_last = 1)
group by p.teamid
)
select *
from teams
join team_weights on where team_weights.team_id = teams.id
order by teams.id;

Get sum of last 5 rows for each unique id

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

Which row has the highest value?

I have a table of election results for multiple nominees and polls. I need to determine which nominee had the most votes for each poll.
Here's a sample of the data in the table:
PollID NomineeID Votes
1 1 108
1 2 145
1 3 4
2 1 10
2 2 41
2 3 0
I'd appreciate any suggestions or help anyone can offer me.
This will match the highest, and will also bring back ties.
select sd.*
from sampleData sd
inner join (
select PollID, max(votes) as MaxVotes
from sampleData
group by PollID
) x on
sd.PollID = x.PollID and
sd.Votes = x.MaxVotes
SELECT
t.NomineeID,
t.PollID
FROM
( SELECT
NomineeID,
PollID,
RANK() OVER (PARTITION BY i.PollID ORDER BY i.Votes DESC) AS Rank
FROM SampleData i) t
WHERE
t.Rank = 1
SELECT PollID, NomineeID, Votes
FROM
table AS ABB2
JOIN
(SELECT PollID, MAX(Votes) AS most_votes
FROM table) AS ABB1 ON ABB1.PollID = ABB2.PollID AND ABB1.most_votes = ABB2.Votes
Please note, if you have 2 nominees with the same number of most votes for the same poll, they'll both be pulled using this query
select Pollid, Nomineeid, Votes from Poll_table
where Votes in (
select max(Votes) from Poll_table
group by Pollid
);

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 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