Por que fazer referência a uma variável em um predicado de junção força loops aninhados?

16

Me deparei com esse problema recentemente e não consegui encontrar nenhuma discussão on-line.

A consulta abaixo

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;

Sempre obtém um plano de loops aninhados

insira a descrição da imagem aqui

Tentar forçar o problema INNER HASH JOINou INNER MERGE JOINdicas produz o seguinte erro.

O processador de consultas não pôde produzir um plano de consulta devido às dicas definidas nesta consulta. Submeta novamente a consulta sem especificar nenhuma dica e sem usar SET FORCEPLAN.

Eu encontrei uma solução alternativa que permite que junções de hash ou mesclagem sejam usadas - envolvendo a variável em um agregado. O plano gerado tem custo significativamente menor (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; 

insira a descrição da imagem aqui

Qual o motivo desse comportamento? e existe uma solução melhor do que a que encontrei? (que talvez não exija as ramificações extras do plano de execução)

Martin Smith
fonte

Respostas:

13

Eu tentei sua consulta em uma instância do SQL 2012 e o sinalizador de rastreamento 4199 parece corrigir o problema. Com isso ativado, recebo uma junção de mesclagem por um custo total de 0,24 e nenhuma das ramificações extras.

O artigo específico da KB para esse problema é Problemas de desempenho ocorrem quando o predicado de junção em sua consulta possui colunas de referência externa no SQL Server 2005 ou no SQL Server 2008

insira a descrição da imagem aqui

Para se qualificar ainda mais, o TF 4199 permite todas as correções do otimizador. Veja este link para mais informações. Habilitar tudo de uma só vez pode ter efeitos colaterais estranhos; portanto, se você encontrar uma correção específica, será melhor habilitá-la por conta própria.

Você pode ativar um sinalizador de rastreamento por consulta usando OPTION (QUERYTRACEON 4199);

Tom V - Equipe Monica
fonte
0

A pergunta antiga, mas ver a resposta não era super definitiva, pensei em publicar uma solução alternativa que encontrei. Não sei por que o otimizador de consulta rejeita HASH, mas acho que não gosta MERGEporque não possui entrada classificada. Em 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;

produz o seguinte plano:

insira a descrição da imagem aqui

Forçar TOPe oORDER BY no cte parecem fornecer ao otimizador conhecimento suficiente sobre o conjunto de dados para executar o MERGE JOIN.

VBlades
fonte