При разработке нового запроса на работе я написал его и профилировал в 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