2009-08-18 5 views
8

У меня есть таблица с около 1 млн записей (работает SQL Server 2008). У меня есть процедура поиска, которая пытается сопоставить код продукта, а также описание продукта. Однако в некоторых случаях это очень медленно. Ниже (урезанный) заявление SQL:Почему этот оператор SQL очень медленный?

WITH AllProducts AS (
    SELECT  p.*, Row_Number() OVER (ORDER BY ProductId) AS RowNumber 
    FROM  Product AS p 
    WHERE p.IsEnabled=1 AND 
    (
     p.BaseSku = 'KPK-3020QWC-C' -- this on its own is fast 
     OR 
     CONTAINS(p.FreeTextStrings, '"KPK-3020QWC*"') -- and this on its own is fast, but not both 
    ) 
) SELECT * FROM AllProducts   
    WHERE RowNumber BETWEEN 1 AND 20; 

Обратите внимание, что если я просто сравнить на [p.BaseSku = 'КРК-3020QWC-C'] или [СОДЕРЖАЩЕГО (p.FreeTextStrings, «" КРК-3020QWC * "')] индивидуально (но не оба) моментально. И если я сравниваю их вместе, это занимает много времени (несколько минут) и возвращает только одну строку.

IsEnabled и BaseSku индексируются, а FreeTextStrings индексируется FTS.

Я помню, что это работало отлично.

Можно ли пролить свет на это и предложить некоторые решения?

План выполнения Файл доступен здесь: http://wiki.webgear.co.nz/GetFile.aspx?File=Temp%5cSearch%20Test.sqlplan.zip

+0

Можете ли вы показать нам план выполнения с вашего SQL Server? –

+0

Эти проблемы возникли после обновления SQL 2005 до SQL 2008. – Muxa

ответ

10

or, как известно, медленно на SQL Server. Это отягчает, мягко говоря.

разделите его на два запроса с union:

WITH AllProducts AS (
    select *, Row_Number() OVER (ORDER BY ProductId) AS RowNumber 
    from (
    SELECT  p.* 
    FROM  Product AS p 
    WHERE p.IsEnabled=1 AND 
     p.BaseSku = 'KPK-3020QWC-C' 
    UNION 
    SELECT  p.* 
    FROM  Product AS p 
    WHERE p.IsEnabled=1 AND 
     CONTAINS(p.FreeTextStrings, '"KPK-3020QWC*"') 
) 
) SELECT * FROM AllProducts   
    WHERE RowNumber BETWEEN 1 AND 20; 
+1

Точно, «или», скорее всего, вызывает сканирование таблицы в таблице продуктов, даже если есть индексы как для BaseSku, так и для FreeTextStrings ... Объединение превратит это индексировать поиск + индексное сканирование ... (при условии наличия индексов, охватывающих эти два столбца) – KristoferA

+0

Я пробовал это, и он действительно показал большие улучшения.Я попробую эту технику с полным заявлением sql. – Muxa

+0

Да, это решило. Однако я думаю, что довольно странно, что одно и то же заявление очень быстро работало в SQL 2005 и медленно работает в SQL 2008. Может быть, это будет рассмотрено в следующем пакете обновления? – Muxa

1

Это, кажется, работает хорошо:

WITH AllProducts AS (
    SELECT  p.*, Row_Number() OVER (ORDER BY ProductId) AS RowNumber 
    FROM  Product AS p 
    WHERE p.IsEnabled=1 AND 
    (
     CONTAINS(p.BaseSku, 'KPK-3020QWC-C') /* instead of p.BaseSku = 'KPK-3020QWC-C' */ 
     OR 
     CONTAINS(p.FreeTextStrings, '"KPK-3020QWC*"') 
    ) 
) SELECT * FROM AllProducts   
    WHERE RowNumber BETWEEN 1 AND 20; 

(я уже Бесеску FTS-индексируются)

0

Make sure all necessary indexes are in place. I имела ту же проблему с предложением or в одном из моих запросов, и создание столбца NONCLUSTERED INDEX с INCLUDE фиксировало производительность ,

После дальнейшего тестирования, он был включаемые столбцы часть индекса, который на самом деле фиксированной проблемы производительности. Вот что я сделал, чтобы определить проблему и как ее исправить:

Используйте план выполнения, чтобы помочь вам создать недостающие индексы:

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

Но если вы посмотрите над планом выполнения в зеленом тексте, SSMS может сказать вам создать некластеризованный индекс. Хм ... стоит того. Поэтому я создал индекс и проблему решена! Вы можете щелкнуть правой кнопкой мыши запрос «CREATE INDEX» и выбрать «Отсутствующие данные индекса ...». Это откроет новую вкладку с полным запросом для запуска. Просто дайте ему имя.

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