SQL Server中的行偏移


133

SQL Server中有什么方法可以使结果从给定的偏移量开始?例如,在另一种类型的SQL数据库中,可以执行以下操作:

SELECT * FROM MyTable OFFSET 50 LIMIT 25

得到结果51-75。SQL Server中似乎不存在此构造。

如何在不加载我不关心的所有行的情况下完成此任务?谢谢!


您可以使用offset并获取下一条语句。 youtu.be/EqHkAiiBwPc
Amresh Kumar Singh

Answers:


152

我会避免使用SELECT *。指定您实际上想要的列,即使可能是全部。

SQL Server 2005以上版本

SELECT col1, col2 
FROM (
    SELECT col1, col2, ROW_NUMBER() OVER (ORDER BY ID) AS RowNum
    FROM MyTable
) AS MyDerivedTable
WHERE MyDerivedTable.RowNum BETWEEN @startRow AND @endRow

SQL Server 2000

通过SQL Server 2000中的大型结果集有效地分页

通过大型结果集进行分页的更有效方法


6
为什么即使选择所有列也建议避免选择SELECT?
亚当·内斯

12
我确定他使用了“ *”,因为它比“ col1,col2,... colN”更易于输入并且更清楚地指出了要点
gillonba 2012年

9
至于为什么不使用它,则SELECT *意味着如果表的结构发生变化,您的查询仍会运行,但会给出不同的结果。如果添加了列,这可能会很有用(尽管您仍然必须在某处使用它的名称);如果删除或重命名了列,那么对SQL而言,明显可见的中断要比对代码进行进一步的更改更好,因为变量未初始化。
IMSoP 2013年

5
选择表的所有数据并剪切?如果有50亿行?选择50亿行并为每个查询剪切?它对于CPU和服务器内存效率不高。
e-info128 2013年

3
请注意,2012 +实施起来更好。请参阅+马丁·史密斯
子午线

100

如果您要处理,以便所有页面,然后简单地记住看到前面的页面上的最后一个关键值,并使用TOP (25) ... WHERE Key > @last_key ORDER BY Key可以得到最好的执行方法,如果存在合适的索引,让这有效地seeked -或API游标,如果他们不这样做。

对于选择arbitary页面的最佳解决方案为SQL Server 2005 - 2008 R2可能是ROW_NUMBERBETWEEN

对于SQL Server 2012+,可以根据需要使用增强的ORDER BY子句。

SELECT  *
FROM     MyTable 
ORDER BY OrderingColumn ASC 
OFFSET  50 ROWS 
FETCH NEXT 25 ROWS ONLY 

尽管还有待观察该选项的性能如何


2
现在可以在SQL Server Compact 4.0中使用-> msdn.microsoft.com/zh-cn/library/gg699618(v=sql.110).aspx
Bart Verkoeijen 2011年

13
现在是时候将它们添加到tSQL中了
JohnFx 2012年

3
仅适用于Sql Server 2012 :(
e-info128 2013年

22

这是一种方法(SQL2000)

SELECT * FROM
(
    SELECT TOP (@pageSize) * FROM
    (
        SELECT TOP (@pageNumber * @pageSize) *
        FROM tableName 
        ORDER BY columnName ASC
    ) AS t1 
    ORDER BY columnName DESC
) AS t2 
ORDER BY columnName ASC

这是另一种方式(SQL 2005)

;WITH results AS (
    SELECT 
        rowNo = ROW_NUMBER() OVER( ORDER BY columnName ASC )
        , *
    FROM tableName 
) 
SELECT * 
FROM results
WHERE rowNo between (@pageNumber-1)*@pageSize+1 and @pageNumber*@pageSize

只是为了澄清第一个...(@pageSize)在这里是实际值的占位符。您必须专门执行“ TOP 25”;SQL Server 2000在TOP子句中不支持变量。这使涉及动态SQL变得很痛苦。
考恩

5
除非行总数恰好是页面大小的倍数,否则该SQL2000解决方案不适用于结果集中的最后一页。
Bill Karwin


7

OFFSET .. FETCH在SQL Server 2012中,但你需要指定一个ORDER BY列。

如果您确实没有可以作为ORDER BY列传递的任何显式列(如其他人所建议),则可以使用以下技巧:

SELECT * FROM MyTable 
ORDER BY @@VERSION 
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY

... 要么

SELECT * FROM MyTable 
ORDER BY (SELECT 0)
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY

当用户未明确指定订单时,我们在jOOQ中使用它。然后,这将产生相当随机的排序,而无需任何额外费用。


6

对于具有更多大数据列的表,我更喜欢:

SELECT 
  tablename.col1,
  tablename.col2,
  tablename.col3,
  ...
FROM
(
  (
    SELECT
      col1
    FROM 
    (
      SELECT col1, ROW_NUMBER() OVER (ORDER BY col1 ASC) AS RowNum
      FROM tablename
      WHERE ([CONDITION])
    )
    AS T1 WHERE T1.RowNum BETWEEN [OFFSET] AND [OFFSET + LIMIT]
  )
  AS T2 INNER JOIN tablename ON T2.col1=tablename.col1
);

--

[CONDITION] can contain any WHERE clause for searching.
[OFFSET] specifies the start,
[LIMIT] the maximum results.

它在具有大数据的表(如BLOB)上具有更好的性能,因为ROW_NUMBER函数只需要浏览一列,并且只有匹配的行与所有列一起返回。


5

查看我选择的分页器

SELECT TOP @limit * FROM (
   SELECT ROW_NUMBER() OVER (ORDER BY colunx ASC) offset, * FROM (

     -- YOU SELECT HERE
     SELECT * FROM mytable


   ) myquery
) paginator
WHERE offset > @offset

这解决了分页;)


3
SELECT TOP 75 * FROM MyTable
EXCEPT 
SELECT TOP 50 * FROM MyTable

性能方面似乎不是最佳的,因为查询随后不必要地执行了两次。特别是当用户转到较高的页面时,查询要丢弃行,即EXCEPT下面的部分将花费越来越长的时间。
vanval

2

您可能无法直接执行此操作,具体取决于您的版本,但是您可以做一些像

select top 25 *
from ( 
  select top 75 *
  from   table 
  order by field asc
) a 
order by field desc 

其中“字段”是关键。


4
除非行总数恰好是页面大小的倍数,否则该SQL2000解决方案不适用于结果集中的最后一页。
Bill Karwin

2

以下将显示25条记录,但不包括SQL Server 2012中的前50条记录。

SELECT * FROM MyTable ORDER BY ID OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY;

您可以根据需要替换ID


PLS还,加上这是可能在SQL Server 2012中
乌斯曼Younas

2

使用该ROW_NUMBER() OVER (ORDER BY)语句时应小心,因为性能相当差。使用通用表表达式的情况也一样ROW_NUMBER(),甚至更糟。我正在使用以下代码段,事实证明该代码段比使用带有标识的表变量提供页码的速度要快。

DECLARE @Offset INT = 120000
DECLARE @Limit INT = 10

DECLARE @ROWCOUNT INT = @Offset+@Limit
SET ROWCOUNT @ROWCOUNT

SELECT * FROM MyTable INTO #ResultSet
WHERE MyTable.Type = 1

SELECT * FROM
(
    SELECT *, ROW_NUMBER() OVER(ORDER BY SortConst ASC) As RowNumber FROM
    (
        SELECT *, 1 As SortConst FROM #ResultSet
    ) AS ResultSet
) AS Page
WHERE RowNumber BETWEEN @Offset AND @ROWCOUNT

DROP TABLE #ResultSet

这将返回11行,而不是10
阿龙贝特朗

1

我使用此技术进行分页。我没有获取所有行。例如,如果我的页面需要显示前100行,则仅获取100个带有where子句的行。SQL的输出应具有唯一键。

该表具有以下内容:

ID, KeyId, Rank

同一排名将分配给多个KeyId。

SQL是 select top 2 * from Table1 where Rank >= @Rank and ID > @Id

第一次我都通过了0。第二次通过1和14。第三次通过2和6 ....

第10条记录Rank&Id的值将传递到下一个

11  21  1
14  22  1
7   11  1
6   19  2
12  31  2
13  18  2

这对系统的压力最小


1

在SqlServer2005中,您可以执行以下操作:

DECLARE @Limit INT
DECLARE @Offset INT
SET @Offset = 120000
SET @Limit = 10

SELECT 
    * 
FROM
(
   SELECT 
       row_number() 
   OVER 
      (ORDER BY column) AS rownum, column2, column3, .... columnX
   FROM   
     table
) AS A
WHERE 
 A.rownum BETWEEN (@Offset) AND (@Offset + @Limit-1) 

不是@Offset + @Limit - 1吗?如果@Limit为10,则将返回11行。
亚伦·伯特兰

1

做到这一点而又不浪费时间订购记录的最佳方法是这样的:

select 0 as tmp,Column1 from Table1 Order by tmp OFFSET 5000000 ROWS FETCH NEXT 50 ROWS ONLY

不到一秒钟!
大表的最佳解决方案。


0

我已经搜索了一段时间(针对通用查询),并找到了另一种在SQL Server 2000+上使用ROWCOUNT和游标且没有TOP或任何临时表的方式。

使用SET ROWCOUNT [OFFSET+LIMIT]可以限制结果,并使用游标直接转到所需的行,然后循环到最后。

因此,您的查询将如下所示:

SET ROWCOUNT 75 -- (50 + 25)
DECLARE MyCursor SCROLL CURSOR FOR SELECT * FROM pessoas
OPEN MyCursor
FETCH ABSOLUTE 50 FROM MyCursor -- OFFSET
WHILE @@FETCH_STATUS = 0 BEGIN
    FETCH next FROM MyCursor
END
CLOSE MyCursor
DEALLOCATE MyCursor
SET ROWCOUNT 0

当您接近表格结尾时,我不希望看到这种情况的表现……
Aaron Bertrand 2013年

0

使用SQL Server 2012(11.x)和更高版本以及Azure SQL数据库,您还可以具有“ fetch_row_count_expression”,还可以具有ORDER BY子句。

USE AdventureWorks2012;  
GO  
-- Specifying variables for OFFSET and FETCH values    
DECLARE @skip int = 0  , @take int = 8;  
SELECT DepartmentID, Name, GroupName  
FROM HumanResources.Department  
ORDER BY DepartmentID ASC   
    OFFSET @skip ROWS   
    FETCH NEXT @take ROWS ONLY; 

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

注意 OFFSET指定在开始从查询表达式返回行之前要跳过的行数。它不是起始行号。因此,必须为0才能包含第一条记录。

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.