Зачем использовать предложение INCLUDE при создании индекса?

Во время подготовки к экзамену 70-433 я заметил, что вы можете создать покрывающий индекс одним из следующих двух способов.

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

-- OR --

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

Предложение INCLUDE для меня ново. Зачем вам его использовать и какие рекомендации вы бы предложили при определении того, следует ли создавать покрывающий индекс с предложением INCLUDE или без него?


person Cory    schedule 20.08.2009    source источник


Ответы (8)


Если столбец находится не в WHERE/JOIN/GROUP BY/ORDER BY, а только в списке столбцов в предложении SELECT, вы используете INCLUDE.

Предложение INCLUDE добавляет данные на самом нижнем / листовом уровне, а не в дереве индекса. Это делает индекс меньше, потому что он не является частью дерева

INCLUDE columns не являются ключевыми столбцами индекса, поэтому они не упорядочены. Это означает, что он бесполезен для предикатов, сортировки и т. Д., Как я упоминал выше. Однако это может быть полезным, если у вас есть остаточный поиск в нескольких строках из ключевого столбца (столбцов).

Еще одна статья MSDN с наработанным примером

person gbn    schedule 20.08.2009
comment
Итак, это был бы метод создания менее затратной версии покрытого индекса? - person JMarsch; 15.09.2012
comment
@gbn, не могли бы вы объяснить это предложение более подробно и объяснить, почему это означает, что предложение include бесполезно для сортировки и т. д. Предложение INCLUDE добавляет данные на самом нижнем / листовом уровне, а не в дереве индекса. Это делает индекс меньше, потому что он не является частью дерева - person Tola Odejayi; 08.05.2013
comment
@JMarsch: извините за поздний ответ, но да, это именно то, что есть. - person gbn; 08.05.2013
comment
@Tola Odejayi: столбцы INCLUDE не являются ключевыми столбцами в индексе, поэтому они не упорядочены. Это делает их не обычно полезными для СОЕДИНЕНИЙ или сортировки. И поскольку они не являются ключевыми столбцами, они не входят во всю структуру B-дерева, как ключевые столбцы. - person gbn; 08.05.2013
comment
@TolaOdejayi: и прочтите также эту серию (ссылка только на один бит, имеющий отношение к этому ответу) Practicalsqldba.com/2013/03/ - person gbn; 08.05.2013
comment
Хотя это наиболее распространенный ответ, я думаю, что необходимы дополнительные пояснения, что, если для некоторых запросов столбец является частью SELECT, а для некоторых нет? \ - person Chisko; 08.03.2017
comment
Если select использует только ключевые и включенные столбцы (в списке выбора или в предложении where), то вы можете увидеть только один шаг поиска из этого индекса в плане выполнения. Если select использует больше столбцов, тогда оптимизатор должен запланировать дополнительный поиск из физической строки с использованием кластерного индекса, чтобы получить дополнительные значения. Включенные столбцы следует использовать осторожно, потому что такой индекс содержит повторяющиеся данные, которые требуют дополнительного места и должны обновляться при изменении неключевых значений. Не используйте его, если ваши команды похожи на select * from .... - person Hink; 26.11.2018

Вы можете использовать INCLUDE, чтобы добавить один или несколько столбцов на конечный уровень некластеризованного индекса, если таким образом вы можете «покрыть» свои запросы.

Представьте, что вам нужно запросить идентификатор сотрудника, идентификатор отдела и фамилию.

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

Если у вас есть некластеризованный индекс для (EmployeeID, DepartmentID), после того, как вы найдете сотрудников для данного отдела, вам теперь нужно выполнить «поиск по закладкам», чтобы получить фактическую полную запись о сотруднике, просто чтобы получить столбец с фамилией. . Это может обойтись довольно дорого с точки зрения производительности, если вы найдете много сотрудников.

Если вы включили эту фамилию в свой индекс:

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

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

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

person marc_s    schedule 20.08.2009
comment
Вы уверены, что будете использовать этот индекс? Почему EmployeeID? Вам нужен только DepartmentID в ключевых столбцах? Здесь вас цитируют как автора: stackoverflow.com/q/6187904/27535 - person gbn; 31.05.2011
comment
Ваше объяснение хорошее, но на самом деле не совпадает с описанным вами вариантом использования. Ключевые столбцы должны быть в фильтре или JOIN ключах в запросе, а INCLUDE должны быть данными, которые вы извлекаете, но не сортируете. - person JNK; 01.02.2012
comment
Прежде всего, индекс Employee (EmployeeID, DepartmentID) не будет использоваться для фильтрации DepartmentID = 5. Поскольку его порядок не соответствует - person AnandPhadke; 02.04.2013

В этом обсуждении упускается важный момент: вопрос не в том, лучше ли включать «неключевые столбцы» в качестве столбцов index или в качестве включенных столбцов. .

Вопрос в том, насколько дорого обходится использование механизма включения для включения столбцов, которые на самом деле не нужны в индекс? (обычно не входит в предложения where, но часто включается в выборки). Итак, ваша дилемма всегда:

  1. Использовать индекс для id1, id2 ... idN отдельно или
  2. Использовать индекс для id1, id2 ... idN plus include col1, col2 ... colN

Где: id1, id2 ... idN - столбцы, часто используемые в ограничениях, а col1, col2 ... colN - часто выбираемые столбцы, но обычно не используемые в ограничениях

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

Так что используйте вариант 1 или 2?

Ответ: Если ваша таблица редко обновляется - в основном вставляется в / удаляется из - тогда относительно недорого использовать механизм включения для включения некоторых «горячих столбцов» (которые часто используются в выборках, но не часто используется для ограничений), поскольку вставки / удаления требуют, чтобы индекс был обновлен / отсортирован в любом случае, и поэтому небольшие дополнительные накладные расходы связаны с сохранением нескольких дополнительных столбцов при уже обновлении индекса. Накладные расходы - это дополнительная память и ЦП, используемые для хранения избыточной информации в индексе.

Если столбцы, которые вы планируете добавить как включенные столбцы, часто обновляются (без обновления index- key -columns) - или - если их так много, что индекс становится близким к копии вашей таблицы - используйте вариант 1, который я предлагаю! Также, если добавление определенных включаемых столбцов не повлияет на производительность - вы можете пропустить идею их добавления :) Убедитесь, что они полезны!

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

Обратите внимание, что если столбец, добавленный как включенный столбец индекса, используется в ограничении: Пока можно использовать индекс как таковой (на основе ограничения для столбцов index- key) - тогда SQL Server сопоставляет ограничение столбца с индексом (значения конечных узлов) вместо того, чтобы идти дорогостоящим способом по сам стол.

person Fredrik Solhaug    schedule 13.06.2015

Столбцы базового индекса сортируются, но включенные столбцы не сортируются. Это экономит ресурсы при обслуживании индекса, но при этом позволяет предоставлять данные во включенных столбцах для покрытия запроса. Итак, если вы хотите охватить запросы, вы можете поместить критерии поиска, чтобы найти строки в отсортированных столбцах индекса, но затем «включить» дополнительные, несортированные столбцы с данными, не относящимися к поиску. Это определенно помогает уменьшить объем сортировки и фрагментации при обслуживании индексов.

person onupdatecascade    schedule 20.08.2009

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

person mrdenny    schedule 22.08.2009
comment
есть ли способ доказать, что он действительно использует меньше памяти? это то, чего я тоже ожидал, но я получаю некоторую статику по этому поводу на работе - person Asken; 16.11.2012
comment
Учитывая, что вам нужно загрузить страницу из кучи или кластеризованного индекса в память, а также страницу индекса, что означает, что вы помещаете повторяющиеся данные в память, математика становится довольно простой. Что касается способа конкретно его измерить, то нет. - person mrdenny; 17.11.2012

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

Это позволяет включать такие столбцы в индекс покрытия. Недавно мне пришлось сделать это, чтобы предоставить сгенерированный nHibernate запрос, в котором было много столбцов в SELECT, с полезным индексом.

person Robin Hames    schedule 21.10.2013

Одна из причин предпочесть 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

person Markus Winand    schedule 30.05.2019

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

С другой стороны, определение индекса для StoreID и включение столбцов LastName, FirstName, DOB, по сути, позволит вам выполнить два предиката поиска индекса для StoreID, а затем предикат поиска для любого из включенных столбцов. Это позволит вам охватить все возможные перестановки поиска, если они начинаются с StoreID.

person mEmENT0m0RI    schedule 01.03.2011