Жесткое и быстрое правило для включения столбцов в индекс

38

Существует ли какое-либо жесткое и быстрое правило для определения того, какие столбцы и в каком порядке следует размещать в Включенном в некластерный индекс. Я только что прочитал этот пост https://stackoverflow.com/questions/1307990/why-use-the-include-clause-when-creating-an-index, и я нашел это для следующего запроса:

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

Постер предложил сделать указатель так:

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

здесь возникает мой вопрос, почему мы не можем сделать индекс, как это

CREATE NONCLUSTERED INDEX NC_EmpDep 
      ON Employee( EmployeeID, DepartmentID, LastName)

или

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

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

Сообщество
источник
3
INCLUDE, как правило, должен содержать поля, которые вам понадобятся ПОСЛЕ того, как будет найдена запись, сохраняя вас в обратном направлении, чтобы получить больше данных. Порядок полей в INCLUDE не важен.
Джимбо
Ryk, лично я считаю этот пост полезным.
Джейсон Янг
Я считаю этот вопрос полезным. Давайте сосредоточимся на хороших вопросах и хороших ответах вместо того, чтобы преследовать людей ...
Volvox

Ответы:

47

Это предложение индекса от marc_s неверно. Я добавил комментарий. (И это был мой ответ тоже принят!)

Индекс для этого запроса будет

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

Индекс обычно

CREATE INDEX <name> ON <table> (KeyColList) INCLUDE (NonKeyColList)

Где:

  • KeyColList = Ключевые столбцы = используется для ограничения строк и обработки
    WHERE, JOIN, ORDER BY, GROUP BY и т. Д.
  • NonKeyColList = неключевые столбцы = используется в SELECT и агрегации (например, SUM (столбец)) после выбора / ограничения
ГБН
источник
+1 - я согласен (см. Мой ответ), что примеры индексов в OP бесполезны для запроса!
JNK
Большой! Еще одна вещь, которая будет определять порядок KeyColList и NonKeyColList. Можете ли вы просто объяснить с моим примером? Предположим, теперь мой запрос: SELECT EmployeeID, DepartmentID, LastName FROM EmployeeWHERE DepartmentID = 5, StateID = 4 Каким должен быть индекс сейчас?
@Rocky - NonKeyColListпорядок не имеет значения. KeyColListпорядок должен быть в порядке частоты, которую вы ожидаете использовать в запросах. Смотрите мои заметки на мой ответ ниже, но это как Last Name, First Name, Middile Initialв телефонной книге. Вам нужно первое поле, чтобы найти второе поле.
JNK
@ gbn Действительно ли нам требуется EmployeeID в списке включения? Как если бы у нас был кластеризованный индекс в столбце EmployeeID, и поверх этого, если мы создаем некластеризованный индекс для столбца DeptId, таким образом, у некластеризованного индекса уже есть ссылка на ключ кластеризации, который включен в структуру некластеризованного индекса, в том числе ключ кластеризации в списке INCLUDE не делает ' не добавляет никаких преимуществ.
Вишванатан Айер
1
@ViswanathanIyer, хотя он и не будет добавлен дважды к реальному хранилищу на диске: SQL Server обнаруживает это. Так что это не нужно, но это проясняет ситуацию. Тем не менее, мы не знаем ни одного кластеризованного индекса в этом вопросе, поэтому безопаснее предположить ни одного.
17
19

JNK и gbn дали отличные ответы, но также стоит рассмотреть общую картину - не просто сосредоточиться на одном запросе. Хотя этот конкретный запрос может выиграть от индекса (# 1):

Employee(DepartmentID) INCLUDE (Lastname, EmployeeID)

Этот индекс совсем не помогает, если запрос немного меняется, например:

SELECT EmployeeID, DepartmentID, LastName
FROM Employee
WHERE DepartmentID = 5 AND LastName = 'Smith'

Для этого нужен индекс (# 2):

Employee(DepartmentID, LastName) INCLUDE (EmployeeID)

Представьте, что в Отделе 5 было 1000 сотрудников. Используя индекс № 1, чтобы найти всех Смитов, вам нужно будет просмотреть все 1000 строк в Отделе 5, поскольку включенные столбцы не являются частью ключа. Используя индекс № 2, вы можете обратиться непосредственно к Отделу 5, LastName Smith.

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


В качестве примечания стоит отметить, что, если EmployeeID был ключом кластеризации для этой таблицы - при условии кластеризованного индекса - тогда вам не нужно включать EmployeeID - он присутствует во всех некластеризованных индексах, то есть индекс # 2 может просто быть

Employee(DepartmentID, LastName)

источник
2
+1 для более полезной информации. Для вашего последнего замечания я проверил это, и явное использование EmployeeID в INCLUDE фактически игнорируется (в зависимости от размера индекса), если EmployeeID является кластеризованным индексом. Это более очевидно, хотя я думаю, и нет недостатка в космосе.
ГБН
1
Я абсолютно согласен - всегда лучше быть явным, особенно если это ничего не стоит!
1
На всякий случай ... Я имею в виду, что я протестировал кластерный ключ в INCLUDE (явно не EmployeeID), и он не добавляет места. В ключевых столбцах это так.
ГБН
@gbn Да, ключ кластера должен находиться только на уровне листа индекса, где находятся столбцы INCLUDE. Перемещение его в ключ индекса будет означать, что оно будет существовать и на страницах без листа. Это приведет к небольшому раздутию, но не к ужасному количеству (на страницах промежуточного уровня вы добавите еще 4 байта на страницу конечного уровня, предполагая целое число).
Это отличный ответ, который включает некоторые эффекты, описанные в этой статье: sqlperformance.com/2014/07/sql-indexes/… Если ваш запрос изменяется, то изменяются и требования ваших индексов. Возможно, вам будет лучше с ответом Джима, но вы можете лучше ответить @gbn.
Джон aka hot2use
7

Я не уверен, как ты получил этот первый. Для этого запроса я бы использовал:

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

В SQL практически нет «жесткого и быстрого правила».

Но, для вашего примера, единственное поле, которое будет использовать индекс, это то, DepartmentIDчто оно находится в WHEREпредложении.

Остальные поля просто должны быть легко доступны оттуда. DepartmentIDЗатем вы выбираете на основе INCLUDEэтих полей в листовом узле индекса.

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

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

Эти INCLUDEполя, как номер телефона, адрес и т.д. другая информация для каждой записи в книге.

РЕДАКТИРОВАТЬ:

Чтобы уточнить, почему не использовать:

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

Этот показатель является только полезным , если у вас есть либо EmployeeIDили ОБА EmployeeID и LastNameв вашем WHEREпредложении. Это в значительной степени противоположность того, что вам нужно для этого запроса.

JNK
источник
@ajbeaven, это правда, поэтому комментарий, который я вставил в правку, говорит, что вам нужен EITHER employeeID или оба столбца.
JNK
durr извините, неправильно прочитал :(
ajbeaven
0

Я думаю, что вы все еще могли бы использовать индекс (employee_id, Department_id), но вам нужно было бы включить строку «фиктивный» в фразу where, например: employee_id = employee_id)

  • имеющий индекс на (employee_id, departemnent_id),
  • необходимость искать / ограничивать только по отделу
  • зная, что он не будет использовать индекс с неправильного порядка (или теперь все изменилось, и следующий «трюк» больше не нужен. Я «старый»?) .
  • Использовать «старый» трюк?

    выберите * из emp сотрудника,
    где emp.employee_id = emp.employee_id
    и emp.department_id = 5

(Таким образом, я не сосредотачиваюсь на включаемой части фамилии, а на да / или не используется ключ.)

С уважением,

Miguell

Мигель Леув
источник
2
Нет, это бесполезно и не эффективно.
ypercubeᵀᴹ
В частности, он все равно должен будет выполнить сканирование индекса, чтобы найти каждый идентификатор сотрудника, чтобы найти все экземпляры отдела_д 5. Если есть 1000 сотрудников и 5 отделов, SQL должен просмотреть все 1000 сотрудников, чтобы найти все строки для определенного отдела.
Марк Совул
Теперь рассмотрим противоположный случай (индекс для отдела_ид, сотрудника_ид). Очевидно, что сейчас легко найти конкретный отдел, но имейте в виду, что для поиска конкретного сотрудника SQL нужно всего лишь просканировать 5 отделов, чтобы найти все строки для конкретного сотрудника.
Марк Совул