SQL Query: last, second last, third last..value (Microsoft SQL)
-
23-07-2021 - |
Question
I am trying to build reports for sport team. The results are stored in a SQL-Database like this:
For the reports I need the columns Home2-Away4 to be filled with the corresponding results. Which means:
Imagine: ID=10 and Home(no.) (Team#) = 1 --> Home1 = result one game before which is ID=3 and the team played away and the result was 0 (=home1). This is what I already have done, but Home2 = result two games ago, as Home3=result three games ago and so on.
And here is how I have done this:
SELECT top 1 [result_home] from (SELECT Top 2 [result_home] FROM
(SELECT top 2 [result_home] from [Table]
where [ID] < 10 and ( [Home(no.)]=1 or [Away(No.)]=1 ) order by [ID] desc ) top2 )top1
(Microsoft SQL Server Express 2012 btw.)
I have written a small Java code which decides if to pick from result_home or result_away.
The basic question is: How is it possible to query for game results two and more gamedays ago?
Thanks in advance.
Kind regards
Stefan
Solution
I think you need the LAG function. The simplest case would be something like:
[Home1] = LAG(result_Home, 1) OVER(PARTITION BY [Home(no.)] ORDER BY ID),
However, it is not quite as simple as this because you need to reference home or away, so you need to first unpivot your data
SELECT ID,
Type,
TeamID,
Result
FROM T
CROSS APPLY
( VALUES
('Home', [Home(no.)], Result_Home),
('Away', [Away(no.)], Result_Away)
) Upvt (Type, TeamID, Result);
This means you can access each teams result whether it is home or away all in one column (For the lag function).
Then with this unpivoted data you apply the lag to get the previous 4 results (I've used ORDER BY ID
but I'd assume there is a date field I am missing?):
SELECT *,
[Result1] = LAG(Result, 1) OVER(PARTITION BY TeamID ORDER BY ID),
[Result2] = LAG(Result, 2) OVER(PARTITION BY TeamID ORDER BY ID),
[Result3] = LAG(Result, 3) OVER(PARTITION BY TeamID ORDER BY ID),
[Result4] = LAG(Result, 4) OVER(PARTITION BY TeamID ORDER BY ID)
FROM (Previous Query)
Then once you have each teams previous 4 results for each match you can join the unpivoted data back together to get the home and away teams on the same row again, giving the full query:
WITH Results AS
( SELECT ID,
Type,
TeamID,
Result
FROM T
CROSS APPLY
( VALUES
('Home', [Home(no.)], Result_Home),
('Away', [Away(no.)], Result_Away)
) Upvt (Type, TeamID, Result)
), Results2 AS
( SELECT *,
[Result1] = LAG(Result, 1) OVER(PARTITION BY TeamID ORDER BY ID),
[Result2] = LAG(Result, 2) OVER(PARTITION BY TeamID ORDER BY ID),
[Result3] = LAG(Result, 3) OVER(PARTITION BY TeamID ORDER BY ID),
[Result4] = LAG(Result, 4) OVER(PARTITION BY TeamID ORDER BY ID)
FROM Results
)
SELECT Home.ID,
[Home(no.)] = Home.TeamID ,
[Away(no.)] = Away.TeamID,
[Result_Home] = Home.Result,
[Result_Away] = Away.Result,
[Home1] = Home.Result1,
[Home2] = Home.Result2,
[Home3] = Home.Result3,
[Home4] = Home.Result4,
[Away1] = Away.Result1,
[Away2] = Away.Result2,
[Away3] = Away.Result3,
[Away4] = Away.Result4
FROM Results2 AS Home
INNER JOIN results2 AS Away
ON Away.ID = Home.ID
AND Away.Type = 'Away'
WHERE Home.Type = 'Home'
ORDER BY ID;
OTHER TIPS
It looks like you're looking for the ROW_NUMBER()
function or another windowing function
Something like
;with cte as
(
select id, home as team, result_home as score from yourtable
union
select id, away, result_away from yourtable
),
games as
(
select *, ROW_NUMBER() over (partition by team order by id) as gameday
from cte
)
select *
from (select team, score, gameday from games) src
pivot
(max(score) for gameday in ([1],[2],[3],[4],[5],[6]))p
If you know an individual games gameday for each team, then two games ago is this games' gameday - 2, etc.