11

У меня есть сохраненный proc, который ищет продукты (250 000 строк), используя полный текстовый индекс.Почему характеристики этих двух запросов настолько разные?

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

-- This is normally a parameter of my stored proc 
DECLARE @Filter VARCHAR(100) 
SET @Filter = 'FORMSOF(INFLECTIONAL, robe)' 

-- #1 - Runs < 1 sec 
SELECT TOP 100 ID FROM dbo.Products 
WHERE CONTAINS(Name, @Filter) 

-- #2 - Runs in 18 secs 
SELECT TOP 100 ID FROM dbo.Products 
WHERE @Filter IS NULL OR CONTAINS(Name, @Filter) 

Вот планы выполнения:

Запрос # 1 Execution plant #1

Запрос # 2 Execution plant #2

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

Я также не совсем понимаю, почему используется индекс с именем IX_SectionID, поскольку он является индексом, который содержит только столбец SectionID и этот столбец нигде не используется.

ответ

8

OR может раздавить производительность, так что сделать это таким образом:

DECLARE @Filter VARCHAR(100) 
SET @Filter = 'FORMSOF(INFLECTIONAL, robe)' 

IF @Filter IS NOT NULL 
BEGIN 
    SELECT TOP 100 ID FROM dbo.Products 
    WHERE CONTAINS(Name, @Filter) 
END 
ELSE 
BEGIN 
    SELECT TOP 100 ID FROM dbo.Products 
END 

Посмотрите на эту статью: Dynamic Search Conditions in T-SQL by Erland Sommarskog и этот вопрос: SQL Server 2008 - Conditional Query.

+0

Nice статьи - добавление 'ВАРИАНТ (RECOMPILE)' фактически решает проблему производительности на 2 запроса (однако другая проблема в том, что 'СОДЕРЖИТ()' прибавки ошибка, когда параметр равен NULL, но это еще одна проблема). –

1

Вы указали условие ИЛИ. В большинстве случаев гораздо проще проверить явно для NULL и выполнить один запрос против вашего метода.

Например, попробуйте следующее:

IF @Filter IS NULL 
BEGIN 
SELECT TOP 100 ID FROM dbo.Products 
END 
ELSE 
BEGIN 
SELECT TOP 100 ID FROM dbo.Products 
WHERE @Filter CONTAINS(Name, @Filter) 
END 
3

Первый план запроса выглядит просто:

  1. полнотекстовый поиск для решения CONTAINS(Name, @Filter)
  2. индекс сканирования, чтобы посмотреть другие столбцы согласованные ряды
  3. объединить два с использованием хеш-соединения

concatenation operator образует объединение двух наборов записей. Так это выглядит как второй запрос делает:

  1. индекса сканирование (впоследствии используется для просмотра других столбцов)
  2. постоянного сканирования. Я предполагаю, что он обрабатывает ваш запрос как не параметризованный, поэтому план запроса не должен работать ни для какого другого значения @Filter. Если правильно, постоянное сканирование разрешает @Filter is not null.
  3. полнотекстовый поиск для решения CONTAINS(Name, @Filter)
  4. профсоюзов результата 3 с пустым набором из 2
  5. петли присоединяется результат 1 и 4, чтобы посмотреть другие столбцы

с помощью хеширования торгует памятью для скорости; если в вашей системе достаточно памяти, это намного быстрее, чем объединение цикла. Это может легко объяснить 10-100-кратное замедление.

Одно исправление заключается в использовании двух отдельных запросов:

if @Filter is null 
    SELECT TOP 100 ID FROM dbo.Products 
else 
    SELECT TOP 100 ID FROM dbo.Products WHERE CONTAINS(Name, @Filter) 
+0

Интересно - есть ли способ заставить хэш-соединение для второго запроса? –

+0

Да, вы могли бы переписать его с помощью 'inner hash join', но это было бы довольно сложно. Было бы лучше использовать одно из решений из статьи Соммарскога. – Andomar

Смежные вопросы