我们有一个存储过程,其中包含如下查询:
SELECT
MyTable.Id, MyTable.Date_Start,
MySeccondTable.Name + ' ' + MySeccondTable.Family AS OwnerName
FROM
MyTable
INNER JOIN
MySeccondTable ON MyTable.UserId = MySeccondTable.Id
WHERE
(@Date_StartMin IS NULL OR MyTable.Date_Start >= @Date_StartMin)
ORDER BY
CASE
WHEN (@SortOrder = 'Id') AND (@ASC = 1)
THEN MyTable.Id
END ASC,
CASE
WHEN (@SortOrder = 'Id') AND (@ASC = 0)
THEN MyTable.Id
END DESC
@Date_StartMin
和@SortOrder
是用户发送到存储过程的参数。
此查询正常运行。但是当我们检查执行计划时,我们发现SQL Server使用的执行计划效率低3倍,执行
当我们将查询更改为:
SELECT
MyTable.Id, MyTable.Date_Start,
MySeccondTable.Name + ' ' + MySeccondTable.Family AS OwnerName
FROM
MyTable
INNER JOIN
MySeccondTable ON MyTable.UserId = MySeccondTable.Id
WHERE
(@Date_StartMin IS NULL OR MyTable.Date_Start >= @Date_StartMin)
ORDER BY
CASE
WHEN (2 > 1) AND (@ASC = 1)
THEN MyTable.Id
END ASC ,
CASE
WHEN (2 > 1) AND (@ASC = 0)
THEN MyTable.Id
END DESC
一切都在发生,执行时间也已确定。
那么重点是什么? 我们如何解决这个问题?
答案 0 :(得分:0)
即使查询看起来像是使用了正确的执行计划,它也可能不适用于所有变量组合。问题是SQL Server缓存执行计划,缓存计划通常基于存储过程的第一次运行。此过程称为"参数嗅探"。
您可以使用with recompile选项来解决这个问题:
SELECT t1.Id, t2.Date_Start,
t2.Name + ' ' + t2.Family AS OwnerName
FROM MyTable t INNER JOIN
MySeccondTable t2
ON t.UserId = t2.Id
WHERE (@Date_StartMin IS NULL OR t.Date_Start >= @Date_StartMin)
ORDER BY (CASE WHEN (@SortOrder = 'Id') AND (@ASC = 1) THEN t.Id
END) ASC,
(CASE WHEN (@SortOrder = 'Id') AND (@ASC = 0)
THEN t.Id
END) DESC
OPTION (RECOMPILE);
但是,我不能100%确定ORDER BY
中常量的参数嗅探,重新编译和编译器优化如何协同工作。
我可能会使用动态SQL:
declare @sql nvarchar(max);
set @sql = '
SELECT t1.Id, t2.Date_Start, t2.Name + ' ' + t2.Family AS OwnerName
FROM MyTable t INNER JOIN
MySeccondTable t2
ON t.UserId = t2.Id
WHERE (@Date_StartMin IS NULL OR t.Date_Start >= @Date_StartMin)
ORDER BY @SortOrder @Asc
OPTION (RECOMPILE)';
set @sql = REPLACE(REPLACE(@sql, '@SortOrder', COALESCE(@SortOrder, '')
), '@Asc', COALESCE(@Asc, '')
);
exec sp_executesql @sql, N'@Date_StartMin Date', @Date_StartMin = @Date_StartMin;