Дуже добре запитання, оскільки це така важлива концепція. Хоча це велика тема, і те, що я хочу вам показати, - це спрощення, щоб ви могли зрозуміти базові поняття.
По-перше, коли ви бачите кластерну таблицю роздумів індексу . На сервері SQL, якщо таблиця не містить кластерного індексу, це купа. Створення кластерного індексу на столі фактично перетворює таблицю в структуру типу b-tree. Ваш кластерний індекс - це ваша таблиця, вона не відокремлена від таблиці
Ніколи не замислювалися, чому у вас може бути лише один кластерний індекс? Добре, якби у нас було два кластерні індекси, нам знадобилися б дві копії таблиці. Зрештою, він містить дані.
Я спробую пояснити це на простому прикладі.
ПРИМІТКА: Я створив таблицю в цьому прикладі і заповнив її понад 3 мільйонами випадкових записів. Потім запустили фактичні запити та вставили сюди плани виконання.
Що вам дійсно потрібно зрозуміти, це O-позначення або операційна ефективність . Припустимо, у вас є наступна таблиця.
CREATE TABLE [dbo].[Customer](
[CustomerID] [int] IDENTITY(1,1) NOT NULL,
[CustomerName] [varchar](100) NOT NULL,
[CustomerSurname] [varchar](100) NOT NULL,
CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED
(
[CustomerID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF
, IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS = ON
, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Отже, у нас є основна таблиця з кластерним ключем на CustomerID (Первинний ключ кластеризований за замовчуванням). Таким чином таблиця розташовується / упорядковується на основі первинного ключа CustomerID. Проміжні рівні містять значення CustomerID. Сторінки даних будуть містити весь рядок, таким чином, це рядок таблиці.
Ми також створимо некластеризований індекс у полі CustomerName. Наступний код зробить це.
CREATE NONCLUSTERED INDEX [ix_Customer_CustomerName] ON [dbo].[Customer]
(
[CustomerName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF
, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF
, DROP_EXISTING = OFF, ONLINE = OFF
, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
Отже, в цьому індексі ви знайдете на сторінках даних / вузлах рівня листів вказівник на проміжні рівні в кластерному індексі. Індекс розташовується / упорядковується навколо поля CustomerName. Таким чином, проміжний рівень містить значення CustomerName, а рівень аркуша міститиме вказівник (ці значення вказівника фактично є значеннями первинного ключа або стовпцем CustomerID).
Так, якщо ми виконаємо такий запит:
SELECT * FROM Customer WHERE CustomerID = 1
SQL, ймовірно, прочитає кластерний індекс через операцію пошуку. Операція пошуку - це двійковий пошук, який набагато ефективніше, ніж сканування, який є послідовним пошуком. Отже, у нашому вище прикладі індекс зчитується і за допомогою двійкового пошуку SQL може усунути дані, які не відповідають критеріям, який ми шукаємо. Дивіться доданий знімок екрана для плану запитів.
Отже, кількість операцій або O-позначення для операції пошуку є такою:
- Здійснюйте двійковий пошук по кластерному індексу, порівнюючи шукане значення зі значеннями на проміжному рівні.
- Поверніть значення, які відповідають (пам’ятайте, оскільки кластерний індекс містить усі дані, він може повернути всі стовпці з індексу, оскільки це дані рядків)
Отже, це дві операції. Однак якщо ми виконали такий запит:
SELECT * FROM Customer WHERE CustomerName ='John'
Тепер SQL використовуватиме некластеризований індекс у CustomerName для пошуку. Однак, оскільки це некластеризований індекс, він не містить усіх даних у рядку.
Таким чином, SQL здійснить пошук на проміжних рівнях, щоб знайти записи, які відповідають, потім зробить пошук, використовуючи значення, повернуті, щоб здійснити інший пошук в кластерному індексі (він же таблиця) для отримання фактичних даних. Це звучить заплутано, я знаю, але читаю далі, і все стане зрозумілим.
Оскільки наш некластеризований індекс містить лише поле CustomerName (значення індексованого поля, що зберігаються у проміжних вузлах) та вказівник на дані, які є CustomerID, індекс не має запису імені CustomerSurname. Ім'я клієнта має бути отримано з кластерного індексу або таблиці.
Під час запуску цього запиту я отримую такий план виконання:
На екрані, знятому вище, ви можете помітити дві важливі речі
- SQL говорить, що у мене відсутній індекс (текст зеленим кольором). SQL пропонує мені створити індекс на CustomerName, який включає CustomerID та CustomerSurname.
- Ви також побачите, що 99% часу запиту витрачається на пошук ключа на індекс первинного ключа / кластерний індекс.
Чому SQL знову пропонує індекс на CustomerName? Отже, оскільки індекс містить лише CustomerID, а SQL CustomerName все ще повинен знайти ім'я CustomerSurname з таблиці / кластерних індексів.
Якби ми створили індекс і включили стовпець CustomerSurname до індексу SQL, можна було б задовольнити весь запит, просто прочитавши некластеризований індекс. Ось чому SQL пропонує мені змінити некластеризований індекс.
Тут ви можете побачити додаткову операцію, яку повинен виконати SQL, щоб отримати стовпець CustomerSurname з кластерного ключа
Таким чином, кількість операцій така:
- Здійснюйте двійковий пошук по некластеризованому індексу, порівнюючи шукане значення зі значеннями на проміжному рівні
- Для вузлів, які відповідають прочитаному вузлу рівня аркуша, який буде містити вказівник для даних кластеризованого індексу (вузли рівня листів, до речі, містять значення первинного ключа).
- Для кожного повернутого значення зробимо читання на кластерному індексі (таблиці), щоб отримати значення рядків тут, ми прочитали б ім'я клієнта.
- Повернути відповідні рядки
Це 4 операції з виведення значень. Удвічі більша кількість необхідних операцій порівняно з читанням кластерного індексу. Покажіть вам, що ваш кластерний індекс - ваш найпотужніший індекс, оскільки він містить усі дані.
Тому просто для уточнення останнього моменту. Чому я кажу, що вказівник у некластеризованому індексі є значенням основного ключа? Добре, щоб продемонструвати, що вузли рівня листів некластеризованого індексу містять значення первинного ключа, я змінюю свій запит на:
SELECT CustomerID
FROM Customer
WHERE CustomerName='Jane'
У цьому запиті SQL може прочитати CustomerID з некластеризованого індексу. Тут не потрібно робити пошук на кластерному індексі. Це ви можете бачити за планом виконання, який виглядає приблизно так.
Зауважте різницю між цим запитом та попереднім запитом. Немає пошуку. SQL може знайти всі дані в некластерному індексі
Сподіваємось, ви можете почати розуміти, що кластерний індекс - це таблиця, а некластеризовані індекси НЕ містять усіх даних. Індексація прискорить вибір через те, що бінарний пошук можна здійснювати, але лише кластерні індекси містять усі дані. Таким чином, пошук по некластеризованому індексу майже завжди призведе до завантаження значень із кластерного індексу. Ці додаткові операції роблять некластеризовані індекси менш ефективними, ніж кластерний індекс.
Сподіваюсь, це все прояснить. Якщо нічого не має сенсу, будь ласка, опублікуйте коментар, і я спробую уточнити. Тут досить пізно, і мій мозок відчуває плаксивість. Час для червоного бика.