等价于SQL Server的LIMIT和OFFSET?


172

在PostgreSQL中有LimitandOffset关键字可以非常容易地分页结果集。

SQL Server的等效语法是什么?


对于sql server 2012,此功能可以轻松实现。看到我的回答
Somnath Muluk

感谢您提出这个问题,我们被迫从MySQL过渡到MsSQL :(
tempcke 2015年

您可以使用offset并通过order by子句在SQL Server中获取下一条语句。尝试一下youtu.be/EqHkAiiBwPc
Amresh Kumar Singh,

Answers:


139

相当于LIMITSET ROWCOUNT,但如果你想通用分页,最好写这样的查询:

;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

这样做的好处是,如果您决定更改分页选项(或允许用户这样做),则可以对偏移量和限制进行参数化。

注意:@Offset为此,该参数应使用基于1的索引,而不是基于零的常规索引。


22
现在老了。Sql Server 2012和更高版本支持OFFSET / FETCH
Joel Coehoorn

31
@JoelCoehoorn不老。我刚刚被分配到使用SLQ Server 2008的项目,而在过去仅使用mysql ...
Cthulhu 2014年

这是相当不错的,但需要稍作调整WHERE RowNum >= (@Offset + 1)
Eric Herlitz 2014年

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。
保罗

2
@Aaronaught如果我Table有200k记录,它将首先获取所有记录,然后应用限制?这个查询有效吗?
吉加尔

231

现在,在SQL Server 2012中可以轻松使用此功能。从SQL Server 2012起可以使用此功能。

用偏移量限制以选择SQL Server中的11至20行:

SELECT email FROM emailTable 
WHERE user_id=3
ORDER BY Id
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY;
  • OFFSET:跳过的行数
  • NEXT:下一行所需的数量

参考:https : //docs.microsoft.com/zh-cn/sql/t-sql/queries/select-order-by-clause-transact-sql?view=sql-server-2017


4
SQL_CALC_FOUND_ROWS使用此功能时是否等效?
Petah 2015年

1
@Petah @@ Rowcount我想您会得到
Rob Sedgwick

GOTCHA:您不能在CTE中使用它。必须在主查询中使用它。我想限制返回的行数(分页),然后对返回的10个左右的行执行昂贵的计算,而不是确定行,执行昂贵的计算,然后跳过/获取我需要的内容。@Aaronaught的答案适用于那些需要限制CTE中的行的人。
Derreck院长

@Somnath Muluk对于较大的数据量示例(偏移量为1000000),此偏移量和访存会花费大量时间。我该如何处理。
Saroj Shrestha '18 -10-28

1
@SarojShrestha:这不是偏移和获取问题。您现在应该重新访问表的体系结构。考虑对表进行分区,数据行以及其不同的列类型和表总大小,如果不经常需要考虑对一些行进行归档,请检查服务器规格。
Somnath Muluk,

23
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}

注意: 该解决方案仅在SQL Server 2005或更高版本中有效,因为该解决方案是在ROW_NUMBER()实施时才进行的。


我已经使用了此查询已有一段时间,它的工作原理非常好,非常感谢。我只是想知道“ xx”代表什么?
Urbley 2014年

子查询需要一个名称。因为我没有使用它,所以只把xx放在这里
jorgeu 2014年

2
xx只是表别名。如果您说这可能会更清楚AS xx
Concrete Gannet

有人知道如何在此查询上进行左联接吗?
Drenyl '19

12

您可以在通用表表达式中使用ROW_NUMBER来实现此目的。

;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

4

对我而言,将OFFSET和FETCH一起使用很慢,因此我使用了TOP和OFFSET的组合,如下所示(速度更快):

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

注意:如果在同一查询中同时使用TOP和OFFSET,例如:

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

然后,您将得到一个错误,因此,要一起使用TOP和OFFSET,需要用子查询将其分开。

如果需要使用SELECT DISTINCT,则查询如下:

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

注意: SELECT ROW_NUMBER和DISTINCT的使用对我不起作用。


1
我得到“不能在与偏移量相同的查询或子查询中使用TOP”。
MichaelRushton

您是正确的@MichaelRushton,不能在同一查询或同一子查询中使用,那么您必须使用子查询将其分开。因此,如果您有类似的SQL SELECT TOP 20 id FROM table1 where id > 10 order by date OFFSET 20 rows,则必须将其转换为SELECT TOP 20 * FROM (SELECT id FROM table1 where id > 10 order by date OFFSET 20 ROWS) t1。我将编辑我的答案。谢谢,请原谅我的英语。
sebasdev

2

另一个样本:

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;

15
我删除了您的反微软仇恨言论。不要在这里讨论圣战;只是以非主观的方式回答问题。
Earlz'7

2

有关此功能在SQL 2011,其悲伤他们选择有点不同的关键字“OFFSET / FETCH”,但它不是非标准然后确定人家讲。


2

在Aaronaught的解决方案上稍加改动,我通常参数化页码(@PageNum)和页面大小(@PageSize)。这样,每个页面单击事件仅发送请求的页码以及可配置的页面大小:

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

2

我能做的最接近的是

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

我猜这类似于 select * from [db].[dbo].[table] LIMIT 0, 10


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

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

由于尚无人提供此代码:

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...

要点:

  • ORDER BY必须相同
  • @limit 可以替换为要检索的结果数,
  • @offset 是要跳过的结果数
  • 请与以前的解决方案比较性能,因为它们可能更有效
  • 此解决方案重复whereorder by条款,如果不同步,将提供错误的结果
  • 另一方面,order by是否明确需要

1
-- @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

1

特别是对于SQL-SERVER,您可以通过许多不同的方式来实现。对于给定的真实示例,我们在此处采用了Customer表。

示例1:使用“ SET ROWCOUNT”

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

要返回所有行,请将ROWCOUNT设置为0

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

示例2:使用“ ROW_NUMBER和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

示例3:使用“ OFFSET and FETCH”,但是使用此“ ORDER BY”是强制性的

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

希望对您有帮助。


-1

在SQL Server中,您可以将TOP与ROW_NUMBER()一起使用


-1

因为,我测试此脚本的次数更多,因此每百万条记录更有用,每页100条具有分页功能的记录更快,我的PC执行此脚本0秒,而与mysql比较则有其自身的限制,并且偏移了4.5秒才能获得结果。

有人可能会错过对Row_Number()总是按特定字段排序的理解。如果我们只需要按顺序定义行,则应使用:

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}

说明:

  • {LIMIT}:每页的记录数
  • {OFFSET}:跳过记录的数量

2
尽管这段代码可以解决问题,但包括解释如何以及为何解决该问题的说明,确实可以帮助提高您的帖子质量,并可能导致更多的投票。请记住,您将来会为读者回答问题,而不仅仅是现在问的人。请编辑您的答案以添加说明,并指出适用的限制和假设。
布赖恩
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.