如何从T-SQL中的排序表中的行M开始获取N行

ina*_*ruk 64 sql t-sql

有一种简单的方法可以从任何表中获取前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)

  • 如果在`WITH`之前有select语句,则必须用分号结束前一个语句(有效地将`WITH`替换为`; WITH`) (3认同)

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)

  • 附录......我用一张包含13,200,833条记录的表格测试了这种方法,页面大小为50,结果为264,017页.访问第1页需要19秒.访问最后一页花了1分11秒.虽然这仍然是对其他方法的巨大改进,但它对于实时响应来说还不够.要克服此性能问题,请在内部选择中添加"前N".这样可以减少以限制总结果为代价获取任何页面所需的时间.您还可以将任何过滤参数添加到内部select的where子句中. (2认同)

Tri*_*ped 10

SQL 2012中,您可以使用OFFSETFETCH:

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)

  • 根据返回的数据量,这可能不是大查询的最佳解决方案 (2认同)

Har*_*lby 5

丑陋,hackish,但应该工作:

select top(M + N - 1) * from TableName
except
select top(N - 1) * from TableName
Run Code Online (Sandbox Code Playgroud)