Коротка версія: шукати набагато краще
Менш коротка версія: шукати, як правило, набагато краще, але дуже багато шукає (спричинене поганим дизайном запитів, наприклад, з неприємними співвіднесеними підзапитими, або тому, що ви робите багато запитів під час роботи курсору чи іншого циклу) може бути гірше, ніж сканування, особливо якщо ваш запит може повернути дані з більшості рядків у порушеній таблиці.
Це допомагає охопити всю сім’ю для операцій з пошуку даних, щоб повністю зрозуміти наслідки продуктивності.
Сканування таблиць: Без індексів, що стосуються вашого запиту, планувальник змушений використовувати сканування таблиці, що означає, що кожен рядок переглядається. Це може призвести до того, що кожна сторінка, що стосується даних таблиці, зчитується з диска, що є найгіршим випадком. Зауважте, що для деяких запитів він використовуватиме сканування таблиці навіть тоді, коли є корисний індекс - це, як правило, тому, що даних у таблиці настільки мало, що обмінювати індекси складніше (якщо це так, то можна було б очікувати плануєте змінитись у міру зростання даних, вважаючи, що міра вибірковості індексу є хорошою).
Сканування покажчиків з пошуку рядків: не знайдено індексу, який можна безпосередньо використати для пошуку, але індекс, що містить правильні стовпці, не може бути використаний. Наприклад, якщо у вас є велика таблиця з 20 стовпцями з індексом на колонки1, col2, col3, і ви видаєте SELECT col4 FROM exampletable WHERE col2=616
, у цьому випадку сканування індексу на запит col2
краще, ніж сканування всієї таблиці. Після того, як будуть знайдені відповідні рядки, тоді сторінки даних потрібно прочитати для вибору col4 для виведення (або подальшого приєднання), що є етапом "пошуку закладок", коли ви бачите це в планах запитів.
Сканування покажчиків без пошуку рядків: Якщо вищенаведений приклад, SELECT col1, col2, col3 FROM exampletable WHERE col2=616
тоді додаткові зусилля для читання сторінок даних не потрібні: як col2=616
тільки знайдеться відповідність рядків індексу , всі необхідні дані будуть відомі. Ось чому ви іноді бачите стовпці, за якими ніколи не буде здійснюватися пошук, але, ймовірно, буде потрібно запит на вихід, доданий до кінця індексів - це може зберегти пошук рядків. Додаючи стовпці до індексу лише з цієї причини, додайте їх із INCLUDE
застереженням, щоб сказати двигуну, що не потрібно оптимізувати макет індексу для запитів на основі цих стовпців (це може пришвидшити оновлення цих колонок) . Сканування покажчиків може бути результатом запитів, у яких відсутні фільтрувальні пропозиції: SELECT col2 FROM exampletable
сканувати цей приклад індексу замість сторінок таблиці.
Шукає індекс (із пошуковими рядками рядків або без них) : при пошуку не враховується весь індекс. Для SELECT * FROM exampletable WHERE c1 BETWEEN 1234 AND 4567
запиту двигун запиту може знайти перший рядок, який буде відповідати, виконуючи пошук на основі дерева по індексу, c1
потім він може переміщатись по індексу, поки не дістанеться до кінця діапазону (це те саме, що і з запитом бо c1=1234
може бути багато рядків, що відповідають умові навіть для =
операції). Це означає, що замість кожної сторінки в індексі (або таблиці) потрібно читати лише відповідні сторінки покажчиків (плюс декілька, необхідні для початкового пошуку).
Кластерні індекси: із кластерним індексом дані таблиці зберігаються у вузлах листків цього індексу, а не в окремій структурі купи. Це означає, що після пошуку рядків за допомогою цього індексу ніколи не знадобиться зайвих пошукових рядків, незалежно від того, які стовпці потрібні [якщо ви не маєте сторонніх даних, таких як TEXT
стовпці або VARCHAR(MAX)
стовпці, що містять довгі дані].
З цієї причини у вас може бути лише один кластерний індекс [1] , кластерний індекс - це ваша таблиця, замість того, щоб мати окрему структуру купи, тож якщо ви використовуєте один [2], вибирайте те, куди ви ставите його обережно, щоб отримати максимальний прибуток.
Також зауважте, що кластерний індекс, оскільки "кластерний ключ" для таблиці і входить до кожного некластеризованого індексу в таблиці, тому широкий кластерний індекс, як правило, не є хорошою ідеєю.
[1] Насправді ви можете ефективно мати декілька кластерних індексів, визначивши некластеризовані індекси, які охоплюють або включають кожен стовпець у таблицю, але це, ймовірно, марнотратне місце має вплив на ефективність запису, тому якщо ви вважаєте, що це зробити, переконайтеся вам справді потрібно.
[2] Коли я говорю «якщо ви використовуєте кластерний індекс», не відзначити , що , як правило , рекомендується робити один на кожному столі. Існують винятки, як і у всіх правилах роботи з таблицями, на яких не бачать нічого, крім об'ємних вставок і невпорядкованих зчитувань (можливо, таблиць постановки для процесів ETL), є найпоширенішим прикладом лічильника.
Додатковий пункт: неповні сканування:
Важливо пам’ятати, що залежно від решти запиту сканування таблиці / індексу може насправді не сканувати всю таблицю - якщо логіка дозволяє, щоб план запитів міг спричинити її переривання рано. Найпростіший приклад цього SELECT TOP(1) * FROM HugeTable
- якщо ви подивитесь на план запитів, ви побачите, що зі сканування повернуто лише один рядок, і якщо ви переглянете статистику IO ( SET STATISTICS IO ON; SELECT TOP(1) * FROM HugeTable
), ви побачите, що він читає лише дуже невелике число сторінок (можливо, лише одна).
Те ж може статися, якщо присудок WHERE
або JOIN ... ON
пункт може виконуватися одночасно зі скануванням, яке є джерелом, якщо його дані. Планувач запитів / бігун іноді може бути дуже розумним щодо відштовхування предикатів назад до джерел даних, щоб дозволити таким чином дострокове припинення сканування (а іноді ви можете бути розумними в перестановці запитів, щоб допомогти це зробити!). У той час як дані протікають справа наліво відповідно до стрілок на екрані стандартного плану запитів, логіка працює ліворуч праворуч і кожен крок (справа наліво) не обов'язково виконується до завершення, перш ніж наступний може розпочатися. У наведеному вище простому прикладі, якщо ви дивитесь на кожен блок плану запитів як агент, SELECT
агент запитує у TOP
агента рядок, який у свою чергу запитуєTABLE SCAN
агент для одного, тоді SELECT
агент запитує іншого, але TOP
агент знає, що немає потреби, не турбується навіть запитувати читач таблиці, SELECT
агент отримує відповідь "не більше актуально" і знає, що вся робота виконана. Багато операцій блокують цей вид оптимізації , звичайно , так часто і в більш складних прикладах таблиці / індекс сканування дійсно чи читати кожен рядок, але будьте обережні , щоб не перейти до висновку , що будь-яка перевірка повинна бути дорогою операцією.