Почему сервер SQL не использует мой индекс? (Фильтрация по объединенным индексированным представлениям) - PullRequest
4 голосов
/ 10 августа 2010

У меня есть два индексированных представления, v_First и v_Second.Эти представления прекрасно работают, когда у меня есть предложение WHERE, которое фильтрует только на основе одного из этих представлений, однако, как только у меня есть критерии фильтрации, основанные на обоих представлениях, я получаю два сканирования кластеризованного индекса и в результате низкую производительность.

Мой запрос:

SELECT * FROM dbo.v_First (NOEXPAND)
JOIN dbo.v_Second (NOEXPAND)
ON dbo.v_First.id = dbo.v_Second.id
WHERE 
dbo.v_First.Firstname = 'JUSTIN'
OR dbo.v_Second.Surname = 'JUSTIN'

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

Почему запрос не выполняется при фильтрации по нескольким индексированным представлениям и что я могу сделать, чтобы исправить это?

(Извините, я не могу опубликовать планы выполнения, они все равно тривиальны- только два сканирования кластеризованного индекса для кластерного индекса двух соответствующих представлений и объединение слиянием)

Обновление:

v_First столбцы:

  • ID (bigint, кластеризованный индекс)
  • FirstName (varchar (254), некластеризованный индекс)

v_Second столбцы:

  • ID (bigint, кластерный индекс)
  • Фамилия (varchar (254), некластеризованный индекс)

Все индексы содержат только один столбец.

Обновление, второй:

Я обнаружил, что если предложение OR изменяется на предложение AND, запрос выполняется нормально.Я также обнаружил, что если я изменю запрос на использование оператора UNION вместо OR, запрос будет выполняться нормально:

SELECT * FROM dbo.v_First (NOEXPAND)
JOIN dbo.v_Second (NOEXPAND)
ON dbo.v_First.ID = dbo.v_Second.ID
WHERE dbo.v_First.Firstname = 'JUSTIN'
UNION SELECT * FROM dbo.v_First (NOEXPAND)
JOIN dbo.v_Second (NOEXPAND)
ON dbo.v_First.ID = dbo.v_Second.ID
WHERE dbo.v_Second.Surname = 'JUSTIN'

Насколько я знаю, эти два запроса должны бытьэквивалентно?

Наконец, я также обнаружил, что использование подзапроса вместо этого также имеет странный эффект, следующий запрос выполняется нормально:

SELECT * FROM dbo.v_First (NOEXPAND)
-- JOIN dbo.v_Second (NOEXPAND)
-- ON dbo.v_First.ID = dbo.v_Second.ID
WHERE dbo.v_First.ID IN
(
      SELECT ID FROM dbo.v_Second (NOEXPAND)
      WHERE dbo.v_Second.Surname = 'JUSTIN'
)
OR dbo.v_First.Firstname = 'JUSTIN'

Однако, если я раскомментирую JOIN (так что я могу получить столбцы из второй таблицы в результатах моего запроса), затем я получаю сканирование таблицы по кластерному индексу v_Second (обратите внимание, однако, что это все же лучше, чем в исходном запросе, поскольку он включает только 1 сканирование вместо 2).).

Я очень смущен - что происходит?Кажется, что я могу обойти эти проблемы, «рефакторинг» моего запроса, однако я обеспокоен тем, что не понимаю, что здесь происходит - я бы предпочел избегать внесения изменений, которые я не до конца понимаю.

1 Ответ

3 голосов
/ 10 августа 2010

Наблюдения

  • У вас есть условие "ИЛИ", которое не соответствует SARGable

  • Возможно, вам понадобится добавить идентификатор в каждый индекс, чтобы ондоступны без сканирования / поиска

  • Я бы все равно хотел увидеть планы.Используйте SET SHOWPLAN_TEXT

Чтобы ответить на ваш вопрос, я бы, вероятно, имел один индекс в базовой таблице с обоими текстовыми столбцами и позволил бы его сканировать.ИЛИ не дает вам много вариантов, и 2 индексированных просмотра бессмысленно ИМХО.После вашего обновления у вас есть несколько надуманных конструкций SQL: вам действительно нужны 2 индексированных представления и необычная производная таблица или UNION?

...