Sintaxe de INNER JOIN aninhada dentro de OUTER JOIN x resultados da consulta

10

TLDR; Se você observar os dois planos de execução, existe uma resposta fácil para qual é melhor? Eu propositalmente NÃO criei índices, por isso é mais fácil ver o que está acontecendo.

Seguindo a minha pergunta anterior, onde encontramos diferenças de desempenho da consulta entre diferentes estilos de junção (por exemplo, aninhados x tradicionais), percebi que a sintaxe aninhada também modifica o comportamento da consulta. Considere as 2 consultas a seguir.

SELECT  a.*, m.*, n.*
FROM    dbo.Autos a
LEFT JOIN dbo.Models m
  JOIN dbo.Manufacturers n  -- <-- Nested INNER JOIN
  ON n.ManufacturerID = m.ManufacturerID
ON m.ModelID = a.ModelID

insira a descrição da imagem aqui

Isso não precisa unir os fabricantes para incluir uma linha automática com um ModelID que NÃO está na tabela Modelos.

insira a descrição da imagem aqui

Usando a sintaxe tradicional, precisamos alterar a junção para Manufactures para uma junção externa, assim ... mas isso altera o plano de consulta.

SELECT a.*, m.*, n.*
FROM dbo.Autos a
LEFT JOIN dbo.Models m
ON m.ModelID = a.ModelID
LEFT JOIN dbo.Manufacturers n -- <-- Now LEFT OUTER JOIN
ON n.ManufacturerID = m.ManufacturerID

insira a descrição da imagem aqui

JeffInCO
fonte

Respostas:

12

Se você observar os dois planos de execução, existe uma resposta fácil para qual é melhor? Eu propositalmente NÃO criei índices, por isso é mais fácil ver o que está acontecendo.

O segundo plano tem um custo estimado mais baixo, portanto, nesse sentido limitado, é "melhor".

Os conjuntos de dados são tão pequenos que o otimizador não passou muito tempo procurando alternativas. A primeira forma da consulta é encontrar um plano usando junção de hash e um spool de tabela desde o início. O custo estimado desse plano é tão baixo que o otimizador não se incomoda em procurar algo melhor.

A segunda forma da consulta encontra um plano usando apenas associações externas de loops aninhados no início do processo de pesquisa e, novamente, o otimizador decide que o plano é bom o suficiente. Acontece que este plano é estimado para ser mais barato.

Dito isto (como mencionado nos comentários da pergunta), as duas consultas não são semanticamente idênticas. Isso pode não ser importante para você, se você puder garantir que os resultados sempre serão os mesmos para todos os possíveis estados futuros do seu banco de dados, mas o otimizador não pode fazer essa suposição. Ele sempre produz planos que garantem a produção dos mesmos resultados especificados pelo SQL, em todas as circunstâncias.

Percebi que a sintaxe aninhada também modifica o comportamento da consulta.

A 'sintaxe aninhada' é apenas um aspecto de toda a especificação de sintaxe de junção ANSI. Para habilitar uma especificação lógica completa para padrões de junção mais complexos, a especificação permite parênteses (opcionais) e FROMsubconsultas de cláusula.

A consulta pode ser gravada usando a mesma sintaxe ANSI usando parênteses:

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Autos AS A
LEFT JOIN
(
    dbo.Manufacturers AS N
    JOIN dbo.Models AS M
        ON M.ManufacturerID = N.ManufacturerID
) ON M.ModelID = A.ModelID;

Este formulário mostra claramente que o requisito lógico é deixar a junção esquerda Autospara o resultado da junção interna Manufacturerspara Models. A omissão dos parênteses opcionais fornece o formulário que você chama de 'aninhado':

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Autos AS A
LEFT JOIN dbo.Manufacturers AS N
JOIN dbo.Models AS M
    ON M.ManufacturerID = N.ManufacturerID
    ON M.ModelID = A.ModelID;

Esta não é uma sintaxe diferente - apenas omite parênteses opcionais e reformata um pouco.

Como Martin mencionou, também é possível, neste caso, expressar o requisito lógico usando junções internas seguidas por uma junção externa direita:

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Manufacturers AS N
JOIN dbo.Models AS M
    ON M.ManufacturerID = N.ManufacturerID
RIGHT JOIN dbo.Autos AS A
    ON A.ModelID = M.ModelID;

Todos os três formulários de consulta acima usam a mesma sintaxe de junção ANSI. Todos os três também produzem o mesmo plano de execução física com o conjunto de dados fornecido:

Plano de execução comum

Como mencionei na minha resposta à sua pergunta anterior, as consultas que expressam exatamente o mesmo requisito lógico nem sempre produzirão o mesmo plano de execução. Qual formulário de consulta lógica você prefere usar é em grande parte uma questão de estilo. Não há correlação entre um estilo específico e os planos de consulta "melhores" em geral. Eu geralmente desaconselharia reescrever uma consulta para obter um plano específico se a nova consulta não for genuinamente logicamente idêntica à original.

O padrão SQL também permite FROMsubconsultas de cláusula, portanto, outra maneira de escrever a mesma especificação de consulta é:

SELECT * 
FROM dbo.Autos AS A
LEFT JOIN
(
    SELECT
        N.ManufacturerID,
        ManufacturerName = N.Name,
        M.ModelID,
        ModelName = M.Name
    FROM dbo.Manufacturers AS N
    JOIN dbo.Models AS M
        ON M.ManufacturerID = N.ManufacturerID
) AS R1
    ON R1.ModelID = A.ModelID;

Usando a sintaxe tradicional, temos que alterar a junção para `Fabricantes para uma junção externa, assim ... mas isso altera o plano de consulta.

Provavelmente isso muda o significado da consulta; nesse caso, tecnicamente, não é uma alternativa válida (mas veja o comentário do ypercube na sua pergunta).

Os parênteses (opcionais) na sintaxe de junção ANSI existem precisamente para requisitos de junção mais complexos como esse, portanto, você não deve ter medo de usá-los sempre que necessário.

Paul White 9
fonte