Навіщо використовувати пункт INCLUDE під час створення індексу?


431

Під час навчання на іспиті 70-433 я помітив, що ви можете створити індекс покриття одним із наступних двох способів.

CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)

- АБО -

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

Стаття INCLUDE для мене нова. Навіщо використовувати його та які вказівки ви б запропонували, визначаючи, чи створювати індекс покриття з або без пункту INCLUDE?

Відповіді:


363

Якщо стовпець не в WHERE/JOIN/GROUP BY/ORDER BY, а лише у списку стовпців у SELECTпункті.

INCLUDEПункт додає дані на найнижчому / лист, а не в дереві індексу. Це робить індекс меншим, оскільки він не є частиною дерева

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

Ще одна стаття MSDN з відпрацьованим прикладом


7
Отже, це була б техніка створення менш дорогої версії критого індексу?
JMarsch

3
@gbn, чи не заперечуєте ви пояснити це речення більш докладно та поясніть, чому це означає, що пункт включення не корисний для сортування тощо: "Включення пропозиції додає дані на найнижчому / рівні листа, а не в індексному дереві . Це робить індекс меншим, тому що він не є частиною дерева "
Tola Odejayi

4
@JMarsch: вибачте за несвоєчасну відповідь, але так, саме це і є.
gbn

10
@Tola Odejayi: INCLUDE стовпці не є ключовими стовпцями в індексі, тому вони не впорядковані. Це робить їх зазвичай не корисними для приєднання або сортування. А оскільки вони не є ключовими стовпцями, вони не сидять у цілій структурі B-дерева, як ключові стовпці
gbn

4
Хоча це найбільш прийнята відповідь, я думаю, що потрібне подальше пояснення: що, якщо для деяких запитів стовпець є частиною, SELECTа для деяких - ні? \
Chisko

215

Ви б використовували INCLUDE, щоб додати один або декілька стовпців до рівня аркуша некластеризованого індексу, якщо, зробивши це, ви зможете "охопити" ваші запити.

Уявіть, що вам потрібно запитати особисту особу, ідентифікатор відділу та прізвище.

SELECT EmployeeID, DepartmentID, LastName
FROM Employee
WHERE DepartmentID = 5

Якщо у вас є некластеризований індекс на (EmployeeeID, DepartmentID), як тільки ви знайдете співробітників для певного відділу, вам доведеться зробити "пошук закладок", щоб отримати фактичну повну реєстрацію працівників, просто щоб отримати стовпець прізвища . Це може стати досить дорогим з точки зору продуктивності, якщо ви знайдете багато співробітників.

Якщо ви включили це прізвище у свій індекс:

CREATE NONCLUSTERED INDEX NC_EmpDep 
  ON Employee(EmployeeID, DepartmentID)
  INCLUDE (Lastname)

то вся необхідна інформація доступна на рівні листків некластеризованого індексу. Тільки шукаючи некластеризований індекс і знаходячи своїх співробітників для певного відділу, ви маєте всю необхідну інформацію, і пошук закладок для кожного співробітника, знайденого в індексі, більше не потрібен -> ви економите багато часу.

Очевидно, що ви не можете включити кожен стовпець до кожного некластеризованого індексу, але якщо у вас є запити, на яких не вистачає лише одного або двох стовпців, щоб бути "охопленими" (і які звикають багато), це може бути дуже корисно ВКЛЮЧИТИ ці у відповідний некластеризований індекс.


25
Ви впевнені, що використовуєте цей індекс? Чому ID працівника? Вам потрібен лише DepartmentID у ключових стовпцях? Ви цитували тут authoratitive: stackoverflow.com/q/6187904/27535
ГБН

3
Ваше пояснення добре, але насправді не узгоджується із випадком використання, який ви окреслили. Стовпчик ключів повинен знаходитись у фільтрі чи JOINклавішах у запиті, а INCLUDEs - це дані, які ви отримуєте, але не сортуєте.
JNK

15
Перш за все індекс Employee (EmployeeID, DepartmentID) не буде використовуватися для фільтрації DepartmentID = 5. Оскільки його порядок не відповідає
AnandPhadke

29

Ця дискусія відсутня у важливому моменті. Питання не в тому, чи краще "стовпчики, що не мають ключа", включити як індекс- стовпці або як включені- стовпці.

Питання в тому, як дорого використовувати механізм include-включення стовпців, які насправді не потрібні в індексі ? (як правило, не є частиною де-застережень, але часто включається до вибору). Тож завжди ваша дилема:

  1. Використовуйте індекс на id1, id2 ... idN самостійно або
  2. Використовуйте індекс на id1, id2 ... idN плюс включайте col1, col2 ... colN

Де: id1, id2 ... idN - стовпці, які часто використовуються в обмеженнях, і col1, col2 ... colN - це стовпці, які часто вибираються, але зазвичай не використовуються в обмеженнях

(Варіант включення всіх цих стовпців до складу ключа-індексу просто завжди нерозумний (якщо вони також не використовуються в обмеженнях) - тому що його завжди було б дорожче підтримувати, оскільки індекс повинен бути оновлений і відсортований, навіть коли "клавіші" не змінилися).

Тож використовуйте варіант 1 або 2?

Відповідь: Якщо ваша таблиця рідко оновлюється - здебільшого вставляється в / видаляється з -, використовувати механізм include для включення деяких "гарячих стовпців" (які часто використовуються у вибраних, але не часто використовуваних обмеженнях) порівняно недорого Вставки / видалення вимагають оновлення / сортування індексу в будь-якому випадку, і, таким чином, невеликі додаткові накладні витрати пов'язані зі збереженням декількох зайвих стовпців під час вже оновлення індексу. Накладні витрати - це додаткова пам'ять і процесор, який використовується для зберігання зайвої інформації в індексі.

Якщо стовпці, які ви вважаєте доданими як стовпці, що додаються, часто оновлюються (без індексу- ключа- стовпці не оновлюються) - або - якщо їх так багато, що індекс стає близьким до копії таблиці - використовуйте варіант 1 Я б запропонував! Крім того, якщо додавання певних стовпців з включеннями не впливає на ефективність - ви можете пропустити ідею додавання їх :) Перевірте, чи вони корисні!

Середня кількість рядків на однакові значення в ключах (id1, id2 ... idN) також може мати певне значення.

Зауважте, що якщо стовпчик, який додається як включений стовпець індексу, використовується в обмеженні : Доки індекс як такий може бути використаний (заснований на обмеження проти індексу- ключа- стовпці) - тоді SQL Server відповідає обмеження стовпця щодо індексу (значень leaf-node), а не дорогий шлях навколо самої таблиці.


18

Основні стовпці індексу сортуються, але включені стовпці не сортуються. Це економить ресурси на підтримці індексу, при цьому все ще дає змогу надати дані у включені стовпці для покриття запиту. Отже, якщо ви хочете охопити запити, ви можете поставити критерії пошуку, щоб розташувати рядки в відсортованих стовпцях індексу, але потім "включити" додаткові, несортовані стовпці з нереєстровими даними. Це безумовно допомагає зменшити кількість сортування та фрагментації в індексі.


7

Причини, чому (включаючи дані на рівні аркуша аркуша), були чітко пояснені. Причина, що ви даєте два похитки з цього приводу, полягає в тому, що при запуску запиту, якщо у вас немає додаткових стовпців (нова функція в SQL 2005), SQL Server повинен перейти до кластерного індексу, щоб отримати додаткові стовпці що займає більше часу і додає більше навантаження службі SQL Server, дискам і пам'яті (кеш-пам'ять буфера), оскільки нові сторінки даних завантажуються в пам'ять, потенційно витісняючи інші, більш часто потрібні дані, з кеш-пам'яті буфера.


чи є спосіб довести, що він насправді використовує менше пам'яті? це те, що я теж очікував, але я отримую статичну інформацію про це на роботі
запитав

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

5

Додатковим міркуванням, яке я не бачив у вже наведених відповідях, є те, що включені стовпці можуть мати типи даних, які не дозволені як стовпці ключових індексів, такі як varchar (max).

Це дозволяє включати такі стовпці до індексу покриття. Нещодавно мені довелося це зробити, щоб забезпечити корисний індекс, створений nHibernate запитом, який мав багато стовпців у SELECT.


3

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

Розглядаючи ваш приклад:

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

Цей індекс найкраще, якщо ваш запит виглядає так:

SELECT col2, col3
  FROM MyTable
 WHERE col1 = ...

Звичайно, вам не слід ставити стовпці, INCLUDEякщо ви зможете отримати додаткову вигоду від наявності їх у ключовій частині. Обидва наступні запити фактично віддають перевагу col2стовпцю в ключі індексу.

SELECT col2, col3
  FROM MyTable
 WHERE col1 = ...
   AND col2 = ...
SELECT TOP 1 col2, col3
  FROM MyTable
 WHERE col1 = ...
 ORDER BY col2

Припустимо, що це не так, і ми маємо це col2в INCLUDEпункті, оскільки просто немає користі від того, щоб це було в деревній частині індексу.

Швидкий вперед кілька років.

Потрібно налаштувати цей запит:

SELECT TOP 1 col2
  FROM MyTable
 WHERE col1 = ...
 ORDER BY another_col

Для оптимізації цього запиту чудовим буде такий індекс:

CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2)

Якщо ви перевірите, які індекси вже є у цій таблиці, ваш попередній індекс може все ще бути там:

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

Тепер ви знаєте , що Col2і Col3не є частиною індексного дерева і, таким чином , не використовуються , щоб звузити діапазон індексів для читання , ні для упорядкування рядків. Досить безпечно додати another_columnдо кінця ключову частину індексу (після col1). Малий ризик щось зламати:

DROP INDEX idx1 ON MyTable;
CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2, Col3);

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

Якщо у вас не буде індексу INCLUDE, ви не могли б знати, які запити ви порушите, додавши another_colвідразу після Col1.

CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)

Що станеться, якщо додати another_colміж Col1і Col2? Чи страждатимуть інші запити?

Є й інші "переваги" INCLUDEстовпців порівняно з клавішами, якщо ви додаєте ці стовпчики просто, щоб уникнути їх отримання з таблиці . Однак я вважаю аспект документації найважливішим.

Щоб відповісти на ваше запитання:

які вказівки ви б запропонували, визначаючи, чи створювати індекс покриття з або без пункту INCLUDE?

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

Якщо додавання стовпчика до індексного ключа приносить додаткові переваги (наприклад, для order byабо тому, що це може звузити діапазон індексу зчитування), додайте його до ключа.

Ви можете прочитати довшу дискусію з цього приводу тут:

https://use-the-index-luke.com/blog/2019-04/include-columns-in-btree-indexes


2

Існує обмеження на загальний розмір усіх стовпців, вкладених у визначення індексу. Це, однак, сказало, що мені ніколи не доводилося створювати індекс так широко. Для мене більшою перевагою є той факт, що ви можете охоплювати більше запитів одним індексом, який містить стовпці, оскільки їх не потрібно визначати в якомусь конкретному порядку. Подумайте, як індекс всередині індексу. Одним із прикладів може бути StoreID (де StoreID - низька селективність, що означає, що кожен магазин асоціюється з великою кількістю клієнтів), а потім демографічні дані клієнтів (LastName, FirstName, DOB): Якщо ви просто вкладете ці стовпці в цьому порядку (StoreID, LastName , FirstName, DOB), ви можете ефективно шукати лише тих клієнтів, для яких ви знаєте StoreID та LastName.

З іншого боку, визначення індексу на StoreID та включення LastName, FirstName, DOB стовпців дозволить вам, по суті, зробити два домагання-індексування предикату на StoreID, а потім шукати предикат для будь-якого з включених стовпців. Це дозволить вам охопити всі можливі перестановки пошуку, поки це починається з StoreID.

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