Roy*_*mir 11 index sql-server-2008-r2
(问题移自 SO)
我有一个带有聚集索引的表(虚拟数据)包含 2 列:
现在我运行这两个查询:
declare
@productid int =1 ,
@priceid int = 1
SELECT productid,
t.priceID
FROM Transactions AS t
WHERE (productID = @productid OR @productid IS NULL)
AND (priceid = @priceid OR @priceid IS NULL)
SELECT productid,
t.priceID
FROM Transactions AS t
WHERE (productID = @productid)
AND (priceid = @priceid)
Run Code Online (Sandbox Code Playgroud)
两个查询的实际执行计划是:
如您所见,第一个使用 SCAN,而第二个使用 SEEK。
但是 - 添加OPTION (RECOMPILE)
到第一个查询,使执行计划也使用 SEEK:
DBA 聊天室的朋友告诉我:
在您的查询中,@productid=1,这意味着(productID=@productID OR @productID IS NULL)可以简化为(productID=@productID)。前者需要扫描才能使用@productID 的任何值,后者可以使用搜索。因此,当您使用 RECOMPILE 时,SQL Server 将查看您在 @productID 中实际拥有的值并为其制定最佳计划。在@productID 中使用非空值时,最好使用查找。如果@productID 的值未知,则计划必须适合@productID 中的任何可能值,这将需要扫描。请注意:每次运行时,OPTION (RECOMPILE) 都会强制重新编译计划,这会为每次执行增加几毫秒。尽管这只是在查询非常频繁地运行时才会出现的问题。
还 :
如果@productID 为空,您会寻求什么值?回答:没有什么可寻求的。所有值都符合条件。
我知道这会OPTION (RECOMPILE)
强制 SQL Server 查看参数具有的实际值,并查看它是否可以使用它进行搜索。
但是现在我失去了提前编译的好处。
题
恕我直言- SCAN 只会在参数为 null 时发生。
没关系 - 让 SQL SERVER 为 SCAN 创建一个执行计划。
但是,如果 SQL Server 发现我多次使用 values: 运行此查询1,1
,那么它为什么不创建另一个执行计划并为此使用 SEEK?
AFAIK-SQL 为命中率最高的查询创建执行计划。
为什么 SQL SERVER 不保存以下的执行计划:
@productid int =1 ,
@priceid int = 1
(我用这些值多次运行它)
Pau*_*ite 10
总结我们聊天室讨论的一些要点:
一般而言,SQL Server为每个语句缓存一个计划。该计划必须对所有可能的未来参数值有效。
不可能为您的查询缓存搜索计划,因为例如,如果@productid为空,该计划将无效。
在未来的某个版本中,SQL Server 可能支持根据运行时参数值在扫描和查找之间动态选择的单一计划,但这不是我们今天所拥有的。
您的查询是模式的一个例子,也被称为“一网打尽”或“动态搜索”查询。有各种解决方案,每个都有自己的优点和缺点。在现代版本的 SQL Server (2008+) 中,主要选项是:
IF
块OPTION (RECOMPILE)
sp_executesql
关于该主题的最全面的工作可能是 Erland Sommarskog,它包含在本答案末尾的参考资料中。无法摆脱所涉及的复杂性,因此有必要花一些时间尝试每个选项以了解每种情况下的权衡。
IF
块为了说明IF
问题中特定情况的块解决方案:
IF @productid IS NOT NULL AND @priceid IS NOT NULL
BEGIN
SELECT
T.productID,
T.priceID
FROM dbo.Transactions AS T
WHERE
T.productID = @productid
AND T.priceID = @priceid;
END;
ELSE IF @productid IS NOT NULL
BEGIN
SELECT
T.productID,
T.priceID
FROM dbo.Transactions AS T
WHERE
T.productID = @productid;
END;
ELSE IF @priceid IS NOT NULL
BEGIN
SELECT
T.productID,
T.priceID
FROM dbo.Transactions AS T
WHERE
T.priceID = @priceid;
END;
ELSE
BEGIN
SELECT
T.productID,
T.priceID
FROM dbo.Transactions AS T;
END;
Run Code Online (Sandbox Code Playgroud)
这包含针对两个参数(或局部变量)中的每一个的四种可能的空或非空情况的单独语句,因此有四个计划。
参数嗅探存在潜在问题,可能需要OPTIMIZE FOR
对每个查询进行提示。请参阅参考资料部分以探索这些类型的微妙之处。
如上所述,您还可以添加OPTION (RECOMPILE)
提示以在每次调用时获取新计划(搜索或扫描)。鉴于您的情况下调用的频率相对较慢(平均每十秒一次,编译时间为亚毫秒),此选项似乎适合您:
SELECT
T.productID,
T.priceID
FROM dbo.Transactions AS T
WHERE
(T.productID = @productid OR @productid IS NULL)
AND (T.priceID = @priceid OR @priceid IS NULL)
OPTION (RECOMPILE);
Run Code Online (Sandbox Code Playgroud)
还可以以创造性的方式组合上述选项的功能,以充分利用每种方法的优点,同时最大限度地减少缺点。详细了解这些内容,然后在实际测试的支持下做出明智的选择,确实没有捷径可走。
RECOMPILE
选项 归档时间: |
|
查看次数: |
519 次 |
最近记录: |