Каков наилучший способ динамического создания предложения where? - PullRequest
3 голосов
/ 14 июля 2011

Я пишу хранимую процедуру, которая работает с тяжелым оператором select. Хранимая процедура принимает в качестве фильтра около 15 параметров, все из которых имеют значение NULL.

Есть две вещи, которые обычно делают параметры - проверить, находится ли x между высоким и низким, или проверить, находится ли значение столбца в y.

Моя главная задача - как написать предложение where.

Пример: динамический SQL общеизвестно медленен, поэтому я не хочу писать предложение where, а затем передавать его в exec.

Я не хочу делать if High = null then High = max, потому что тогда у меня все еще будет оператор промежуточного уровня, который потребляет вычислительную мощность и не имеет смысла.

Я не хочу делать (if High = null or X <= High), потому что проверка нуля будет по-прежнему обрабатываться для каждой строки, и я слышал слухи, которые будут портить индексы.

Короче говоря, я ищу руководство по наилучшей практике, которое учитывает производительность.

Ответы [ 3 ]

7 голосов
/ 14 июля 2011

Динамический SQL раньше был медленным, потому что планы выполнения для динамически генерируемого SQL не кэшировались.Это уже не тот случай, и планы выполнения для динамических запросов SQL будут кэшироваться, если текст запроса идентичен.Это означает, что вы должны:

  • Использовать параметры, чтобы избежать изменения значений параметров в тексте запроса
  • Попробуйте отсортировать предложения where так, чтобы они отображались в том же порядке

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


Вашдругих предложений (установка различных параметров в NULL) следует избегать, и на самом деле они могут работать довольно плохо - операторы могут иметь только один кэшированный план запроса, однако план запроса оптимальный будет зависеть от параметров и может быть оченьотличается в зависимости от предоставленных значений.

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

В конечном итоге это будет означать, что:

  • Большинствовремя, когда выбранный план запроса, вероятно, не будет оптимальным, или
  • . Вы можете заставить SQL Server генерировать новый план для каждого выполнения, что всегда приводит к разумному плану выполнения, однако исключает преимущества кэширования плана

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

По этим причинам я 'Я бы сказал, что динамический SQL, безусловно, лучший выбор.

3 голосов
/ 14 июля 2011

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

Комментарий ниже: не будет так много (да, потенциально 2 ^ 15, но на практике гораздо меньше, возможно, чаще всего используются 2 ^ 4) комбинаций существующих и отсутствующих параметров;они могут быть кэшированы.Если фактические значения параметров включены в предложение WHERE (которое я видел выполненным), каждый запрос уникален и не будет кэшироваться.

1 голос
/ 14 июля 2011

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

declare @myParameter int

select *
from dbo.foo t
where t.someColumn = coalesce( @myParameter , t.someColumn )

Оптимизатор все еще может использовать индекс для t.someColumn, и вы избегаетеOR оператор (это то, что обычно сбивает с толку использование индекса.

Это одна вещь, на которую нужно смотреть.

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

  • Если 1-й выполненный запрос выполняется с тем, что вы могли быЕсли описать как «нестандартные» параметры, то кэшированный план выполнения, вероятно, будет работать плохо для более обычных случаев.

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

  • список можно продолжить ...

Что я в итоге делал, должен был инструмент хранимой процедурылог, как это называлось.После того, как у меня были некоторые исходные данные, небольшой анализ показал мне 4 или 5 наиболее распространенных способов их использования.

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

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

Кроме того, помните о перекомпиляции хранимых процедур.В загруженной системе перекомпиляция может отрицательно повлиять на производительность.Если хранимая процедура перекомпилируется один или несколько раз при каждом вызове хранимой процедуры, перекомпиляция снимает блокировки компиляции, которые (A) не позволяют другим выполнять хранимую процедуру до завершения перекомпиляции, и (B) блокируют различные задействованные ресурсы / зависимостив перекомпиляции.В загруженной системе ваш администратор базы данных вряд ли будет с пользой смотреть на блокировку.

Вот MSDN на Кэширование и повторное использование плана выполнения

Надеюсь, это поможет.

...