SQL Server 中的行偏移量
Posted
技术标签:
【中文标题】SQL Server 中的行偏移量【英文标题】:Row Offset in SQL Server 【发布时间】:2010-09-16 07:46:38 【问题描述】:SQL Server 中是否有任何方法可以从给定偏移量开始获取结果?例如,在另一种类型的 SQL 数据库中,可以这样做:
SELECT * FROM MyTable OFFSET 50 LIMIT 25
获得结果 51-75。 SQL Server 中似乎不存在此构造。
如何在不加载所有我不关心的行的情况下完成此操作?谢谢!
【问题讨论】:
对于喜欢官方文档而不是视频的人:docs.microsoft.com/en-us/sql/t-sql/queries/… 【参考方案1】:我会避免使用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 的回答【参考方案2】:
如果您将按顺序处理所有页面,那么只需记住在上一页上看到的最后一个键值并使用TOP (25) ... WHERE Key > @last_key ORDER BY Key
可能是最佳性能方法,如果存在合适的索引以允许有效地寻找它 - 或@987654321 @如果他们不这样做。
对于选择任意页面,SQL Server 2005 - 2008 R2 的最佳解决方案可能是 ROW_NUMBER
和 BETWEEN
对于 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.
【讨论】:
现在可以在 SQL Server Compact 4.0 中使用 --> msdn.microsoft.com/en-us/library/gg699618(v=sql.110).aspx 是时候将它添加到 tSQL 中了 仅适用于 Sql Server 2012 :(【参考方案3】:这是一种方式(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 的解决方案不适用于结果集中的最后一页,除非总行数恰好是页面大小的倍数。【参考方案4】:你可以使用ROW_NUMBER()
函数来得到你想要的:
SELECT *
FROM (SELECT ROW_NUMBER() OVER(ORDER BY id) RowNr, id FROM tbl) t
WHERE RowNr BETWEEN 10 AND 20
【讨论】:
【参考方案5】: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 中使用它。然后,这将产生相当随机的排序,而无需任何额外费用。
【讨论】:
【参考方案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 函数只需要查看一列,并且只返回与所有列匹配的行。
【讨论】:
【参考方案7】:查看我的分页器选择
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
这解决了分页;)
【讨论】:
【参考方案8】:SELECT TOP 75 * FROM MyTable
EXCEPT
SELECT TOP 50 * FROM MyTable
【讨论】:
性能方面似乎不是最优的,因为查询随后会不必要地执行两次。尤其是当用户进入更高的页面时,丢弃行的查询,即除了下面的部分将花费越来越长的时间。【参考方案9】:根据您的版本,您无法直接执行此操作,但您可以执行一些 hacky 之类的操作
select top 25 *
from (
select top 75 *
from table
order by field asc
) a
order by field desc
其中“字段”是键。
【讨论】:
SQL2000 的解决方案不适用于结果集中的最后一页,除非总行数恰好是页面大小的倍数。【参考方案10】:以下将显示 25 条记录,不包括 SQL Server 2012 中的前 50 条记录。
SELECT * FROM MyTable ORDER BY ID OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY;
您可以根据需要替换 ID
【讨论】:
请补充一下,这在 SQL SERVER 2012 中是可能的【参考方案11】:使用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 行。【参考方案12】:我使用这种技术进行分页。我没有获取所有行。例如,如果我的页面需要显示前 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
这对系统的压力最小
【讨论】:
【参考方案13】:在 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 行。【参考方案14】:
不浪费时间订购记录的最佳方法是这样的:
select 0 as tmp,Column1 from Table1 Order by tmp OFFSET 5000000 ROWS FETCH NEXT 50 ROWS ONLY
不到一秒! 大型表的最佳解决方案。
【讨论】:
【参考方案15】:使用 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 才能包含第一条记录。
【讨论】:
【参考方案16】:我一直在寻找这个答案一段时间(对于通用查询),并找到了另一种在 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
【讨论】:
我不想在你接近表尾时看到它的表现......【参考方案17】:方法一:
这里的顺序似乎很重要
在偏移之前引入限制似乎有效。
SELECT *
FROM MyTable
LIMIT 25
OFFSET 50
方法二:
或者,您可以仅使用限制 LIMIT 有两个值 第一个:偏移值 第二:要显示的行数
选择 * 来自我的表 LIMIT (OffsetValue), (NoOfRows)
SELECT *
FROM MyTable
LIMIT 50, 25
【讨论】:
以上是关于SQL Server 中的行偏移量的主要内容,如果未能解决你的问题,请参考以下文章
iOS7 在 UITableViewCell 中以适当的偏移量编辑 UITextField 时滚动到 UITableViewController 的行