In my experience, an aggregate (DISTINCT or GROUP BY) can be quicker then a ROW_NUMBER() approach. Saying that, ROW_NUMBER is better with SQL Server 2008 than SQL Server 2005.
However, you'll have to try for your situation.
Compare query plans, and use Profiler and SET to capture IO, CPU, Duration etc
For a lot of background, see these SO questions:
Finally, do you need the ROW_NUMBER approach? It looks like you're fixing a problem caused by de-normalisation.
And some notes:
- shouldn't YearID be in the GROUP BY or PARTITION BY?
- Won't DISTINCT give different output?
- Are these columns indexed?
Okay, so here is the query modified to work the way you want:
DECLARE @players table
(
PlayerID uniqueidentifier NOT NULL PRIMARY KEY,
PlayerName nvarchar(64) NOT NULL
);
DECLARE @playerScores table
(
ID bigint NOT NULL IDENTITY PRIMARY KEY,
PlayerID uniqueidentifier NOT NULL,
DateCreated datetime NOT NULL,
Score int NOT NULL,
TimeTaken bigint NOT NULL,
PuzzleID int NOT NULL
);
DECLARE @puzzleId int = 0;
SELECT TOP 50
a.PlayerID,
p.PlayerName,
a.Score,
a.TimeTaken,
a.PlayedDate
FROM
(
SELECT
ps.PlayerID,
ps.Score,
ps.TimeTaken,
ps.DateCreated AS PlayedDate,
ROW_NUMBER()
OVER
(
PARTITION BY ps.PlayerID
ORDER BY ps.Score DESC, ps.TimeTaken, ps.DateCreated
) AS RN
FROM @playerScores ps
WHERE ps.PuzzleID = @puzzleId
) a
INNER JOIN @players p ON p.PlayerID = a.PlayerID
WHERE a.RN = 1
ORDER BY
a.Score DESC,
a.TimeTaken,
a.PlayedDate;
Having written this (note: indexes are not optimized), and looking at the other queries you're going to need to write, what I would actually recommend is to abandon this type of query entirely, and create a denormalized high-score table (rows are unique on the combination of PlayerID
, PuzzleID
), on which to run aggregates instead.
The reason why is because the GameResult
table is going to grow huge in the database, and so it will be less and less efficient to run aggregates on it directly as time passes, and the requirements are incompatible with doing something like creating an indexed view to summarize the information.
Also, if you aren't doing this already, it's highly likely you'll want to use an asynchronous process to compute the "leaderboards" periodically and cache the results, instead of computing them just-in-time. (You could do something like merge the current player's score with the cached leaderboards so the player can see themself on the leaderboards immediately if they got a high score.) See my answer here for some ideas to consider when implementing a caching mechanism.
Best Answer
CTE:
You can embed you
SELECT
withRANK()
into a CTE and thenUPDATE
the CTE.Don't forget the
;
terminator at the end of the line preceding the CTE statement.Sub Query:
You can also self
JOIN
your table on a sub query with the expectedRANK
.This query expects an
Id
or a group of column in both the sub query and theJOIN
. It is used to uniquely identify each row andJOIN
it to the table. From your data in your sample picture, this seems to bemanager_code+, archive_year, archive_day_of_year
Sample Data used:
This gives your 2 correct syntaxes using this sample data. Queries must be adapted to your real table(s).