Может ли запрос использовать более одного некластеризованного индекса для таблицы?

#sql #sql-server #sql-server-2008 #indexing

#sql #sql-сервер #sql-server-2008 #индексирование

Вопрос:

У меня есть запрос, который выглядит следующим образом

 SELECT TOP 1000 C.iId_company
FROM dbo.Company AS C WITH(NOLOCK)
WHERE 
C.col_1 LIKE 'something%'
OR C.col_1 LIKE 'something2%'
OR C.col_1 LIKE 'something3%'
OR C.col_2 LIKE 'something%'
OR C.col_2 LIKE 'something2%'
OR C.col_2 LIKE 'something3%'
  

Я попытался ускорить этот запрос и попытался добавить индексы для col_1 и col_2. Если я закомментирую условия, касающиеся col_2, запрос выполняется очень быстро, то же самое для col_1 (если я закомментирую условия для col_1). Но когда я оставляю все как есть, это все та же старая история, очень медленная.

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

На самом деле моим единственным решением было бы разделить запрос и использовать ОБЪЕДИНЕНИЕ. Есть ли способ ускорить такой запрос и сохранить его в одном запросе?

Комментарии:

1. Что, если вы попробуете OPTION (FORCESEEK) подсказку? Тогда выполняется объединение индексов?

2. @Martin Не могли бы вы просто создать индекс по обоим столбцам в этом случае?

3. @rsbarro. Если они создадут составной индекс col1,col2 or col2,col1 , то его можно будет просмотреть только для col_1 LIKE 'something%' or col_2 LIKE 'something%' соответственно. Все равно потребуется просканировать весь индекс, чтобы определить другие OR отредактированные критерии.

4. @Martin Я вижу. Я думаю, составной индекс помог бы, если бы они были объединены, верно?

5. @rsbarro Я думаю, вы правы, использование a и приведет к использованию составного индекса, поскольку запрос будет соответствовать составу индекса, а затем выполнять поиск по нему.

Ответ №1:

Используйте объединение. Это:

 SELECT TOP 1000 C.iId_company
FROM dbo.Company AS C WITH(NOLOCK)
WHERE 
C.col_1 LIKE 'something%'
OR C.col_1 LIKE 'something2%'
OR C.col_1 LIKE 'something3%'

union all 

SELECT TOP 1000 C.iId_company
FROM dbo.Company AS C WITH(NOLOCK)
WHERE 
C.col_2 LIKE 'something%'
OR C.col_2 LIKE 'something2%'
OR C.col_2 LIKE 'something3%'
  

Настройте по мере необходимости (например, вам может потребоваться обернуть все это в select, чтобы вы могли добавить предложение order by, чтобы получить все, что вы считаете 1000 лучших). Но я думаю, вы будете довольны этим решением.

Комментарии:

1. Использование ОБЪЕДИНЕНИЯ ALL для присоединения двух результирующих наборов может привести к дублированию строк, поэтому выражению, которое в конечном итоге выбирает из этого объединения, либо потребуется выдать SELECT DISTINCT, либо приведенный выше запрос может использовать ОБЪЕДИНЕНИЕ и выполнять сортировку на этом уровне.

2. Это то, чего я пытался избежать. Я уже смог создать этот запрос. (Последняя строка в моем вопросе)

3. @Fred: ты прав. Я полагаю, что получение 1000 лучших индексов из набора будет немного более эффективным при выполнении distinct при выборе из объединения all, а не при выполнении distinct внутри объединения.

4. @MaxiWheat: Вот как вы заставляете его использовать несколько индексов. Спросите себя, что для вас важнее — производительность или ремонтопригодность.

Ответ №2:

Ваши требования указывают на необходимость двух отдельных индексов, по одному для каждого столбца, который вы ищете. Используйте любой инструмент DBA, который вы предпочитаете, для создания и просмотра плана объяснения запроса. Теперь вы можете начать переработку запроса, чтобы посмотреть, выглядит ли план объяснения лучше, чем ваши предыдущие попытки. Возможно, вам потребуется использовать ОБЪЕДИНЕНИЕ или общее табличное выражение, чтобы объединить два запроса в единый результирующий набор.