Sunday, December 22, 2013

How to Get the Last 5 Rows from SQL Server

"And hear, I think, the very latest counsel"
-- Shakespeare, Henry IV Part 2
Act IV, Scene V, Line 182

Intro


Say you have the following need.

Given the following trans table:

CREATE TABLE trans (
   id int identity(1,1) not null
  ,transactionId int not null
  ,transactionTime datetime2 not null default (sysutcdatetime())
  ,amount int not null
);

  And the following data:

INSERT INTO trans (transactionId, amount) VALUES(1, 100);
INSERT INTO trans (transactionId, amount) VALUES(1, 125);
INSERT INTO trans (transactionId, amount) VALUES(2, 400);
INSERT INTO trans (transactionId, amount) VALUES(4, 25);
INSERT INTO trans (transactionId, amount) VALUES(5, 20);
INSERT INTO trans (transactionId, amount) VALUES(6, 70);
INSERT INTO trans (transactionId, amount) VALUES(7, 100);
INSERT INTO trans (transactionId, amount) VALUES(8, 33);

** Note, done this way to have different transactionTime **

When I query the trans table

Then I want to get the last 5 records in the trans table as indicated by transactionTime

This setup will give us the following.

http://sqlfiddle.com/#!6/11a33/2

Luckily, SQL Server 2012 offers a few ways to look at the last few records of a table.

TOP N


SELECT TOP 5 *
  FROM trans
  ORDER BY transactionTime DESC
;

By ordering by the transactionTime in descending order we are able to use the TOP filter to obtain the last 5 records.

http://sqlfiddle.com/#!6/11a33/1

ORDER BY with FETCH and OFFSET


SELECT *
  FROM trans
  ORDER BY transactionTime DESC
  OFFSET 0 ROWS
  FETCH NEXT 5 ROWS ONLY
;

Using the ORDER BY clause, we can use the new to SQL Server 2012 FETCH and OFFSET we are able to follow the ISO SQL 2008 standard for our limiting.

http://sqlfiddle.com/#!6/11a33/3

Page Size option


DECLARE @PageSize INT = 5

SELECT *
  FROM trans
  ORDER BY transactionTime DESC
  OFFSET 0 ROWS
  FETCH NEXT (SELECT @PageSize) ROWS ONLY
;

In SQL Server 2012, the new FETCH on the ORDER BY clause allows for queries to obtain the number of rows to retrieve!

http://sqlfiddle.com/#!6/11a33/6

Using ROW_NUMBER


SELECT id, transactionId, transactionTime, amount
  FROM (
    SELECT
      ROW_NUMBER() OVER(ORDER BY transactionTime DESC) AS row
     ,*
      FROM trans
  ) AS r
  WHERE r.row <= 5
;

Using the ROW_NUMBER() windowing function, we can create a poor man's FETCH and OFFSET by filtering the row numbers returned to be the number of records we want to obtain.

This idea came from Suprotim Agarwal's blog post on SQL Server Curry.

http://sqlfiddle.com/#!6/11a33/7

Performance


"Inform the database whenever you don't need all rows."
-- Markus Winand, Use the Index Luke - Top-N Rows Queries

SQL Server offers many different ways to get the last 5 records, but one must think of performance.  If you have an indexed order by then the DBMS would not even have to perform the sort!