Devo aninhar associações externas dependentes no SQL Server?

9

Ouvi informações contraditórias sobre isso e espero uma opinião canônica ou de especialistas.

Se eu tiver vários LEFT OUTER JOINs, cada um dependente do último, é melhor aninhar eles?

Para um exemplo artificial, o JOINto MyParentdepende do JOINpara MyChild: http://sqlfiddle.com/#!3/31022/5

SELECT
    {columns}
FROM
    MyGrandChild AS gc
LEFT OUTER JOIN
    MyChild AS c
        ON c.[Id] = gc.[ParentId]
LEFT OUTER JOIN
    MyParent AS p
        ON p.[id] = c.[ParentId]

insira a descrição da imagem aqui

Comparado com http://sqlfiddle.com/#!3/31022/7

SELECT
    {columns}
FROM
    MyGrandChild AS gc
LEFT OUTER JOIN
    (
    MyChild AS c            
    LEFT OUTER JOIN
        MyParent AS p
            ON p.[id] = c.[ParentId]
    )
    ON c.[Id] = gc.[ParentId]

insira a descrição da imagem aqui

Como mostrado acima, eles produzem diferentes planos de consulta no SS2k8

Mateus
fonte
Eu gosto de usar junções aninhadas: michaeljswart.com/2012/09/when-i-use-nested-joins No entanto, pode ser uma questão de estilo.
Michael J Swart
@MichaelJSwart seu blog só aparece para discutir quando o dependente JOINé umINNER JOIN
Matthew
11
Como você deseja definir "melhor"? Pessoalmente, acho o primeiro muito mais fácil de ler - minha mente não gira em torno de tentar fazer engenharia reversa no relacionamento. Ter ON ... ONduas vezes seguidas (parênteses ou não) é muito confuso.
Aaron Bertrand
4
Quando não encontro diferença de desempenho entre duas maneiras de fazer algo, a próxima pergunta que me faço é: se eu fosse atropelado por um ônibus ou ganhasse na loteria hoje à noite, qual versão seria mais facilmente entendida e mantida por quem assumir o meu código amanhã ?
Aaron Bertrand
11
A use plandica funciona ao transplantar o segundo plano de consulta para o primeiro, mas não vice-versa.
Martin Smith

Respostas:

3

Isso absolutamente não é uma resposta canônica, mas notei que, para os planos de consulta de loops aninhados mostrados no SQL Fiddle, era possível aplicar o plano da Consulta 2 à Consulta 1 com o uso da USE PLANdica, mas a tentativa de operação inversa falha com

O processador de consultas não pôde produzir o plano de consulta porque a dica USE PLAN contém um plano que não pode ser verificado como legal para a consulta. Remova ou substitua a dica USE PLAN. Para melhor probabilidade de forçar o plano com êxito, verifique se o plano fornecido na dica USE PLAN é um gerado automaticamente pelo SQL Server para a mesma consulta.

Desabilitar a regra de transformação do otimizador ReorderLOJN impede que a dica de plano previamente bem-sucedida também seja bem-sucedida.

Experiências com maiores quantidades de dados mostra que o SQL Server é certamente capaz de transformar (A LOJ B) LOJ Ca A LOJ (B LOJ C)naturalmente bem, mas eu não vi qualquer evidência de que o inverso é verdadeiro.

Um caso muito artificial em que a primeira consulta tem um desempenho melhor que a segunda é

DROP TABLE  MyGrandChild , MyChild,  MyParent

CREATE TABLE MyParent
(Id int)

CREATE TABLE MyChild
(Id int PRIMARY KEY
,ParentId int,
Filler char(8000) NULL)

CREATE TABLE MyGrandChild
(Id int
,ParentId int)

INSERT INTO MyChild
                      (Id, ParentId)
SELECT TOP (100000) ROW_NUMBER() OVER (ORDER BY @@SPID),
                     ROW_NUMBER() OVER (ORDER BY @@SPID)    
FROM master..spt_values  v1, master..spt_values                  

INSERT INTO MyGrandChild
                      (Id, ParentId)
OUTPUT INSERTED.Id INTO MyParent
SELECT TOP (3000) Id, Id AS ParentId
FROM MyChild
ORDER BY Id

SET STATISTICS IO ON;
SET STATISTICS TIME ON;

SELECT gc.Id       AS gcId,
       gc.ParentId AS gcpId,
       c.Id        AS cId,
       c.ParentId  AS cpId,
       p.Id        AS pId
FROM   MyGrandChild AS gc
       LEFT OUTER JOIN MyChild AS c
         ON c.[Id] = gc.[ParentId]
       LEFT OUTER JOIN MyParent AS p
         ON p.[Id] = c.[ParentId]

SELECT gc.Id       AS gcId,
       gc.ParentId AS gcpId,
       c.Id        AS cId,
       c.ParentId  AS cpId,
       p.Id        AS pId
FROM   MyGrandChild AS gc
       LEFT OUTER JOIN( MyChild AS c
                        LEFT OUTER JOIN MyParent AS p
                          ON p.[Id] = c.[ParentId])
         ON c.[Id] = gc.[ParentId] 

O que dá planos

insira a descrição da imagem aqui

Para mim, a consulta 1 teve um tempo decorrido de 108 ms vs 1.163 ms para a consulta 2.

Consulta 1

Table 'Worktable'. Scan count 0, logical reads 0 
Table 'MyChild'. Scan count 0, logical reads 9196
Table 'MyGrandChild'. Scan count 1, logical reads 7
Table 'MyParent'. Scan count 1, logical reads 5

Consulta 2

Table 'MyParent'. Scan count 1, logical reads 15000
Table 'MyChild'. Scan count 0, logical reads 9000 
Table 'MyGrandChild'. Scan count 1, logical reads 7

Portanto, pode-se presumir provisoriamente que a primeira sintaxe ("não aninhada") é potencialmente benéfica, pois permite que mais pedidos de associação em potencial sejam considerados, mas eu não fiz testes exaustivos o suficiente para ter muita confiança nisso como regra geral.

Pode ser perfeitamente possível criar exemplos de contador em que a Consulta 2 tenha um desempenho melhor. Experimente os dois e veja os planos de execução.

Martin Smith
fonte
-1

não existe esse tipo de JOIN chamado "Junção aninhada". é uma outra variação de escrita que o JOIN pode ser para fins de conveniência de legibilidade. você pode vê-los como "Subconsultas" apenas para fins de entendimento.

se você está mais preocupado com a legibilidade do código, minha opinião é que é a escolha do indivíduo com o que eles são conferíveis.

E se você estiver preocupado com o desempenho da consulta, e a dica "Forçar JOIN ORDER ORDER" não for usada na consulta, não importa se a consulta foi escrita com "Junção Aninhada" ou Todas "Junção Externa". O servidor SQL fornece a ordem com base no custo da junção de duas tabelas e / ou resultado. O SQL Server faz o JOIN entre dois conjuntos de dados por vez apenas.

de fato, imagine que, na segunda maneira, "junção aninhada", se o servidor SQL decidir fazer a segunda parte, "MyChild AS c LEFT OUTER JUNTO AO MyParent AS p ON p. [id] = c. [ParentId]" e essas tabelas acontecem ter linhas que serão descartadas em NEXT LEFT JOIN. nesse caso, o SQL Server gastou recursos desnecessários executando o OUTER JOIN esses dois e passando esse resultado para o próximo JOIN.

você também pode procurar uma pergunta semelhante e responder adequadamente aqui. Entendendo a sintaxe 'Junta aninhada'

Anup Shah
fonte
11
Por que, então, eles produzem planos de consulta diferentes sem usar a FORCE JOIN ORDERdica?
10243 Matthew
sem essa dica, não podemos garantir a ordem JOIN e, como você vê o plano de execução diferente que prova isso. por exemplo, na primeira maneira, o servidor SQL "usando todas as associações externas" pode estar executando qualquer um destes dois. primeiro "MyChild + MyGrandChild" e depois junte-se a "MyParent". Ou Primeiro, "MyChild + MyParent" e, em seguida, cadastre-se em "MyGrandChild".
Anup Shah