【问题标题】:Row Offset in SQL ServerSQL Server 中的行偏移量
【发布时间】:2010-09-16 07:46:38
【问题描述】:

SQL Server 中是否有任何方法可以从给定偏移量开始获取结果?例如,在另一种类型的 SQL 数据库中,可以这样做:

SELECT * FROM MyTable OFFSET 50 LIMIT 25

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

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

【问题讨论】:

标签: sql sql-server


【解决方案1】:

根据您的版本,您无法直接执行此操作,但您可以执行一些 hacky 之类的操作

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

其中“字段”是键。

【讨论】:

  • SQL2000 的解决方案不适用于结果集中的最后一页,除非总行数恰好是页面大小的倍数。
【解决方案2】:

这是一种方式(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 变得很痛苦。
  • SQL2000 的解决方案不适用于结果集中的最后一页,除非总行数恰好是页面大小的倍数。
【解决方案3】:

我会避免使用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

Efficiently Paging Through Large Result Sets in SQL Server 2000

A More Efficient Method for Paging Through Large Result Sets

【讨论】:

  • 即使您选择了所有列,为什么还建议避免使用 SELECT?
  • 我确信他使用了“*”,因为它比“col1, col2, ... colN”更容易输入,并且更容易理解。
  • 至于为什么不用它,SELECT *的意思是如果表的结构发生变化,你的查询仍然运行,但是给出不同的结果。如果添加了一个列,这可能很有用(尽管您仍然必须在某处按名称使用它);如果一列被删除或重命名,最好让你的 SQL 明显中断,而不是因为变量未初始化而导致代码进一步向下表现奇怪。
  • 选择表的所有数据并剪切?如果有 5000000000 行?为每个查询选择 5000000000 行并剪切?它对服务器的cpu和内存效率不高。
  • 请注意,2012+ 的实现方式更好。查看 +Martin Smith 的回答
【解决方案4】:

你可以使用ROW_NUMBER()函数来得到你想要的:

SELECT *
FROM (SELECT ROW_NUMBER() OVER(ORDER BY id) RowNr, id FROM tbl) t
WHERE RowNr BETWEEN 10 AND 20

【讨论】:

    【解决方案5】:

    在 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 行。
    【解决方案6】:

    使用ROW_NUMBER() OVER (ORDER BY) 语句时应该小心,因为性能很差。使用带有ROW_NUMBER() 的公用表表达式也是如此,情况更糟。我正在使用以下 sn-p,它已被证明比使用具有标识的表变量来提供页码稍快。

    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 行。
    【解决方案7】:

    如果您将按顺序处理所有页面,那么只需记住在上一页上看到的最后一个键值并使用TOP (25) ... WHERE Key > @last_key ORDER BY Key 可能是最佳性能方法,如果存在合适的索引以允许有效地寻找它 - 或@987654321 @如果他们不这样做。

    对于选择任意页面,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 
    

    虽然it remains to be seen how well performing this option will be.

    【讨论】:

    【解决方案8】:

    我一直在寻找这个答案一段时间(对于通用查询),并找到了另一种在 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
    

    【讨论】:

    • 我不想在你接近表尾时看到它的表现......
    【解决方案9】:

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

    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 函数只需要查看一列,并且只返回与所有列匹配的行。

    【讨论】:

      【解决方案10】:

      我使用这种技术进行分页。我没有获取所有行。例如,如果我的页面需要显示前 100 行,我只使用 where 子句获取 100 行。 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
      

      这对系统的压力最小

      【讨论】:

        【解决方案11】:

        查看我对分页器的选择

        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
        

        这解决了分页;)

        【讨论】:

          【解决方案12】:

          SQL Server 2012 中有 OFFSET .. FETCH,但您需要指定一个 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 中使用它。然后,这将产生相当随机的排序,而无需任何额外费用。

          【讨论】:

            【解决方案13】:
            SELECT TOP 75 * FROM MyTable
            EXCEPT 
            SELECT TOP 50 * FROM MyTable
            

            【讨论】:

            • 性能方面似乎不是最优的,因为查询随后会不必要地执行两次。尤其是当用户进入更高的页面时,丢弃行的查询,即除了下面的部分将花费越来越长的时间。
            【解决方案14】:

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

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

            您可以根据需要替换 ID

            【讨论】:

            • 请补充一下,这在 SQL SERVER 2012 中是可能的
            【解决方案15】:

            不浪费时间订购记录的最佳方法是这样的:

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

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

            【讨论】:

              【解决方案16】:

              使用 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/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql?view=sql-server-ver15

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

              【讨论】:

                【解决方案17】:

                方法一:

                这里的顺序似乎很重要

                在偏移之前引入限制似乎有效。

                SELECT *
                FROM MyTable
                LIMIT 25
                OFFSET 50
                

                方法二:

                或者,您可以仅使用限制
                LIMIT 有两个值
                第一个:偏移值
                第二:要显示的行数

                选择 *
                来自我的表
                LIMIT (OffsetValue), (NoOfRows)

                SELECT *
                FROM MyTable
                LIMIT 50, 25
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2013-02-09
                  • 2014-07-12
                  • 1970-01-01
                  • 1970-01-01
                  • 2023-03-28
                  • 1970-01-01
                  • 2016-02-28
                  相关资源
                  最近更新 更多