Почему несколько условий WHERE замедляют запрос, а не ускоряют его? - PullRequest
16 голосов
/ 29 января 2010

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

Теперь запрос.

Select Count(*)
From 
    SearchTable 
Where 
    [Date] >= '8/1/2009' 
    AND 
    [Zip] In (Select ZipCode from dbo.ZipCodesForRadius('30348', 150))
    AND 
    FreeText([Description], 'keyword list here')  

Первое условие говорит само за себя. Второй использует UDF для получения списка почтовых индексов в пределах 150 миль от 30348. Третий использует полнотекстовый индекс для поиска предоставленных слов.

Только с этим условием

[Date] >= '8/1/2009' 

Запрос возвращает 43884 (размер таблицы чуть меньше 500 тыс. Строк) за 3 секунды .

Использование только этого условия

[Zip] In (Select ZipCode from dbo.ZipCodesForRadius('30348', 150))

Я получаю 27920, также возвращается через 3 секунды .

И только с полнотекстовой частью

FreeText([Description], 'keyword list here')

68404 возвращается через 8 секунд .

Когда я использую только почтовый индекс и полнотекстовые условия, я получаю 4919 за 4 секунды .
Только дата и условия полного текста дают мне 9481 всего за 14 секунд .
Использование даты и условий почтового индекса дает мне только 3238 в 14 секунд .
При всех трех условиях запрос возвращает 723 за 2 минуты, 53 секунды . (wtfbbq)

Ответы [ 8 ]

17 голосов
/ 29 января 2010

Единственный способ узнать, почему стоит проверить план выполнения. Попробуйте SET SHOWPLAN_TEXT ON .

10 голосов
/ 29 января 2010

Получить план выполнения

Вам нужно посмотреть на план выполнения, чтобы иметь любую надежду , чтобы понять реальную причину изменения времени отклика. В частности, в этом случае следует учитывать несколько факторов:

  • Возможно, что некоторые запросы, возвращающие больше строк, выполняются быстрее, потому что они выполняют сканирование таблиц - у каждого есть детализация "сканирования таблицы медленно", но в зависимости от распределения данных сканирование таблицы может быть быстрее. чем 50000 строк поиска. Его просто невозможно определить без проверки выполнения.
  • Также возможно, что неправильная статистика не позволяет SQL-серверу точно прогнозировать количество строк, которые он ожидает вернуть - если SQL-сервер ожидает 20 строк, но их действительно 20 000, то в более сложных запросах он, вероятно, в конечном итоге будет делать что-то в неправильном порядке, приводящем к очень медленному запросу - опять же, это просто невозможно определить без плана выполнения.
  • В частности, использование Freetext означает, что используется механизм полнотекстового поиска, что может вызвать дополнительные проблемы с SQL-сервером при прогнозировании числа возвращаемых строк.

Действительно, получите план выполнения.

Обновление:

Возможные причины

В отсутствие плана выполнения, я думаю, что наиболее вероятной причиной медленного выполнения являются плохие оценки условий на ZipCode и Description:

  • Трудно оценить количество совпадений при условии ZipCode, поскольку его результат зависит от хранимой процедуры.
  • Трудно оценить количество совпадений в условии FreeText, поскольку оно основано на результатах механизма полнотекстовых запросов.

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

Для очень сложного запроса я видел, как SQL-сервер выполнял ~ 3 000 000 запросов, пытаясь вернуть одну строку - в таблице даже не было 3 000 000 строк!

Что нужно попробовать - поместите ZipCodeForRadius во временную таблицу.

Если я прав, то, чтобы помочь с первым, вы можете попытаться поместить результаты хранимой процедуры ZipCodesForRadius во временную таблицу, я должен признать, что у меня нет хорошего объяснения, почему это поможет , но у меня есть несколько теорий о том, почему может помочь:

  • Лучшая статистика по временной таблице
  • Побочный эффект будет вызывать перекомпиляцию основного оператора SELECT каждый раз, когда вы запускаете запрос (если диапазон почтовых индексов не очень мал) - в любом случае процесс занимает несколько секунд, это будет Хорошо, если есть большие различия в соответствующих почтовых индексах. Если нет, то есть способы предотвратить перекомпиляцию.

Это, безусловно, не должно наносить слишком много урона в любом случае.

2 голосов
/ 29 января 2010

Если у вас есть одно условие для count (), тогда запрос может отсканировать самый узкий индекс, охватывающий счет. Даже если выполняется полное сканирование, количество прочитанных страниц намного меньше, чем при сканировании кластерного индекса, что, вероятно, намного шире. Если у вас несколько условий, строки-кандидаты должны быть объединены, и план запроса может отказаться от сканирования некластеризованного индекса (или сканирования диапазона) и перейти к полному сканированию таблицы.

В вашем случае, скорее всего, произойдет:

  • [Date] >= '8/1/2009' удовлетворяется индексом, который содержит Date, скорее всего, индексом ON Date, поэтому это сканирование с быстрым диапазоном
  • [Zip] In (Select ZipCode from dbo.ZipCodesForRadius('30348', 150)) такой же, как Дата. Даже если у вас нет индекса на Zip, у вас, вероятно, есть индекс, содержащий Zip.
  • FreeText([Description], 'keyword list here') полнотекстовый поиск для счета, который выполняется по внутренним индексам FT, быстро.

  • Все три условия. Теперь это становится грязным. Если у вас достаточно ОЗУ, запрос может сначала составить план для поиска FT, затем HASH-JOIN, затем Zip scan, а затем HASH-JOIN the Date. Это было бы быстро, порядка 3 + 3 + 8 секунд + изменение (для операции хеширования). Но если у вас недостаточно ОЗУ или если оптимизатору не нравится выполнять хеш-соединение, ему придется выполнить поиск FT, затем поиск по вложенному циклу по Zip, а затем поиск по вложенному циклу по коду, и это может привести к Индекс переломного момента в его решениях. Так что, скорее всего, вы получите сканирование таблицы. Это, конечно, предположение с моей стороны, но в конце концов вы опубликовали только текст T-SQL и ноль информации о структуре ваших кластеризованных и некластеризованных индексов.

В конце вы должны помнить, что SQL не является вашим C-подобным процедурным языком. Когда речь идет о производительности в SQL, речь никогда не идет о сравнениях и логической логике. Это всегда о доступе к данным и количестве прочитанных страниц. Таким образом, даже если каждое отдельное условие может быть удовлетворено небольшим быстрым сканированием диапазона индексов узкого некластеризованного индекса или индекса FT, комбинация не может (или в его случае Оптимизатор запросов не нашел пути к этому).

2 голосов
/ 29 января 2010

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

Также вы можете использовать кнопку «Включить фактический план выполнения» в Management Studio, чтобы показать, как происходит определение того, какие записи вы получаете.

UPDATE

Судя по одному из ваших комментариев, этот запрос извлекается из временной таблицы. В этом случае после создания таблицы к ней применяются индексы. Добавление индексов с последующим выполнением запросов будет быстрее, чем сканирование таблицы для временной таблицы строк 500 КБ.

2 голосов
/ 29 января 2010
  • Строковые операции, такие как FreeText, дороги
  • Функция ZipCodesForRadius может быть слишком дорогой, в зависимости от того, как она закодирована, и от того, присутствуют ли необходимые индексы или нет

Если упорядочение предложений WHERE не ускоряет процесс, использование выбора вокруг вашего выбора может сработать (в некоторых случаях он ускорил работу с DB2 / 400, не уверен насчет того, как оптимизируется SqlServer):

Select Count(*)
From
(
    Select [Description]
    From 
        SearchTable 
    Where 
        [Date] >= '8/1/2009' 
        AND 
        [Zip] In (Select ZipCode from dbo.ZipCodesForRadius('30348', 150))

) as t1
Where FreeText([Description], 'keyword list here')  
2 голосов
/ 29 января 2010

Потому что больше условий для проверки - больше работы для ядра базы данных. Мне кажется логичным.

Если бы у вас было одно условие для поля кластерного индекса, эта особая проверка не сильно замедлила бы операцию. Рассматривали ли вы реорганизацию индексов в соответствии с вашим запросом?

0 голосов
/ 29 января 2010

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

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

Select Count(*)
From (
    Select 1
    From SearchTable 
    Where [Zip] In (Select ZipCode from dbo.ZipCodesForRadius('30348', 150))
) 
Where [Date] >= '8/1/2009' 
    AND FreeText([Description], 'keyword list here')  
0 голосов
/ 29 января 2010

Мудрый способ передачи данных, вы правы в своем мышлении: меньше данных, быстрее время выполнения. Однако обычно это время минимально, и большая часть времени уходит на фактическую обработку запросов.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...