Mar*_*ark 7 sql t-sql sql-server stored-procedures sql-server-2005
我有一个执行非常糟糕的存储过程.当我声明一个变量时,设置它的值,然后在where子句中使用它,该语句需要一个多小时才能运行.当我在where子句中对变量进行硬编码时,它会在不到一秒的时间内运行.
我开始通过执行计划调查它出了什么问题.看起来当我尝试传递一些声明的变量时,执行计划会创建一些Hash Match,因为它从使用UNION和公用表表达式的视图中选择值.
/************* Begin of Stored Procedure ***************/
CREATE PROCEDURE GetFruit
@ColorId bigint,
@SeasionId bigint
WITH RECOMPILE
AS
BEGIN
SELECT
A.Name
FROM
[Apple_View] A /* This is the view down below */
INNER JOIN [Fruit] F
ON ( F.ColorId = @ColorId
AND A.FruitId = F.FruitId)
WHERE
(A.ColorId = @ColorId
AND
A.SeasonId = @SeasonId)
END
/************* End of Stored Procedure ***************/
/************* Begin of View ***************/
WITH Fruits (FruitId, ColorId, SeasonId) AS
(
-- Anchor member
SELECT
F.FruitId
,F.ColorId
,F.SeasonId
FROM
((
SELECT DISTINCT
EF.FruitId
,EF.ColorId
,EF.SeasonId
,EF.ParentFruitId
FROM
ExoticFruit EF
INNER JOIN Fruit FR
ON FR.FruitId = EF.FruitId
UNION
SELECT DISTINCT
SF.FruitId
,SF.ColorId
,SF.SeasonId
,SF.ParentFruitId
FROM
StinkyFruit SF
INNER JOIN Fruit FR
ON FR.FruitId = SF.FruitId
UNION
SELECT DISTINCT
CF.FruitId
,CF.ColorId
,CF.SeasonId
,CF.ParentFruitId
FROM
CrazyFruit CF
INNER JOIN Fruit FR
ON FR.FruitId = CF.FruitId
)) f
UNION ALL
-- Recursive Parent Fruit
SELECT
FS.FruitId
,FS.ColorId
,FS.SeasonId
,FS.ParentFruitId
FROM
Fruits FS
INNER JOIN MasterFruit MF
ON MF.[ParentFruitId] = fs.[FruitId]
)
SELECT DISTINCT
FS.FruitId
,FS.ColorId
,FS.SeasonId
FROM
Fruits FS
/************* End of View ***************/
/* To Execute */
EXEC GetFruit 1,3
如果我使用设置值运行存储过程,则需要一个多小时,这是执行计划.

如果我运行存储过程删除DECLARE和SET值并将Where子句设置为以下语句,它将在不到一秒的时间内运行,这是执行计划:
WHERE(A.ColorId = 1 AND A.SeasonId = 3)
Run Code Online (Sandbox Code Playgroud)

注意硬编码变量如何使用索引,而第一个使用哈希集.这是为什么?为什么where子句中的硬编码值与声明的变量不同?
-------这是最后在@ user1166147的帮助下完成的------
我更改了存储过程以使用sp_executesql.
CREATE PROCEDURE GetFruit
@ColorId bigint,
@SeasionId bigint
WITH RECOMPILE
AS
BEGIN
DECLARE @SelectString nvarchar(max)
SET @SelectString = N'SELECT
A.Name
FROM
[Apple_View] A /* This is the view down below */
INNER JOIN [Fruit] F
ON ( F.ColorId = @ColorId
AND A.FruitId = F.FruitId)
WHERE
(A.ColorId = ' + CONVERT(NVARCHAR(MAX), @ColorId) + '
AND
A.SeasonId = ' + CONVERT(NVARCHAR(MAX), @SeasonId) + ')'
EXEC sp_executesql @SelectString
END
编辑摘要 根据 Damien_The_Un believer 的要求
目标是在创建计划之前获取有关 SQL 变量值的最佳/最多信息,通常参数嗅探就是这样做的。在这种情况下,参数嗅探被“禁用”可能是有原因的。如果没有看到实际代码的更好表示,我们就无法真正说出解决方案是什么或问题存在的原因。请尝试以下操作,强制受影响的区域使用实际值生成计划。
*包含更多细节的长版 *
这是您实际的存储过程吗?你的参数有默认值吗?如果有,它们是什么?
参数嗅探可以提供帮助 - 但它必须具有典型的参数值才能很好地创建计划,如果没有,则不会真正有帮助,或者会根据非典型参数值创建一个糟糕的计划。因此,如果变量的默认值为 null 或在第一次运行和编译计划时不是典型值,则会创建一个错误的计划。
如果其他人编写了这个存储过程 - 他们可能出于某种原因故意“禁用”局部变量的参数嗅探。业务规则可能需要这些可变结构。
目标是在创建计划之前获取有关 SQL 变量值的最佳/最多信息,通常参数嗅探会执行此操作。但有些事情可能会使其对性能产生负面影响,这可能就是它被“禁用”的原因。看起来该计划仍然是使用非典型参数值创建的,或者仍然没有足够的信息 - 使用或不使用参数嗅探。
尝试使用 sp_executesql 调用存储过程内的查询来执行受影响的查询,强制它使用实际变量为该区域生成计划,并查看是否更好。如果您必须拥有这种不规则的参数值,这可能是您的解决方案 - 创建运行受影响部分的存储过程,并稍后在变量收到典型值后从存储过程中调用它们。
如果没有看到实际代码的更好表示,就很难看出问题所在。希望这些信息会有所帮助 -
| 归档时间: |
|
| 查看次数: |
3502 次 |
| 最近记录: |