ChatGPT解决这个技术问题 Extra ChatGPT

Equivalent of LIMIT and OFFSET for SQL Server?

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?

For sql server 2012, this feature is implemented in easy way. See my answer
Thanks for asking this question, we are being forced to transition from MySQL to MsSQL :(
OFFSET / FETCH in ORDER CLAUSE is the SQL ISO standard. LIMIT and TOP are vendor solutions and are not portable between different RDBMS
One point pertinent to note is that "Order By" clause is mandatory while using Offset Fetch

I
Ian Kemp

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


Is there an equiv of SQL_CALC_FOUND_ROWS when using this?
@Petah @@Rowcount will give you that I think
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.
@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.
Thank you so much @SomnathMuluk
E
Earlz

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.


Old now. Sql Server 2012 and later support OFFSET/FETCH
@JoelCoehoorn Not old. I just got assigned to project using SLQ Server 2008 having used only mysql in the past...
This is quite good but needs to be ajusted a little WHERE RowNum >= (@Offset + 1)
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.
@Aaronaught If my Table has 200k records, it will fetch all first, then apply limit ? Is this query efficient ?
A
Asken
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.


I've been using this query for a little while now and it works great so thanks for that. I'm just wondering what the 'xx' represents?
the sub query requires a name. as I'm not using it just put xx there
The xx is just a table alias. It might be a little clearer if you said AS xx
anyone knows how to do left join on this query?
T
Tom H

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

Wondering if SELECT inside CTE will load whole table first and then outer WHERE will filter out not needed records? Or SQL will optimize it and avoid loading content of whole My_Table? I am asking because if it doesn't and table has lot's of records it may be inefficient.
Normally SQL Server will optimize around CTEs when it can. Because this is doing a BETWEEN I don't know if it will or not. I'm on my phone, so I can't test it right now, but it should be easy enough to test. Just make sure that you use a large enough dataset that SQL doesn't just load the whole table anyway and try using a BETWEEN that starts somewhere past the first row.
H
Humayoun_Kabir

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.


s
sebasdev

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.


I get "A TOP can not be used in the same query or sub-query as a OFFSET."
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.
s
shakeel
-- @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

Works for Microsoft SQL Server 13.x Thanks a lot .
T
Tom

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

E
Earlz

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;

I removed your anti-microsoft hate speech. Don't discuss holy wars here; just answer and ask questions in a non-subjective way.
C
Community

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.


S
Szymon

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


P
Paul Roub
select top (@TakeCount) * --FETCH NEXT
from(
    Select  ROW_NUMBER() OVER (order by StartDate) AS rowid,*
    From YourTable
)A
where Rowid>@SkipCount --OFFSET

n
noureddine ahmer el kaab
@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

ROW_NUMBER() OVER is a slow process to get results...
p
przemo_li

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


M
Mike97

Elaborating the Somnath-Muluk's answer just use:

SELECT *
FROM table_name_here
ORDER BY (SELECT NULL AS NOORDER)
OFFSET 9 ROWS 
FETCH NEXT 25 ROWS ONLY 

w/o adding any extra column. Tested in SQL Server 2019, but I guess could work in older ones as well.


S
SQLMenace

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


V
Vanda Ros

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


While this code may solve the question, including an explanation 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.