带有视图的 T-SQL 查询中的文字与变量会产生截然不同的查询时间
Literal vs variable in T-SQL query with view produces vastly different query time
当我在针对视图的查询中的 WHERE 子句中使用文字时,结果基本上是即时的。当我使用一个设置为相同值的变量时,一个完全不同且非常慢的查询计划取而代之。这怎么可能?这些怎么会有很大的不同:
DECLARE @a INT = 5
SELECT ...
WHERE myview.mycol = @a
对
SELECT ...
WHERE myview.mycol = 5
这是我遇到的确切查询和时间(我可以 post 有关视图本身的其他信息,但我不知道 post 视图定义本身是有用:它很复杂并且依赖于十几个其他视图和表格。如果它对回答问题有帮助,我可以post它。)
DECLARE @productdbuid INT = 5
DECLARE @t1 DATETIME;
DECLARE @t2 DATETIME;
---------------------
SET @t1 = GETDATE();
SELECT
*
FROM
vwPublishingActions
WHERE
productdbuid = 5 AND storedbuid = 1
SET @t2 = GETDATE();
SELECT DATEDIFF(MILLISECOND,@t1,@t2) time1;
---------------------
SET @t1 = GETDATE();
SELECT
*
FROM
vwPublishingActions
WHERE
productdbuid = @productdbuid AND storedbuid = 1
SET @t2 = GETDATE();
SELECT DATEDIFF(MILLISECOND,@t1,@t2) time2;
- 时间 1: 13
- time2: 2796
是什么导致 SQL 服务器对待文字 5 的方式与 @productbuid INT = 5
如此不同?
非常感谢您的指导。
更新:我被要求包含视图定义:
SELECT
T2.productdbuid,
T2.storedbuid,
ISNULL(paca.publishactiondbuid, 8) publishingaction, -- 8 = ERROR_REPORT
T2.change_product,
T2.change_price,
T2.change_stockstatus,
T2.inventory_belowtrigger,
T2.ruledbuid ruledbuid
FROM
(SELECT
T.productdbuid,
T.storedbuid,
-- pick first fully matching set of conditions
(SELECT TOP 1 paca.dbuid
FROM dbo.z_PublishingActionCalcs paca
WHERE (paca.storetypedbuid = T.storetypedbuid)
AND (paca.publishingcommanddbuid = T.publishcommanddbuid OR paca.publishingcommanddbuid IS NULL)
AND (ISNULL(paca.publishingstatusdbuid, 0) = ISNULL(T.publishstatusdbuid, 1007) OR paca.publishingstatusdbuid IS NULL) -- 1007 = NOTSET
AND (ISNULL(ABS(paca.change_product),0) = ISNULL(ABS(T.change_product),0) OR paca.change_product IS NULL)
AND (ISNULL(ABS(paca.change_price),0) = ISNULL(ABS(T.change_price),0) OR paca.change_price IS NULL)
AND (ISNULL(ABS(paca.change_stockstatus),0) = ISNULL(ABS(T.change_stockstatus),0) OR paca.change_stockstatus IS NULL)
AND (ISNULL(ABS(paca.inventory_belowtrigger),0) = ISNULL(ABS(T.inventory_belowtrigger),0) OR paca.inventory_belowtrigger IS NULL)
AND (ISNULL(paca.stockstatusdbuid, 0) = ISNULL(T.stockstatusdbuid, 0) OR paca.stockstatusdbuid IS NULL)
ORDER BY paca.sort) ruledbuid,
ABS(ISNULL(T.change_product,0)) change_product,
ABS(ISNULL(T.change_price,0)) change_price,
ABS(ISNULL(T.change_stockstatus,0)) change_stockstatus,
ABS(ISNULL(T.inventory_belowtrigger,0)) inventory_belowtrigger
FROM
(SELECT
p.productid,
s.storetypedbuid,
CASE
WHEN pdpcm.publishcommanddbuid <> 4
THEN NULL -- STOCKSTATUS
ELSE pss.stockstatusdbuid
END product_stockstatus,
CASE
WHEN pdpcm.publishcommanddbuid <> 5
THEN NULL -- INVENTORY
ELSE itr.inventory_belowtrigger
END inventory_belowtrigger,
p.dbuid productdbuid,
s.dbuid storedbuid,
pdpc.change_product,
pdpc.change_price,
pdpc.change_stockstatus,
pdpcm.publishcommanddbuid,
pdps.publishstatusdbuid,
pss.stockstatusdbuid
FROM
dbo.ProductDetailsPublishingCommands pdpcm
INNER JOIN
dbo.Stores s ON s.dbuid = pdpcm.storedbuid
INNER JOIN
dbo.Products p ON pdpcm.productdbuid = p.dbuid
INNER JOIN
dbo.StoreTypeSet st ON st.dbuid = s.storetypedbuid
LEFT JOIN
dbo.vwPublishingChanges pdpc ON pdpc.productdbuid = p.dbuid
AND pdpc.storedbuid = s.dbuid
LEFT JOIN
dbo.ProductDetailsPublishingStatuses pdps ON pdps.productdbuid = p.dbuid
AND pdps.storedbuid = s.dbuid
LEFT JOIN
dbo.vwProductStockStatus pss ON pss.productdbuid = p.dbuid
LEFT JOIN
dbo.vwProductInventory pri ON pri.productdbuid = p.dbuid
LEFT JOIN
dbo.vwInventoryTriggers itr ON itr.storedbuid = s.dbuid
AND itr.productdbuid = p.dbuid) T
) T2
LEFT JOIN
dbo.z_PublishingActionCalcs paca ON T2.ruledbuid = paca.dbuid
您需要查看执行计划才能确定。
当您使用变量 mycol = @a
SQL 服务器将根据 mycol
中的值的平均列密度创建一个计划。
mycol = 5
谓词可能明显高于或低于平均水平。当您使用文字 SQL 服务器可以在列统计信息中查找值 5
并可能获得更准确的估计,从而获得更合适的计划。
此外,使用文字可以进行一些额外的优化和简化。
例如,具有 PARTITION BY mycol
的视图可以具有字面谓词 pushed further down 而变量或参数通常可以(使用 OPTION (RECOMPILE)
时除外)。
此外,在编译时可以使用文字值 SQL 服务器可以简化表达式并使用矛盾检测来消除 运行 时的一些工作。
当我在针对视图的查询中的 WHERE 子句中使用文字时,结果基本上是即时的。当我使用一个设置为相同值的变量时,一个完全不同且非常慢的查询计划取而代之。这怎么可能?这些怎么会有很大的不同:
DECLARE @a INT = 5
SELECT ...
WHERE myview.mycol = @a
对
SELECT ...
WHERE myview.mycol = 5
这是我遇到的确切查询和时间(我可以 post 有关视图本身的其他信息,但我不知道 post 视图定义本身是有用:它很复杂并且依赖于十几个其他视图和表格。如果它对回答问题有帮助,我可以post它。)
DECLARE @productdbuid INT = 5
DECLARE @t1 DATETIME;
DECLARE @t2 DATETIME;
---------------------
SET @t1 = GETDATE();
SELECT
*
FROM
vwPublishingActions
WHERE
productdbuid = 5 AND storedbuid = 1
SET @t2 = GETDATE();
SELECT DATEDIFF(MILLISECOND,@t1,@t2) time1;
---------------------
SET @t1 = GETDATE();
SELECT
*
FROM
vwPublishingActions
WHERE
productdbuid = @productdbuid AND storedbuid = 1
SET @t2 = GETDATE();
SELECT DATEDIFF(MILLISECOND,@t1,@t2) time2;
- 时间 1: 13
- time2: 2796
是什么导致 SQL 服务器对待文字 5 的方式与 @productbuid INT = 5
如此不同?
非常感谢您的指导。
更新:我被要求包含视图定义:
SELECT
T2.productdbuid,
T2.storedbuid,
ISNULL(paca.publishactiondbuid, 8) publishingaction, -- 8 = ERROR_REPORT
T2.change_product,
T2.change_price,
T2.change_stockstatus,
T2.inventory_belowtrigger,
T2.ruledbuid ruledbuid
FROM
(SELECT
T.productdbuid,
T.storedbuid,
-- pick first fully matching set of conditions
(SELECT TOP 1 paca.dbuid
FROM dbo.z_PublishingActionCalcs paca
WHERE (paca.storetypedbuid = T.storetypedbuid)
AND (paca.publishingcommanddbuid = T.publishcommanddbuid OR paca.publishingcommanddbuid IS NULL)
AND (ISNULL(paca.publishingstatusdbuid, 0) = ISNULL(T.publishstatusdbuid, 1007) OR paca.publishingstatusdbuid IS NULL) -- 1007 = NOTSET
AND (ISNULL(ABS(paca.change_product),0) = ISNULL(ABS(T.change_product),0) OR paca.change_product IS NULL)
AND (ISNULL(ABS(paca.change_price),0) = ISNULL(ABS(T.change_price),0) OR paca.change_price IS NULL)
AND (ISNULL(ABS(paca.change_stockstatus),0) = ISNULL(ABS(T.change_stockstatus),0) OR paca.change_stockstatus IS NULL)
AND (ISNULL(ABS(paca.inventory_belowtrigger),0) = ISNULL(ABS(T.inventory_belowtrigger),0) OR paca.inventory_belowtrigger IS NULL)
AND (ISNULL(paca.stockstatusdbuid, 0) = ISNULL(T.stockstatusdbuid, 0) OR paca.stockstatusdbuid IS NULL)
ORDER BY paca.sort) ruledbuid,
ABS(ISNULL(T.change_product,0)) change_product,
ABS(ISNULL(T.change_price,0)) change_price,
ABS(ISNULL(T.change_stockstatus,0)) change_stockstatus,
ABS(ISNULL(T.inventory_belowtrigger,0)) inventory_belowtrigger
FROM
(SELECT
p.productid,
s.storetypedbuid,
CASE
WHEN pdpcm.publishcommanddbuid <> 4
THEN NULL -- STOCKSTATUS
ELSE pss.stockstatusdbuid
END product_stockstatus,
CASE
WHEN pdpcm.publishcommanddbuid <> 5
THEN NULL -- INVENTORY
ELSE itr.inventory_belowtrigger
END inventory_belowtrigger,
p.dbuid productdbuid,
s.dbuid storedbuid,
pdpc.change_product,
pdpc.change_price,
pdpc.change_stockstatus,
pdpcm.publishcommanddbuid,
pdps.publishstatusdbuid,
pss.stockstatusdbuid
FROM
dbo.ProductDetailsPublishingCommands pdpcm
INNER JOIN
dbo.Stores s ON s.dbuid = pdpcm.storedbuid
INNER JOIN
dbo.Products p ON pdpcm.productdbuid = p.dbuid
INNER JOIN
dbo.StoreTypeSet st ON st.dbuid = s.storetypedbuid
LEFT JOIN
dbo.vwPublishingChanges pdpc ON pdpc.productdbuid = p.dbuid
AND pdpc.storedbuid = s.dbuid
LEFT JOIN
dbo.ProductDetailsPublishingStatuses pdps ON pdps.productdbuid = p.dbuid
AND pdps.storedbuid = s.dbuid
LEFT JOIN
dbo.vwProductStockStatus pss ON pss.productdbuid = p.dbuid
LEFT JOIN
dbo.vwProductInventory pri ON pri.productdbuid = p.dbuid
LEFT JOIN
dbo.vwInventoryTriggers itr ON itr.storedbuid = s.dbuid
AND itr.productdbuid = p.dbuid) T
) T2
LEFT JOIN
dbo.z_PublishingActionCalcs paca ON T2.ruledbuid = paca.dbuid
您需要查看执行计划才能确定。
当您使用变量 mycol = @a
SQL 服务器将根据 mycol
中的值的平均列密度创建一个计划。
mycol = 5
谓词可能明显高于或低于平均水平。当您使用文字 SQL 服务器可以在列统计信息中查找值 5
并可能获得更准确的估计,从而获得更合适的计划。
此外,使用文字可以进行一些额外的优化和简化。
例如,具有 PARTITION BY mycol
的视图可以具有字面谓词 pushed further down 而变量或参数通常可以(使用 OPTION (RECOMPILE)
时除外)。
此外,在编译时可以使用文字值 SQL 服务器可以简化表达式并使用矛盾检测来消除 运行 时的一些工作。