Почему параметризованный запрос приводит к значительно более медленному плану запроса по сравнению с не параметризованным запросом? - PullRequest
21 голосов
/ 04 февраля 2009

В базе данных SQL Server 2005 я работаю над этим запросом:

выберите *
от Foo
присоединиться к bar на bar.x = foo.x
присоединяйтесь к baz на baz.y = foo.y
где foo.x = 1000

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

объявите @ p0 int
set @ p0 = 1000
выберите *
от Foo
присоединиться к bar на bar.x = foo.x
присоединяйтесь к baz на baz.y = foo.y
где foo.x = @ p0

В моем конкретном случае версия с литералом запускается за долю секунды. Параметризованная версия занимает 2-3 секунды. Я ожидал, что они будут идентичны, учитывая, что это один и тот же запрос.

Почему они получают разные планы запросов?

Можно ли сделать так, чтобы параметризованная версия имела ту же производительность, что и буквальная версия?

Вот планы запроса. Мой реальный запрос несколько отличается от общего, который я дал выше, однако ЕДИНСТВЕННАЯ разница между двумя запросами, которые дали эти планы, заключается в параметре. Почему замена литерала параметром привела бы к таким совершенно другим планам?

Ответы [ 4 ]

8 голосов
/ 04 февраля 2009

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

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

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

В частности, в вашем запросе попробуйте это:

declare @p0 int
set @p0 = 1000
select *
from foo
join bar on bar.x = foo.x
join baz on baz.y = foo.y
where foo.x = @p0
OPTION ( OPTIMIZE FOR (@p0 = 1000))

Но я бы с осторожностью поступил так, не будучи уверенным, что данные, содержащиеся в этом запросе, не изменятся и что ваш запрос по этому плану ВСЕГДА будет более эффективным.

6 голосов
/ 04 февраля 2009

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

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

Один из способов обойти это - заключить запрос в хранимую процедуру, которая напрямую принимает параметр, а затем применяет его к запросу - что-то вроде этого:

CREATE PROCEDURE test
  @p0 int
AS
BEGIN
  select *
  from foo
  join bar on bar.x = foo.x
  join baz on baz.y = foo.y
  where foo.x = @p0
END

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

2 голосов
/ 19 августа 2014

В моем случае в столбце таблицы БД тип столбца был определен как VarChar, а в параметризованном запросе тип параметра был определен как NVarChar, это ввело CONVERT_IMPLICIT в фактический план выполнения, чтобы соответствовать типу данных перед сравнением, и это было виновником производительности свиноматки, 2 с против 11 с Простое исправление типа параметра сделало параметризованный запрос столь же быстрым, как и не параметризованная версия.

Надеюсь, что это может помочь кому-то с подобной проблемой.

2 голосов
/ 04 февраля 2009

Отправной точкой должен быть профилировщик SQL. Запустите оба через профилировщик и посмотрите, каков план запроса в каждом случае ... затем обновите вопрос, чтобы описать два плана.

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

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