186

In PostgreSQL there is the Limit and Offset keywords which will allow very easy pagination of result sets.

What is the equivalent syntax for SQL Server?

shA.t
  • 15,232
  • 5
  • 47
  • 95
Earlz
  • 57,517
  • 89
  • 275
  • 484
  • For sql server 2012, this feature is implemented in easy way. See [my answer](http://stackoverflow.com/a/9261762/1045444) – Somnath Muluk Jan 11 '13 at 05:23
  • Thanks for asking this question, we are being forced to transition from MySQL to MsSQL :( – tempcke Nov 05 '15 at 14:57
  • You can use offset and fetch next statement in SQL server with order by clause. Try it https://youtu.be/EqHkAiiBwPc – Amresh Kumar Singh May 17 '20 at 03:19
  • OFFSET / FETCH in ORDER CLAUSE is the SQL ISO standard. LIMIT and TOP are vendor solutions and are not portable between different RDBMS – SQLpro Oct 07 '20 at 14:42

16 Answers16

260

This feature is now made easy in SQL Server 2012. This is working from SQL Server 2012 onwards.

Limit with offset to select 11 to 20 rows in SQL Server:

SELECT email FROM emailTable 
WHERE user_id=3
ORDER BY Id
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY;
  • ORDER BY: required
  • OFFSET: optional number of skipped rows
  • NEXT: required number of next rows

Reference: https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql

Ian Kemp
  • 24,155
  • 16
  • 97
  • 121
Somnath Muluk
  • 46,917
  • 28
  • 204
  • 217
  • 4
    Is there an equiv of `SQL_CALC_FOUND_ROWS` when using this? – Petah Mar 23 '15 at 23:10
  • 1
    @Petah @@Rowcount will give you that I think – Rob Sedgwick Dec 20 '15 at 18:08
  • GOTCHA: You can't use this from within a CTE. It has to be used in the main query. I wanted to limit the amount of rows returned (pagination) and then perform an expensive calculation to the 10 or so rows returned, rather than determine the rows, perform the expensive computation, and then skip/take what I needed. @Aaronaught's answer will work for those needing to restrict rows within a CTE. – Derreck Dean Jun 19 '17 at 18:38
  • 1
    @SarojShrestha: This is not Offset and Fetch issue. You should revisit architecture of your table now. Consider Partitioning of tables, your data row and it's different column types and total table size, consider archiving some rows if not required regularly, check your server specs. – Somnath Muluk Oct 28 '18 at 12:51
  • Thank you so much @SomnathMuluk – mickael Oct 16 '20 at 22:28
139

The equivalent of LIMIT is SET ROWCOUNT, but if you want generic pagination it's better to write a query like this:

;WITH Results_CTE AS
(
    SELECT
        Col1, Col2, ...,
        ROW_NUMBER() OVER (ORDER BY SortCol1, SortCol2, ...) AS RowNum
    FROM Table
    WHERE <whatever>
)
SELECT *
FROM Results_CTE
WHERE RowNum >= @Offset
AND RowNum < @Offset + @Limit

The advantage here is the parameterization of the offset and limit in case you decide to change your paging options (or allow the user to do so).

Note: the @Offset parameter should use one-based indexing for this rather than the normal zero-based indexing.

Earlz
  • 57,517
  • 89
  • 275
  • 484
Aaronaught
  • 115,846
  • 24
  • 251
  • 329
  • 26
    Old now. Sql Server 2012 and later support OFFSET/FETCH – Joel Coehoorn Sep 17 '13 at 03:06
  • 32
    @JoelCoehoorn Not old. I just got assigned to project using SLQ Server 2008 having used only mysql in the past... – Cthulhu Feb 13 '14 at 11:35
  • This is quite good but needs to be ajusted a little `WHERE RowNum >= (@Offset + 1)` – Eric Herlitz Apr 03 '14 at 07:05
  • @EricHerlitz: Please refer to the note at the end of this answer. – Aaronaught Apr 04 '14 at 01:05
  • 5
    `The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified`. MSSQL2008 R2. – Paul Aug 26 '14 at 19:55
  • The order by won't work because of the WITH unfortunately. – Zorkind Aug 28 '15 at 19:44
  • @EricHerlitz don't complicate this even more.. just use `WHERE RowNum > @Offset` and we're good with the start condition. – Damian Vogel Jan 12 '16 at 16:00
  • 2
    @Aaronaught If my `Table` has 200k records, it will fetch all first, then apply limit ? Is this query efficient ? – Jigar Jun 20 '16 at 07:15
  • anyone knows how to do `left join` on this query? – Drenyl Feb 22 '19 at 03:05
  • RowNum >= (@Offset+1) AND RowNum < (@Offset+1) + @Limit – AminGolmahalle Feb 28 '19 at 17:13
  • I compared the results of this query with SELECT * FROM Table WHERE ORDER BY (SortCol1, SortCol2, ...) ASC OFFSET *Offset ROWS FETCH NEXT *Limit ROWS ONLY; And both seem to work similarly with same efficiency. Does this query provide any additional benefit ? Also to note, the efficiency of both queries start to drop when *Offset increases. @Jigar – Anupam Chand Apr 29 '21 at 03:44
25
select top {LIMIT HERE} * from (
      select *, ROW_NUMBER() over (order by {ORDER FIELD}) as r_n_n 
      from {YOUR TABLES} where {OTHER OPTIONAL FILTERS}
) xx where r_n_n >={OFFSET HERE}

A note: This solution will only work in SQL Server 2005 or above, since this was when ROW_NUMBER() was implemented.

Asken
  • 6,897
  • 9
  • 40
  • 70
jorgeu
  • 669
  • 6
  • 14
12

You can use ROW_NUMBER in a Common Table Expression to achieve this.

;WITH My_CTE AS
(
     SELECT
          col1,
          col2,
          ROW_NUMBER() OVER(ORDER BY col1) AS row_number
     FROM
          My_Table
     WHERE
          <<<whatever>>>
)
SELECT
     col1,
     col2
FROM
     My_CTE
WHERE
     row_number BETWEEN @start_row AND @end_row
Tom H
  • 44,871
  • 12
  • 81
  • 121
4

For me the use of OFFSET and FETCH together was slow, so I used a combination of TOP and OFFSET like this (which was faster):

SELECT TOP 20 * FROM (SELECT columname1, columname2 FROM tablename
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS) aliasname

Note: If you use TOP and OFFSET together in the same query like:

SELECT TOP 20 columname1, columname2 FROM tablename
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS

Then you get an error, so for use TOP and OFFSET together you need to separate it with a sub-query.

And if you need to use SELECT DISTINCT then the query is like:

SELECT TOP 20 FROM (SELECT DISTINCT columname1, columname2
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS) aliasname

Note: The use of SELECT ROW_NUMBER with DISTINCT did not work for me.

sebasdev
  • 169
  • 1
  • 6
  • 1
    I get "A TOP can not be used in the same query or sub-query as a OFFSET." – MichaelRushton May 08 '17 at 10:24
  • You are right @MichaelRushton, can not be used in the same query or in the same sub-query, then you have to use a sub-query to separate it. So if you have the SQL like `SELECT TOP 20 id FROM table1 where id > 10 order by date OFFSET 20 rows`, you must transform it like `SELECT TOP 20 * FROM (SELECT id FROM table1 where id > 10 order by date OFFSET 20 ROWS) t1`. I will edit my answer. Thanks and excuse me my English. – sebasdev May 09 '17 at 17:12
4
-- @RowsPerPage  can be a fixed number and @PageNumber number can be passed 
DECLARE @RowsPerPage INT = 10, @PageNumber INT = 2

SELECT *

FROM MemberEmployeeData

ORDER BY EmployeeNumber

OFFSET @PageNumber*@RowsPerPage ROWS

FETCH NEXT 10 ROWS ONLY
shakeel
  • 49
  • 1
3

Adding a slight variation on Aaronaught's solution, I typically parametrize page number (@PageNum) and page size (@PageSize). This way each page click event just sends in the requested page number along with a configurable page size:

begin
    with My_CTE  as
    (
         SELECT col1,
              ROW_NUMBER() OVER(ORDER BY col1) AS row_number
     FROM
          My_Table
     WHERE
          <<<whatever>>>
    )
    select * from My_CTE
            WHERE RowNum BETWEEN (@PageNum - 1) * (@PageSize + 1) 
                              AND @PageNum * @PageSize

end
Tom
  • 422
  • 4
  • 7
2

The closest I could make is

select * FROM( SELECT *, ROW_NUMBER() over (ORDER BY ID ) as ct from [db].[dbo].[table] ) sub where ct > fromNumber  and ct <= toNumber

Which I guess similar to select * from [db].[dbo].[table] LIMIT 0, 10

Szymon
  • 41,313
  • 16
  • 90
  • 109
2

Specifically for SQL-SERVER you can achieve that in many different ways.For given real example we took Customer table here.

Example 1: With "SET ROWCOUNT"

SET ROWCOUNT 10
SELECT CustomerID, CompanyName from Customers
ORDER BY CompanyName

To return all rows, set ROWCOUNT to 0

SET ROWCOUNT 0  
SELECT CustomerID, CompanyName from Customers
    ORDER BY CompanyName

Example 2: With "ROW_NUMBER and OVER"

With Cust AS
( SELECT CustomerID, CompanyName,
ROW_NUMBER() OVER (order by CompanyName) as RowNumber 
FROM Customers )
select *
from Cust
Where RowNumber Between 0 and 10

Example 3 : With "OFFSET and FETCH", But with this "ORDER BY" is mandatory

SELECT CustomerID, CompanyName FROM Customers
ORDER BY CompanyName
OFFSET 0 ROWS
FETCH NEXT 10 ROWS ONLY

Hope this helps you.

Humayoun_Kabir
  • 1,118
  • 9
  • 12
2

Another sample :

declare @limit int 
declare @offset int 
set @offset = 2;
set @limit = 20;
declare @count int
declare @idxini int 
declare @idxfim int 
select @idxfim = @offset * @limit
select @idxini = @idxfim - (@limit-1);
WITH paging AS
    (
        SELECT 
             ROW_NUMBER() OVER (order by object_id) AS rowid, *
        FROM 
            sys.objects 
    )
select *
    from 
        (select COUNT(1) as rowqtd from paging) qtd, 
            paging 
    where 
        rowid between @idxini and @idxfim
    order by 
        rowid;
Earlz
  • 57,517
  • 89
  • 275
  • 484
sillyim
  • 21
  • 2
  • 16
    I removed your anti-microsoft hate speech. Don't discuss holy wars here; just answer and ask questions in a non-subjective way. – Earlz Jul 06 '11 at 20:10
2

There is here someone telling about this feature in sql 2011, its sad they choose a little different keyword "OFFSET / FETCH" but its not standart then ok.

Community
  • 1
  • 1
1
select top (@TakeCount) * --FETCH NEXT
from(
    Select  ROW_NUMBER() OVER (order by StartDate) AS rowid,*
    From YourTable
)A
where Rowid>@SkipCount --OFFSET
Paul Roub
  • 35,100
  • 27
  • 72
  • 83
Barny
  • 325
  • 1
  • 3
  • 13
1
@nombre_row :nombre ligne par page  
@page:numero de la page

//--------------code sql---------------

declare  @page int,@nombre_row int;
    set @page='2';
    set @nombre_row=5;
    SELECT  *
FROM    ( SELECT    ROW_NUMBER() OVER ( ORDER BY etudiant_ID ) AS RowNum, *
      FROM      etudiant

    ) AS RowConstrainedResult
WHERE   RowNum >= ((@page-1)*@nombre_row)+1
    AND RowNum < ((@page)*@nombre_row)+1
ORDER BY RowNum
1

Since nobody provided this code yet:

SELECT TOP @limit f1, f2, f3...
FROM t1
WHERE c1 = v1, c2 > v2...
AND
    t1.id NOT IN
        (SELECT TOP @offset id
         FROM t1
         WHERE c1 = v1, c2 > v2...
         ORDER BY o1, o2...)
ORDER BY o1, o2...

Important points:

  • ORDER BY must be identical
  • @limit can be replaced with number of results to retrieve,
  • @offset is number of results to skip
  • Please compare performance with previous solutions as they may be more efficient
  • this solution duplicates where and order by clauses, and will provide incorrect results if they are out of sync
  • on the other hand order by is there explicitly if that's what's needed
przemo_li
  • 3,703
  • 3
  • 31
  • 52
-1

In SQL server you would use TOP together with ROW_NUMBER()

SQLMenace
  • 125,031
  • 23
  • 195
  • 219
-1

Since, I test more times this script more useful by 1 million records each page 100 records with pagination work faster my PC execute this script 0 sec while compare with mysql have own limit and offset about 4.5 sec to get the result.

Someone may miss understanding Row_Number() always sort by specific field. In case we need to define only row in sequence should use:

ROW_NUMBER() OVER (ORDER BY (SELECT NULL))

SELECT TOP {LIMIT} * FROM (
      SELECT TOP {LIMIT} + {OFFSET} ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS ROW_NO,*
      FROM  {TABLE_NAME}
) XX WHERE ROW_NO > {OFFSET}

Explain:

  • {LIMIT}: Number of records for each page
  • {OFFSET}: Number of skip records
Vanda Ros
  • 77
  • 1
  • 5
  • 2
    While this code may solve the question, [including an explanation](https://meta.stackexchange.com/q/114762) of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please [edit] your answer to add explanations and give an indication of what limitations and assumptions apply. – Brian Apr 12 '20 at 15:58