SQL Server中的LIMIT 10..20


161

我正在尝试做类似的事情:

SELECT * FROM table LIMIT 10,20

要么

SELECT * FROM table LIMIT 10 OFFSET 10

但是使用SQL Server

我发现的唯一解决方案看起来过于矫kill过正:

SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name) as row FROM sys.databases 
 ) a WHERE row > 5 and row <= 10

我还发现

SELECT TOP 10 * FROM stuff; 

...但这不是我想要做的,因为我无法指定起始限制。

我还有另一种方法吗?

另外,只是好奇,SQL Server不支持该LIMIT功能或类似原因吗?我不想这么刻薄,但这听起来确实像是DBMS所需要的东西。如果确实如此,那么我很抱歉这么无知!在过去的5年中,我一直在使用MySQL和SQL +。


1
我已经能够最好地使用CTE作为范围的宽度ROW_NUMBER()并限制TOP范围的范围和范围WHERE的边界的条件。我还注意到,如果该TOP子句使用文字而不是变量,则性能会更好
Jodrell

任何涉及ROW_NUMBER()的解决方案的问题是,如果您事先不知道将拥有哪些列,并且具有联接,并且联接的表具有相同的列名,则将获得“已多次指定“ xxx””。这并不像最初听起来的那样罕见。我使用Dapper,并且我的表都有一个Id列。Dapper对此进行拆分和映射,因此我不想重命名它们,但不能使用SELECT * FROM([原始查询])别名。我还没有找到解决方案!
史蒂夫·欧文

Answers:


104

LIMIT子句不是标准SQL的一部分。MySQL,PostgreSQL和SQLite都将它作为SQL的供应商扩展来支持。

其他品牌的数据库可能具有类似的功能(例如,TOP在Microsoft SQL Server中),但是这些功能并不总是相同的。

这是很难使用TOP在Microsoft SQL Server模仿LIMIT条款。在某些情况下,它根本不起作用。

您显示的解决方案ROW_NUMBER()在Microsoft SQL Server 2005和更高版本中可用。这是目前仅作为查询一部分的最佳解决方案。

另一种解决方案是TOP用于获取第一个计数 + 偏移量行,然后使用API​​来查找超过第一个偏移量行。

也可以看看:


135

对于SQL Server 2012+ ,可以使用

SELECT  *
FROM     sys.databases
ORDER BY name 
OFFSET  5 ROWS 
FETCH NEXT 5 ROWS ONLY 

10
SQl Server 2012要求在使用OFFSET 5 ROWS FETCH NEXT 5 ROWS时指定ORDER BY,而MySql和SQLite在使用LIMIT 5,5时不需要ORDER BY
Tomas Kubes 2013年

4
@ qub1n- 在这种情况下,MySQL 不保证您会返回哪些行。
马丁·史密斯

3
您是否必须使用offset,还是可以省去该行(假设您不想要偏移量)?
Cullub '16


您的示例查询运行正常,但如果按如下所示按col更改表名称和顺序SELECT * FROM DimProduct ORDER BY ProductKey偏移量5行FETCH下一个5行仅给出错误Parse error at line: 4, column: 1: Incorrect syntax near 'OFFSET'
shashwat

36

如您所见,这是首选的sql server方法:

SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name) as row FROM sys.databases 
 ) a WHERE a.row > 5 and a.row <= 10

为什么要a在内部选择之后?我假设你是给内选择一个别名,但你似乎从来没有使用它...你应该再做a.row,而不是仅仅row
卢卡斯

3
@Lucas,您需要在( )派生表后面放置一个别名,但是如果您随后忘记使用别名来引用列,它将被删除。我已经修复了...
KM。

谢谢,我发现了困难的方式(试图把别名排除在外)。
卢卡斯

1
投票+1:但是,将执行计划与这种方法的执行计划进行比较之后,我对@MartinSmith的答案进行了投票,我发现该解决方案的运行速度更快。
严苛的

10

如果您使用的是SQL Server 2012+,请投票给Martin Smith答案,并使用OFFSETFETCH NEXT扩展名ORDER BY

如果您很不幸无法使用较早的版本,则可以执行以下操作,

WITH Rows AS
(
    SELECT
              ROW_NUMBER() OVER (ORDER BY [dbo].[SomeColumn]) [Row]
            , *
        FROM
              [dbo].[SomeTable]
)
SELECT TOP 10
          *
     FROM
         Rows
    WHERE Row > 10

我相信在功能上等同于

SELECT * FROM SomeTable LIMIT 10 OFFSET 10 ORDER BY SomeColumn

以及在MS SQL 2012之前的TSQL中我所知的最佳执行方式。


如果行太多,使用临时表而不是CTE可能会获得更好的性能。


在提供2012年之前的解决方案时指出了马丁·史密斯的答案(并链接到它)时表示赞赏。也为临时表建议,因为您是正确的:)
fujiiface '17

7

不幸的是,这ROW_NUMBER()是您可以做的最好的事情。实际上更正确,因为a limittop子句不考虑某些特定顺序的情况下并没有真正的意义。但这仍然很痛苦。

更新: Sql Server 2012 limit通过OFFSET和FETCH关键字添加了类似功能。与相对LIMIT,这是ansi标准的方法,后者是非标准的MySql扩展。


@Joel:您能解释一下为什么ROW_NUMBER()无法按行从ORDER BY中出来的方式对行进行编号吗?我一直想知道为什么“ OVER(ORDER BY name)”是强制性的,但是我想这是有充分理由的。或至少是一个原因。
Tomalak

3
因为没有order by子句就没有order等东西。您将获得记录可用于服务器的任何顺序,并且该顺序可能因查询请求而异。
Joel Coehoorn

1
@marcgg:我从没读过任何迹象表明微软计划实施LIMIT。即使他们确实有这样的计划,封闭源供应商也往往不会预先宣布功能。这当然是一个有用的功能,但是鉴于他们的代码,我们不知道要实现多少工作。
Bill Karwin 09年

3
如果您不想在ORDER BY子句中重复自己,请使用ROW_NUMBER()别名而不是原始的一组列。
Peter Radocchia,2009年

2
@Tomalak:就SQL Server而言,用于计算ROW_NUMBER()的顺序与结果集的顺序完全无关。因此,您必须分别指定它们。
LukeH

6

这个怎么样?

SET ROWCOUNT 10 

SELECT TOP 20 *
FROM sys.databases
ORDER BY database_id DESC

它为您提供了前20行中的最后10行。缺点之一是顺序颠倒了,但是至少很容易记住。


6
如果表中只有14行怎么办?您得到的第14行下降到5,这与LIMIT 10 OFFSET 10返回的行不同(应该是第14行下降到11)。
Bill Karwin 09年

2
SELECT TOP 10 *
FROM TABLE
WHERE IDCOLUMN NOT IN (SELECT TOP 10 IDCOLUMN FROM TABLE)

应给记录11-20。如果增加以获取更多页面,则效率可能不太高,并且不确定是否可能受到排序的影响。可能必须在两个WHERE语句中都指定它。


1

一个好的方法是创建一个过程:

create proc pagination (@startfrom int ,@endto int) as
SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name desc) as row FROM sys.databases 
 ) a WHERE a.row > @startfrom and a.row <= @endto

就像限制0.2,///////////////执行分页0.4


1

仅针对适用于大多数数据库引擎的记录解决方案,尽管可能不是最有效的:

Select Top (ReturnCount) *
From (
    Select Top (SkipCount + ReturnCount) *
    From SourceTable
    Order By ReverseSortCondition
) ReverseSorted
Order By SortCondition

请注意:无论SkipCount是什么,最后一页仍将包含ReturnCount行。但这在许多情况下可能是一件好事。


1

LIMIT的等效项是SET 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

0
select * from (select id,name,ROW_NUMBER() OVER (ORDER BY id  asc) as row
from tableName1) tbl1
where tbl1.row>=10 and tbl1.row<=15

将打印10到15行。


0

到目前为止,这种格式对我有用(尽管不是最佳性能):

SELECT TOP {desired amount of rows} * 
FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY {order columns} asc)__row__ FROM {table})tmp
WHERE __row__ > {offset row count}

旁注,对动态数据进行分页可能会导致奇怪/意外的结果。


0

从MS SQL Server联机文档(http://technet.microsoft.com/zh-cn/library/ms186734.aspx ),这是我已经测试并工作的示例,用于检索一组特定的行。ROW_NUMBER需要OVER,但您可以按照自己的喜好订购:

WITH OrderedOrders AS
(
  SELECT SalesOrderID, OrderDate,
  ROW_NUMBER() OVER (ORDER BY OrderDate) AS RowNumber
  FROM Sales.SalesOrderHeader 
) 
SELECT SalesOrderID, OrderDate, RowNumber  
FROM OrderedOrders 
WHERE RowNumber BETWEEN 50 AND 60;

0

使用所有SQL Server:;使用tbl作为(SELECT ROW_NUMBER()over(order by(select 1))as RowIndex,* from table *)从表中选择top 10 * from tbl where RowIndex> = 10


-3
 SELECT * FROM users WHERE Id Between 15 and 25

它将像MYSQl中的限制一样从15到25打印


2
如果用户删除了15到25之间的记录怎么办?
GökçerGökdal
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.