Пагинація в SQL Server


17

У мене дуже велика база даних, приблизно 100 ГБ. Я виконую запит:

select * from <table_name>;

і я хочу показати лише 100-й-200-й рядки.

Я хочу зрозуміти, як це відбувається всередині. Чи база даних завантажує всі записи з диска в пам'ять і відсилає запитуючий клієнт з 100 по 400 рядки? Або існує якийсь механізм, так що з бази даних виймаються лише ті записи (100-ті - 200-ті) - використовуючи механізм індексації, як B-дерева тощо?

Я виявив, що це пов'язано з концепцією пагинації, але я не міг точно знайти, як це відбувається внутрішньо на рівні бази даних.

Відповіді:


37

У опублікованому вами запиті:

select * from <table_name>;

Немає такого поняття, як 100-й-200-й рядки, оскільки ви не вказуєте ЗАМОВЛЕННЯ. Замовлення не гарантується, якщо ви не включите ЗАМОВЛЕННЯ за цілою низкою цікавих причин, але це насправді не в цьому суть.

Отже, щоб проілюструвати вашу думку, давайте скористаємося таблицею - я буду використовувати таблицю користувачів з дампу даних переповнення стека та запустіть цей запит:

SELECT * FROM dbo.Users ORDER BY DisplayName;

За замовчуванням у полі DisplayName немає індексу, тому SQL Server повинен сканувати всю таблицю, а потім сортувати її за DisplayName. Ось план виконання :

Кластерне сканування індексів сортуванням

Це не дуже - це велика робота, орієнтовна вартість якого складе близько 30 тис. (Ви можете бачити це, якщо навести курсор миші на оператора вибору на PasteThePlan.) Що буде, якщо ми хочемо лише рядків 100-200? Ми можемо використовувати цей синтаксис у SQL Server 2012+:

SELECT * FROM dbo.Users ORDER BY DisplayName OFFSET 100 ROWS FETCH NEXT 100 ROWS ONLY;

План виконання щодо цього теж досить потворний:

Кластерне сканування індексів сортуванням і верхом

SQL Server все ще сканує всю таблицю для складання відсортованого списку лише для того, щоб дати вам рядки 100-200, а вартість все ще становить близько 30 тис. Ще гірше, що весь цей список буде перебудовуватися щоразу, коли ваш запит запускається (адже зрештою, хтось міг змінити своє DisplayName.)

Щоб зробити це швидше, ми можемо створити некластеризований індекс на DisplayName, який є копією нашої таблиці, відсортованою за цим конкретним полем:

CREATE INDEX IX_DisplayName ON dbo.Users(DisplayName);

За допомогою цього індексу план виконання нашого запиту зараз шукає індекс:

Шукати індекси та пошук ключів

Запит завершується миттєво і має орієнтовну вартість піддерева лише 0,66 (на відміну від 30 к).

Підсумовуючи це, якщо ви впорядковуєте дані таким чином, щоб підтримувати запити, які ви часто виконуєте, то так, SQL Server може приймати ярлики для швидшого запуску запитів. Якщо, з іншого боку, все, що ви маєте, це купи або кластеризовані індекси, ви накручені.


"За замовчуванням у полі DisplayName немає індексу, тому SQL Server повинен сканувати всю таблицю, а потім сортувати її за DisplayName". Вибачте мене, якщо це дуже основне питання - у випадку, коли я цитував вашу відповідь, коли ви сказав "Сканувати всю таблицю", чи це означає, що всі дані заносяться в пам'ять і сортуються (що не схоже на правильний шлях)?
AV94

З вашої відповіді я розумію, що якщо поле індексується, то робити запити на зразок - отримати 100-й до 200-й рядок дуже ефективно, оскільки SQL шукає індекс (B-дерево тощо) і безпосередньо переходить до цієї точки (100-й рядок). Скажіть, будь ласка, чи правильно це розуміння?
AV94

@AnilVedala про ваше перше запитання - так, дані мають бути відсортовані. Як ще можна було б виконати базу даних з несортованим списком?
Брент Озар

1
@AnilVedala з приводу вашого другого питання - саме там входить останній план виконання, який я вам дав. (Якщо ви запитуєте про те, як прочитати план виконання, підберіть книгу "Плани виконання" Гранта Фрітчея.)
Брент Озар

15

Так само як доповнення до відповіді Брента при використанні неіндексуючого індексу для уникнення сортування є потенційна проблема із пізнішими номерами сторінок, які видно із запуску нижче

SELECT * 
FROM dbo.Users 
ORDER BY DisplayName 
OFFSET 100000 ROWS 
FETCH NEXT 100 ROWS ONLY;

План виконання показує, що пошук був виконаний 100 100 разів, хоча всі операції, окрім 100, потім фільтруються оператором TOP.

введіть тут опис зображення

Це можна пом'якшити за допомогою наведеної нижче схеми

WITH T
     AS (SELECT Id,
                DisplayName
         FROM   dbo.Users
         ORDER  BY DisplayName
        OFFSET 100000 ROWS 
        FETCH NEXT 100 ROWS ONLY
        )
SELECT U.*
FROM   dbo.Users U
       JOIN T
         ON U.Id = T.Id
ORDER  BY T.DisplayName 

Це фільтрує всі, крім останніх 100 рядків, перед тим, як зробити пошук, який може мати значний вплив на швидкість для великих значень зміщення.

введіть тут опис зображення


3

Це дійсно залежить від того, як ви реалізуєте розбиття сторінки у вашому запиті, характер даних та спосіб налаштування вашої системи. Досить впевнено сказати, що SQL Server намагатиметься повернути ваші дані, використовуючи те, що, на його думку, є мінімально можливим зусиллям. Якщо у вас немає чіткого порядку сортування, фільтрування, групування чи будь-якого вікна, тоді SQL Server може оптимізувати план запитів таким чином, щоб він міг повертати лише ті сторінки з диска, які містили дані, необхідні для вашого запиту, а ще краще, безпосередньо з буферний басейн. Як тільки ви почнете змінювати запит, щоб він включав сортування, групування, відкривання вікон та фільтрування, він починає ускладнюватися.

Існує дуже хороша стаття про SQL Performance тут , який йде в деталі різних методів верстки і як вони впливають на план виконання запиту. Я дуже рекомендую прочитати його, а потім спробувати деякі з різних методів, які вони вказують, і побачити, який план запитів обраний у вашій власній системі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.