Pourquoi le fait de référencer une variable dans un prédicat de jointure force-t-il les boucles imbriquées?

16

J'ai rencontré ce problème récemment et je n'ai trouvé aucune discussion à ce sujet en ligne.

La requête ci-dessous

DECLARE @S VARCHAR(1) = '';

WITH T
     AS (SELECT name + @S AS name2,
                *
         FROM   master..spt_values)
SELECT *
FROM   T T1
       INNER JOIN T T2
         ON T1.name2 = T2.name2;

Obtient toujours un plan de boucles imbriquées

entrez la description de l'image ici

Tenter de forcer le problème avec INNER HASH JOINou des INNER MERGE JOINconseils produit l'erreur suivante.

Le processeur de requêtes n'a pas pu produire un plan de requête en raison des indications définies dans cette requête. Renvoyez la requête sans spécifier d'indices et sans utiliser SET FORCEPLAN.

J'ai trouvé une solution de contournement qui permet d'utiliser des jointures de hachage ou de fusion - en enveloppant la variable dans un agrégat. Le plan généré est nettement moins cher (19.2025 vs 0.261987)

DECLARE @S2 VARCHAR(1) = '';

WITH T
     AS (SELECT name + (SELECT MAX(@S2)) AS name2,
                *
         FROM   spt_values)
SELECT *
FROM   T T1
       INNER JOIN T T2
         ON T1.name2 = T2.name2; 

entrez la description de l'image ici

Quelle est la raison de ce comportement? et y a-t-il une meilleure solution de contournement que celle que j'ai trouvée? (cela ne nécessite peut-être pas les branches de plan d'exécution supplémentaires)

Martin Smith
la source

Réponses:

13

J'ai essayé votre requête sur une instance SQL 2012 et l'indicateur de trace 4199 semble résoudre le problème. Avec elle activée, j'obtiens une jointure de fusion pour un coût total de 0,24 et aucune des branches supplémentaires.

L'article de la base de connaissances spécifique à ce problème est Les problèmes de performances se produisent lorsque le prédicat de jointure dans votre requête a des colonnes de référence externes dans SQL Server 2005 ou SQL Server 2008

entrez la description de l'image ici

Pour se qualifier davantage, TF 4199 active tous les correctifs de l'optimiseur. Voir ce lien pour plus d'informations. Tout activer en même temps peut avoir des effets secondaires étranges, donc si vous pouvez trouver un correctif spécifique, il serait préférable de l'activer seul.

Vous pouvez activer un indicateur de trace pour chaque requête en utilisant OPTION (QUERYTRACEON 4199);

Tom V - Équipe Monica
la source
0

Vieille question, mais en voyant la réponse n'était pas super définitive, j'ai pensé publier une solution de contournement que j'ai trouvée. Je ne sais pas pourquoi l'optimiseur de requêtes rechigne HASH, mais je pense qu'il n'aime pas MERGEcar il n'a pas d'entrée triée. Le 2012/14,

DECLARE @S VARCHAR(1) = '';

    WITH T
        AS (SELECT TOP (2147483647)
                name + @S AS name2,
                *
            FROM   master..spt_values
            ORDER BY name + @S)
    SELECT *
    FROM   T T1
           INNER JOIN T T2
             ON T1.name2 = T2.name2;

produit le plan suivant:

entrez la description de l'image ici

Le forçage TOPet le ORDER BYdans le cte semblent donner à l'optimiseur suffisamment de connaissances sur l'ensemble de données pour effectuer le MERGE JOIN.

VBlades
la source