带有视图的T-SQL查询中的文字与变量会产生截然不同的查询时间

时间:2017-03-12 20:30:02

标签: sql sql-server tsql sql-execution-plan sql-view

当我在对视图的查询中的WHERE子句中使用文字时,结果基本上是即时的。当我使用设置为相同值的变量时,一个完全不同且非常慢的查询计划取而代之。这怎么可能?这些如何大不相同:

DECLARE @a INT = 5

SELECT ... 
WHERE myview.mycol = @a 

VS

SELECT ... 
WHERE myview.mycol = 5

这是我遇到的确切查询和时间(我可以发布有关视图本身的其他信息,但我不知道发布视图定义本身是有用的:它很复杂并依赖于其他十几个视图和表格。如果它有助于回答问题我可以发布。)

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;
  • time1:13
  • time2:2796

是什么导致SQL Server对文字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

1 个答案:

答案 0 :(得分:1)

您需要查看执行计划以确定。

使用变量mycol = @a时,SQL Server将根据mycol中值的平均列密度创建计划。

mycol = 5谓词可能明显高于或低于平均值。当您使用文字SQL Server时,可以在列统计信息中查找值5,并可能获得更准确的估算值,从而获得更合适的计划。

另外,使用文字可以允许一些额外的优化和简化。

一个示例是,PARTITION BY mycol的视图可以包含文字谓词pushed further down而不是变量或参数(使用OPTION (RECOMPILE)时除外)。

此外,在编译时可用的文字值SQL Server可能能够简化表达式并使用矛盾检测来消除运行时的一些工作。