有一种简单的方法可以从任何表中获取前N行:
SELECT TOP 10 * FROM MyTable ORDER BY MyColumn
Run Code Online (Sandbox Code Playgroud)
有没有有效的方法从行N开始查询M行
例如,
Id Value
1 a
2 b
3 c
4 d
5 e
6 f
Run Code Online (Sandbox Code Playgroud)
并查询这样的
SELECT [3,2] * FROM MyTable ORDER BY MyColumn /* hypothetical syntax */
Run Code Online (Sandbox Code Playgroud)
查询从3d行开始的2行,即返回3d和第4行.
Jan*_*ich 90
更新如果您使用的是SQL 2012,则会添加新语法以使其变得非常简单.请参阅使用此查询实现分页(跳过/获取)功能
我想最优雅的是使用ROW_NUMBER函数(可从MS SQL Server 2005获得):
WITH NumberedMyTable AS
(
SELECT
Id,
Value,
ROW_NUMBER() OVER (ORDER BY Id) AS RowNumber
FROM
MyTable
)
SELECT
Id,
Value
FROM
NumberedMyTable
WHERE
RowNumber BETWEEN @From AND @To
Run Code Online (Sandbox Code Playgroud)
Dan*_*ace 16
该线程和网络上其他地方的建议存在的问题是,所有提出的解决方案都在线性时间内相对于记录数量运行.例如,考虑如下查询.
select *
from
(
select
Row_Number() over (order by ClusteredIndexField) as RowNumber,
*
from MyTable
) as PagedTable
where RowNumber between @LowestRowNumber and @HighestRowNumber;
Run Code Online (Sandbox Code Playgroud)
获取第1页时,查询需要0.577秒.但是,在获取第15,619页时,同样的查询需要2分55秒.
我们可以通过创建记录号,索引交叉表来大大改善这一点,如下面的查询所示.交叉表称为PagedTable,并且是非持久的.
select *
from
(
select
Row_Number() over (order by Field1 asc, Field2 asc, Field3 asc) as RowNumber,
ClusteredIndexField
from MyTable
) as PagedTable
left join MyTable on MyTable.ClusteredIndexField = PagedTable.ClusteredIndexField
where RowNumber between @LowestRowNumber and @HighestRowNumber;
Run Code Online (Sandbox Code Playgroud)
与上一个示例中一样,我在一个包含780,928条记录的非常宽的表中对此进行了测试.我使用的页面大小为50,结果为15,619页.
第1页(第一页)所用的总时间为0.413秒.第15,619页(最后一页)所用的总时间为0.987秒,仅为第1页的两倍.这些时间是使用SQL Server Profiler测量的,DBMS是SQL Server 2008 R2.
当您按索引对表进行排序时,此解决方案适用于任何情况.索引不必是聚簇的或简单的.在我的例子中,索引由三个字段组成:varchar(50)asc,varchar(15)asc,numeric(19,0)asc.尽管索引繁琐,但性能非常出色,这进一步证明了这种方法的有效性.
但是,Row_Number窗口函数中的order by子句对应于索引是至关重要的.否则,性能将降低到与第一个示例相同的级别.
这种方法仍然需要线性操作来生成非持久性交叉表,但由于这只是一个添加了行号的索引,因此它很快就会发生.在我的情况下花了0.347秒,但我的案例有需要复制的varchars.单个数字索引将花费更少的时间.
出于所有实际目的,此设计减少了服务器端分页从线性操作到对数操作的扩展,允许扩展大型表.以下是完整的解决方案.
-- For a sproc, make these your input parameters
declare
@PageSize int = 50,
@Page int = 15619;
-- For a sproc, make these your output parameters
declare @RecordCount int = (select count(*) from MyTable);
declare @PageCount int = ceiling(convert(float, @RecordCount) / @PageSize);
declare @Offset int = (@Page - 1) * @PageSize;
declare @LowestRowNumber int = @Offset;
declare @HighestRowNumber int = @Offset + @PageSize - 1;
select
@RecordCount as RecordCount,
@PageCount as PageCount,
@Offset as Offset,
@LowestRowNumber as LowestRowNumber,
@HighestRowNumber as HighestRowNumber;
select *
from
(
select
Row_Number() over (order by Field1 asc, Field2 asc, Field3 asc) as RowNumber,
ClusteredIndexField
from MyTable
) as PagedTable
left join MyTable on MyTable.ClusteredIndexField = PagedTable.ClusteredIndexField
where RowNumber between @LowestRowNumber and @HighestRowNumber;
Run Code Online (Sandbox Code Playgroud)
Tri*_*ped 10
在SQL 2012中,您可以使用OFFSET
和FETCH
:
SELECT *
FROM MyTable
ORDER BY MyColumn
OFFSET @N ROWS
FETCH NEXT @M ROWS ONLY;
Run Code Online (Sandbox Code Playgroud)
DECLARE @CurrentSetNumber int = 0;
DECLARE @NumRowsInSet int = 2;
SELECT *
FROM MyTable
ORDER BY MyColumn
OFFSET @NumRowsInSet * @CurrentSetNumber ROWS
FETCH NEXT @NumRowsInSet ROWS ONLY;
SET @CurrentSetNumber = @CurrentSetNumber + 1;
Run Code Online (Sandbox Code Playgroud)
@NumRowsInSet
您想要返回的行数在哪里,是要跳过的行@CurrentSetNumber
数@NumRowsInSet
.
小智 8
如果要从第25条记录中选择100条记录:
select TOP 100 * from TableName
where PrimaryKeyField
NOT IN(Select TOP 24 PrimaryKeyField from TableName);
Run Code Online (Sandbox Code Playgroud)
丑陋,hackish,但应该工作:
select top(M + N - 1) * from TableName
except
select top(N - 1) * from TableName
Run Code Online (Sandbox Code Playgroud)