Другой план выполнения при выполнении оператора напрямую и из хранимой процедуры
-
05-07-2019 - |
Вопрос
При разработке нового запроса на работе я написал его и профилировал в SQL Query Analyzer. Запрос выполнялся действительно хорошо без каких-либо сканирований таблицы, но когда я инкапсулировал его в хранимую процедуру, производительность была ужасной. Когда я посмотрел на план выполнения, я увидел, что SQL Server выбрал другой план, в котором вместо поиска по индексу в TableB использовалось сканирование таблицы (я был вынужден немного запутать имена таблиц и столбцов, но ни одна из логики запроса не использовалась). изменилось).
Вот запрос
SELECT
DATEADD(dd, 0, DATEDIFF(dd, 0, TableA.Created)) AS Day,
DATEPART(hh, TableA.Created) AS [Hour],
SUM(TableB.Quantity) AS Quantity,
SUM(TableB.Amount) AS Amount
FROM
TableA
INNER JOIN TableB ON TableA.BID = TableB.ID
WHERE
(TableA.ShopId = @ShopId)
GROUP BY
DATEADD(dd, 0, DATEDIFF(dd, 0, TableA.Created)),
DATEPART(hh, TableA.Created)
ORDER BY
DATEPART(hh, TableA.Created)
Когда я запускаю запрос " raw " Я получаю следующую статистику трассировки
Event Class Duration CPU Reads Writes SQL:StmtCompleted 75 41 7 0
И когда я запускаю запрос как сохраненный процесс, используя следующую команду
DECLARE @ShopId int
SELECT @ShopId = 1
EXEC spStats_GetSalesStatsByHour @ShopId
Я получаю следующую статистику трассировки
Event Class Duration CPU Reads Writes SQL:StmtCompleted 222 10 48 0
Я также получаю тот же результат, если я сохраняю запрос в nvarchar и выполняю его, используя sp_executesql следующим образом (он работает как sproc)
DECLARE @SQL nvarchar(2000)
SET @SQL = 'SELECT DATEADD(dd, ...'
exec sp_executesql @SQL
Хранимая процедура не содержит ничего, кроме оператора выбора выше. Что заставит сервер sql выбрать худший план выполнения только потому, что оператор выполняется как хранимая процедура?
В настоящее время мы работаем на SQL Server 2000
Решение
Обычно это связано с анализом параметров. Это может быть очень неприятно иметь дело. Иногда это можно решить путем перекомпиляции хранимой процедуры, а иногда вы даже можете использовать дублирующую переменную внутри хранимой процедуры, например так:
alter procedure p_myproc (@p1 int) as
declare @p1_copy int;
set @p1_copy = @p1;
А затем используйте @ p1_copy в запросе. Кажется смешным, но это работает.
Проверьте мой недавний вопрос на ту же тему:
Другие советы
Да, я видел это и в Oracle DB 11g - один и тот же запрос выполнялся быстро на 2 узлах db-сервера при запросе SQL, НО при вызове из пакета он буквально зависал!
пришлось очистить общий пул, чтобы получить идентичное поведение: причина, по которой выполнялось какое-то задание / сценарий, в котором старая копия была заблокирована в кеше / памяти библиотеки на одном узле с худшим планом выполнения.