SQL Server Query: быстро с литералом, но медленно с переменной - PullRequest
38 голосов
/ 16 декабря 2010

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

SELECT * FROM view1 WHERE ID = 1

Однако, если я запрашиваю представление, как это, это занимает 4 секунды.

DECLARE @id INT = 1
SELECT * FROM View1 WHERE ID = @id

Я проверил2 плана запроса, и первый запрос выполняет поиск кластеризованного индекса по главной таблице, возвращая 1 запись, затем применяя остальную часть запроса представления к этому набору результатов, где в качестве второго запроса выполняется сканирование индекса, которое возвращает около 3000 записей записейа не только тот, который мне интересен, а затем отфильтровываю набор результатов.

Есть ли что-то очевидное, чего мне не хватает, чтобы попытаться заставить второй запрос использовать поиск индекса, а не сканирование индекса,Я использую SQL 2008, но все, что я делаю, должно работать и на SQL 2005. Сначала я подумал, что это какая-то проблема с анализом параметров, но я получаю те же результаты, даже если очищаю кеш.

Ответы [ 7 ]

38 голосов
/ 16 декабря 2010

Вероятно, это связано с тем, что в случае параметра оптимизатор не может знать, что значение не равно нулю, поэтому ему необходимо создать план, который будет возвращать правильные результаты, даже если это так.Если у вас SQL Server 2008 с пакетом обновления 1 (SP1), попробуйте добавить OPTION(RECOMPILE) к запросу.

4 голосов
/ 16 декабря 2010

Вы можете добавить ОПТИМИЗАЦИЮ ДЛЯ подсказку к вашему запросу, например

DECLARE @id INT = 1
SELECT * FROM View1 WHERE ID = @id OPTION (OPTIMIZE FOR (@ID = 1))
3 голосов
/ 19 августа 2014

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

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

1 голос
/ 17 января 2018

Я сам столкнулся с этой проблемой с представлением, которое работало <10 мс с прямым присваиванием (<strong> WHERE UtilAcctId = 12345 ), но занимало в 100 раз больше времени при присваивании переменной ( WHERE UtilAcctId= @ UtilAcctId ).
План выполнения для последнего не отличался от того, если бы я запустил представление для всей таблицы.

Мое решение не требовало тонны индексов, подсказок оптимизатора или длинного обновления статистики.

Вместо этого я преобразовал представление в User-Table-Function , где параметром было значение, необходимое в предложении WHERE.Фактически, это предложение WHERE было вложено в 3 запроса, и оно все еще работало и вернулось к скорости <10 мс.</p>

В конце концов я изменил параметр на ТИП, который является таблицей UtilAcctIds (int).Тогда я могу ограничить предложение WHERE списком из таблицы.ГДЕ UtilAcctId = [список параметров] .UtilAcctId.Это работает даже лучше.Я думаю, что пользовательские табличные функции предварительно скомпилированы.

1 голос
/ 24 декабря 2010

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

Использование подсказки оптимизации и значения говорит SQL, что «это значение будет использоваться большую часть времени, поэтому оптимизируйте для этого значения», и план сохраняется, как если бы использовалось это буквальное значение. Использование подсказки по оптимизации и вспомогательной подсказки UNKNOWN говорит SQL, что вы не знаете, каким будет значение, поэтому SQL просматривает статистику для столбца и решает, что лучше искать или сканировать, и соответственно составляет план.

0 голосов
/ 25 апреля 2018

DECLARE @id INT = 1

SELECT * FROM View1 WHERE ID = @ id

Сделайте это

DECLARE @sql varchar (max)

SET @ sql = 'SELECT * FROM View1 WHERE ID =' + CAST (@id как varchar)

EXEC (@sql)

Решает вашу проблему

0 голосов
/ 27 июня 2013

Я сталкивался с этой же проблемой сам, и это оказалось отсутствующим индексом, включающим (левое) объединение в результате подзапроса.

select *
from foo A
left outer join (
  select x, count(*)
  from bar
  group by x
) B on A.x = B.x

Добавлен индекс с именем bar_x для bar.x

...