简体   繁体   中英

Sql Server paging rows by offset - without 'ORDER BY'

A production table of mine contains over a million records. An requirement calls for a paging query to retrieve records by OFFSET and LIMIT parameters(similar to MySql's LIMIT clause), without sorting the result set, just as rows' natural order are in a table-scan, since 'ORDER BY' generates unacceptable performance impact, including 'ORDER BY' clause used in traditional technique of ROW_NUMBER() OVER (ORDER BY ...).
Could any expert offer a solution to this problem? Paging records without any ordering of the result set. eg

Create table RandomRecords(int id, datetime recordDate)
----
select * from RandomRecords
34, '1/1/2009'
123, '8/1/2008'
11, '2/23/2008'
10, '3/2/2008'
4, '2/5/2009'
78, '1/1/2008'
55, '5/2/2008'
6666, '2/12/2009'
....
one million rows

-----
paging query with @Offset = 3 and @limit=4 generates
11, '2/23/2008'
10, '3/2/2008'
4, '2/5/2009'
78, '1/1/2008'

This is just meant as a comment in addition to Remus' answer.

Paging a primary key doesn't cause SQL Server to sort, because the primary key is stored in sort order. You can page on primary key without a WITH statement like:

SELECT *
FROM (
    SELECT 
        ROW_NUMBER() OVER (ORDER BY PrimaryKey) as rn
    ,   *
    FROM BigTable
) sub 
WHERE sub.rn BETWEEN 3 and 7

The subquery is still required, because you can't use ROW_NUMBER() in a WHERE statement.

ORDER BY only generates additional impact if it cannot be addressed by an index. If you see 'unacceptable' impact it means that either you did not properly design your table, or you did not properly design the query.

Some us constant ORDER BY expressions SELECT ..., ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM ... . But that may still generate a sorting spool if not properly designed.

So to consider your example:

CREATE TABLE RandomRecords (recordId int not null primary key, id int);
INSERT INTO RandomRecords (id) values (...)
WITH PagedRandomRecords (
   SELECT id, 
      ROW_NUMBER() OVER (ORDER BY recordId) as rn
      FROM RandomRecords)
SELECT id FROM PagedRandomRecords
   WHERE rn BETWEEN 3 and 7;

This will not sort the data because the recordId PK clustered index can deliver the rows in the needed order.

If you still find no column for ORDER BY, you may sort by a constant column you add just to get the query working:

SELECT col1, col2 FROM
   (SELECT col1, col2,
      ROW_NUMBER() OVER (ORDER BY alias_sort) AS alias_rownum
   FROM 
      (SELECT col1, col2, 0 AS alias_sort
      FROM 
         (SELECT col1, col2
         FROM ...)))
WHERE alias_rownum >= 12345 AND alias_rownum <= 67890

" 0 AS alias_sort " provides the constant column used in the ORDER BY clause in the parent query. Top outer query provides the filter and gets rid of both surrogate alias_rownum and alias_sort inner columns.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM