Короткая версия: искать гораздо лучше
Менее короткая версия: поиск, как правило, намного лучше, но большое количество запросов (вызванных, например, неправильным дизайном запросов с неприятными коррелированными подзапросами или выполнением большого количества запросов в операции курсора или в другом цикле) может быть хуже сканировать, особенно если ваш запрос может в конечном итоге вернуть данные из большинства строк в затронутой таблице.
Это помогает охватить всю семью операциями по поиску данных, чтобы полностью понять последствия для производительности.
Сканирование таблиц. При отсутствии индексов, относящихся к вашему запросу, планировщик вынужден использовать сканирование таблицы, что означает просмотр каждой строки. Это может привести к тому, что каждая страница, относящаяся к данным таблицы, будет прочитана с диска, что часто является наихудшим случаем. Обратите внимание, что для некоторых запросов он будет использовать сканирование таблицы даже при наличии полезного индекса - обычно это происходит из-за того, что данные в таблице настолько малы, что обходить индексы затруднительно (если это так, вы можете ожидать планируйте изменения по мере роста данных, предполагая, что показатель избирательности индекса хороший).
Сканирование по индексу с поиском строк: если не найден индекс, который можно напрямую использовать для поиска, но имеется индекс, содержащий правильные столбцы, можно использовать сканирование по индексу. Например, если у вас есть большая таблица с 20 столбцами с индексом column1, 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
- если вы посмотрите на план запроса для этого, вы увидите, что только одна строка была возвращена из сканирования, и если вы посмотрите статистику ввода-вывода ( SET STATISTICS IO ON; SELECT TOP(1) * FROM HugeTable
), вы увидите, что она читает только очень маленькое число страниц (возможно, только одна).
То же самое может произойти, если предикат предложения WHERE
или JOIN ... ON
может быть запущен одновременно со сканированием, которое является источником его данных. Планировщик / исполнитель запросов иногда может быть очень умным в отношении толкания предикатов обратно к источникам данных, чтобы таким образом досрочно завершить сканирование (и иногда вы можете быть умными в перестановке запросов, чтобы помочь в этом!). В то время как данные передаются справа налево в соответствии со стрелками на стандартном отображении плана запроса, логика выполняется слева направо, и каждый шаг (справа налево) не обязательно выполняется до завершения следующего запуска. В приведенном выше простом примере, если вы посмотрите на каждый блок в плане запроса в качестве агента, SELECT
агент запрашивает у TOP
оператора строку, которая, в свою очередь, запрашиваетTABLE SCAN
агент для одного, затем SELECT
агент запрашивает другого, но TOP
агент знает, что в этом нет необходимости, даже не спрашивает читателя таблицы, SELECT
агент получает ответ «больше не актуально» и знает, что вся работа выполнена. Многие операции блокируют этот вид оптимизации , конечно , так часто и в более сложных примерах таблицы / индекс сканирование действительно ли читать каждую строку, но будьте осторожны , чтобы не перейти к выводу , что любая проверка должна быть дорогостоящей операцией.