Приклади у запитанні не зовсім дають однакові результати (у OFFSET
прикладі є помилка по одному). Оновлені форми нижче виправляють цю проблему, видаляють зайвий сорт для цього ROW_NUMBER
випадку та використовують змінні, щоб зробити рішення загальнішим:
DECLARE
@PageSize bigint = 10,
@PageNumber integer = 3;
WITH Numbered AS
(
SELECT TOP ((@PageNumber + 1) * @PageSize)
o.*,
rn = ROW_NUMBER() OVER (
ORDER BY o.[object_id])
FROM #objects AS o
ORDER BY
o.[object_id]
)
SELECT
x.name,
x.[object_id],
x.principal_id,
x.[schema_id],
x.parent_object_id,
x.[type],
x.type_desc,
x.create_date,
x.modify_date,
x.is_ms_shipped,
x.is_published,
x.is_schema_published
FROM Numbered AS x
WHERE
x.rn >= @PageNumber * @PageSize
AND x.rn < ((@PageNumber + 1) * @PageSize)
ORDER BY
x.[object_id];
SELECT
o.name,
o.[object_id],
o.principal_id,
o.[schema_id],
o.parent_object_id,
o.[type],
o.type_desc,
o.create_date,
o.modify_date,
o.is_ms_shipped,
o.is_published,
o.is_schema_published
FROM #objects AS o
ORDER BY
o.[object_id]
OFFSET @PageNumber * @PageSize - 1 ROWS
FETCH NEXT @PageSize ROWS ONLY;
ROW_NUMBER
План має орієнтовну вартість 0.0197935 :
OFFSET
План має орієнтовну вартість 0.0196955 :
Це економія 0,000098 одиниць орієнтовної вартості (хоча OFFSET
план потребує додаткових операторів, якщо ви хочете повернути номер рядка для кожного рядка). OFFSET
План ще буде трохи дешевше, взагалі кажучи, але пам'ятайте , що кошторисні витрати саме це - реальне тестування ще потрібно. Основна частина витрат в обох планах становить вартість повного виду вхідного набору, тому корисні індекси виграють обом рішенням.
Якщо використовуються постійні літеральні значення (наприклад, OFFSET 30
в оригінальному прикладі), оптимізатор може використовувати сортування TopN замість повного сортування, а потім Top. Коли рядки, необхідні для сортування TopN, є постійним буквальним значенням і <= 100 (сума OFFSET
та FETCH
), механізм виконання може використовувати інший алгоритм сортування, який може виконувати швидше, ніж узагальнений сортування TopN. Усі три випадки мають загальну характеристику продуктивності.
Щодо того, чому оптимізатор не автоматично трансформує ROW_NUMBER
синтаксичний зразок у використанні OFFSET
, є низка причин:
- Практично неможливо написати перетворення, яке б відповідало всім існуючим напрямкам
- Якщо деякі запити підкачки автоматично трансформуються, а інші не можуть бентежити
OFFSET
План не гарантовано буде краще в усіх випадках
Один із прикладів третьої точки вище - там, де набір підказок досить широкий. Це може бути набагато ефективніше шукати потрібні ключі, використовуючи некластеризований індекс і вручну шукати кластерний індекс в порівнянні зі скануванням індексу з OFFSET
або ROW_NUMBER
. Є додаткові питання, які слід врахувати, чи потрібно додатку підкачки знати, скільки всього рядків чи сторінок. Існує ще одна хороша дискусія відносних переваг «ключ шукати» і «зміщення» методи тут .
В цілому, напевно, краще, щоб люди приймали усвідомлене рішення змінити свої пошукові запити на користування OFFSET
, якщо це доречно, після ретельного тестування.