2017-03-12 1 views
0

Lorsque j'utilise un littéral dans une clause WHERE dans une requête sur une vue, le résultat est essentiellement instantané. Lorsque j'utilise un ensemble de variables à la même valeur, un plan de requête complètement différent et très lent prend sa place. Comment est-ce possible? Comment cela peut-être très différent:Littéral vs variable dans une requête T-SQL avec affichage produit un temps de requête très différent

DECLARE @a INT = 5 

SELECT ... 
WHERE myview.mycol = @a 

vs

SELECT ... 
WHERE myview.mycol = 5 

Voici la requête exacte et le calendrier que je rencontre (je peux publier des informations supplémentaires sur la vue elle-même, mais je ne sais pas que l'affichage de la définition de la vue elle-même est très utile. il est complexe et repose sur une douzaine d'autres tables et vues, je peux le poster si elle est utile pour répondre à la question)

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

ce qui provoque SQL Server pour traiter 5 littérale si différemment de @productbuid INT = 5?

Merci beaucoup pour vos conseils.

MISE À JOUR: On m'a demandé d'inclure la définition de la vue:

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 
+0

Quelle est la définition de vwPublishingActions? À quoi ressemblent les plans d'exécution? –

+0

@MartinSmith Merci, j'ai posté la définition de la vue maintenant. Je vais essayer d'afficher quelques informations sur la différence entre les plans d'exécution. –

Répondre

1

Vous auriez besoin de regarder pour être sûr au plan d'exécution. Lorsque vous utilisez une variable mycol = @a SQL Server créera un plan basé sur la densité moyenne des colonnes pour les valeurs mycol.

Le prédicat mycol = 5 peut être significativement supérieur ou inférieur à la moyenne. Lorsque vous utilisez un SQL Server littéral peut rechercher la valeur 5 dans les statistiques de la colonne et éventuellement obtenir des estimations plus précises et donc un plan plus approprié. De plus, l'utilisation d'un littéral peut permettre des optimisations et des simplifications supplémentaires.

Un exemple est qu'une vue avec PARTITION BY mycol peut avoir le prédicat littéral pushed further down qu'une variable ou un paramètre peut généralement (sauf si vous utilisez OPTION (RECOMPILE)). En outre, avec la valeur littérale disponible lors de la compilation, SQL Server peut peut-être simplifier les expressions et utiliser la détection de contradictions pour éliminer une partie du travail au moment de l'exécution.

+0

Martin merci. Je ne comprends toujours pas la différence - je ne transmets pas de valeur variable à une fonction, c'est une variable déclarée et définie directement dans la requête. Sûrement SQL Server sait que ma variable a une valeur qui peut être utilisée aussi facilement que le littéral qu'il représente? –

+1

@SalahatSoapHope - SQL Server ne "renifle" pas la valeur des variables sauf si vous ajoutez 'OPTION (RECOMPILE)' - le lot est compilé avant même que la variable ne soit affectée mais ne le renifle pas même si l'instruction est recompilée l'affectation, sauf avec cet indice. C'est parce que l'affectation aux variables locales est/était une technique courante pour contourner les problèmes de reniflage des paramètres et cette technique serait brisée si elle commençait à renifler les variables de façon routinière. –