Por que a associação de loops aninhados suporta apenas associações à esquerda?

11

No blog de Craig Freedman, Nested Loops Join , ele explica por que a junção de loops aninhados não pode suportar uma junção externa direita:

O problema é que examinamos a tabela interna várias vezes - uma vez para cada linha da junção externa. Podemos encontrar as mesmas linhas internas várias vezes durante essas várias verificações. Em que ponto podemos concluir que uma linha interna específica não se uniu ou não se unirá?

Alguém pode explicar isso de uma maneira realmente simples e educacional?

Isso significa que o loop começa com a tabela externa ( R1) e verifica a interna ( R2)?

Entendo que, para um R1valor que não se associe R2, ele deve ser substituído por um NULLpara que o conjunto de resultados se torne ( NULL, R2). Para mim, parece impossível retornar um R2valor quando R1não se junta, pelo motivo de não saber qual R2valor retornar. Mas não é assim que se explica. Ou é?

De fato, o SQL Server otimiza (e geralmente substitui) RIGHT JOINpor LEFT JOIN, mas a questão é explicar por que é tecnicamente impossível uma lógica de NESTED LOOPS JOINusar / dar suporte RIGHT JOIN.

GordonLiddy
fonte

Respostas:

12

A questão principal aqui é a implementação de uma junção externa, usando loops aninhados, de uma maneira técnica oposta à lógica , onde a tabela interna é acessada através do loop externo e a tabela externa é acessada através do loop interno .

Dadas as tabelas A e B, vamos implementar A LEFT JOIN B.

A
--
1
2

B
_
1
3

Primeiro, vamos fazer da maneira " natural ".

Nós iteramos através de A.
Nós acessamos o registro 1.
Nós iteramos através de B.
Encontramos o registro 1 em B e a saída 1-1 .

Continuamos iterando por A. Acessamos o
registro 2.
Nós iteramos por B.
Não encontramos nenhuma correspondência em B.
Produzimos 2-null .

Agora, vamos fazê-lo da maneira " oposta ".

Nós iteramos através de B.
Nós acessamos o registro 1.
Nós iteramos através de A.
Nós encontramos o registro 1 em A e a saída 1-1 .

Continuamos iterando por B. Acessamos o
registro 3.
Nós iteramos por A.
Não encontramos nenhuma correspondência em A.

Agora lembre-se de que sim A LEFT JOIN B, o que significa que, além de 1-1 , devemos gerar 2-null .
O problema é que, nesse ponto, não temos idéia para quais registros de identificação A já temos uma correspondência (1) e para quais registros não temos (2).


Na verdade, isso pode ser resolvido de várias maneiras, por exemplo, mantendo uma matriz de bits para a tabela A.
Quando um registro A está sendo encontrado como uma correspondência, marcamos na matriz de bits.
No final dos loops aninhados, passamos pela matriz de bits e produzimos e produzimos qualquer registro que não tenha sido marcado.
Isso é obviamente mais complicado que o loop aninhado "natural".

David Markovitz
fonte
13

O que não gosto no artigo vinculado é a afirmação de que "o algoritmo de junção de loop aninhado não suporta o operador de junção lógica da junção direita" .

Embora exista uma limitação, as palavras neste momento são um pouco confusas. Espero que o seguinte explique melhor as coisas:

O algoritmo de junção aninhada aninhada envolve duas tabelas (sejam irrelevantes as tabelas base ou os conjuntos de resultados de operações anteriores) que são denominadas tabela externa e interna e são tratadas de maneira diferente pelo algoritmo (a tabela "externa" é atravessada na externa) loop e a tabela "interna" nos loops internos).

Então, digamos que temos uma associação:

A (some_type) JOIN B

O algoritmo pode ser executado como:

outer-loop-A  nested-loop  inner-loop-B

ou:

outer-loop-B  nested-loop  inner-loop-A

Agora, se ( some_type) for INNERou CROSSjunção, o planejador poderá escolher entre uma das duas formas (com características de desempenho diferentes, dependendo do tamanho dos conjuntos, distribuição de valores das colunas unidas, índices, etc.) Normalmente, a menor tabela será escolhida para ser a tabela "externa" no algoritmo).

Mas quando some_typeé LEFTjoin, ele só pode usar:

outer-loop-A  nested-loop  inner-loop-B

e não

outer-loop-B  nested-loop  inner-loop-A

E como a RIGHTsempre pode ser reescrita como uma LEFTjunção, ela tem a mesma limitação, ao contrário. Para A RIGHT JOIN B(que pode ser reescrito a B LEFT JOIN A), ele só pode usar:

outer-loop-B  nested-loop  inner-loop-A

e não o contrário * .

A mesma limitação aplica-se ao semijoin esquerdo, anti-semijoin esquerdo, semijoin direito e anti-semijoino direito.

A FULLjunção, por outro lado, não pode ser tratada diretamente com um algoritmo de junção de loop aninhado. O artigo explica muito bem (está próximo do fim) como uma junção completa pode ser reescrita (e é pelo otimizador) para uma união de uma junção esquerda e um anti-semijoin esquerdo que pode ser planejado como dois loops aninhados (e um União).

* Como Dudu Markovitz explica em sua resposta, o caminho inverso poderia ser usado, mas apenas se modificássemos o algoritmo de junção de loop aninhado para ter uma estrutura extra e uma etapa extra no final.

ypercubeᵀᴹ
fonte
Bem, isso esclareceu muito. Sua resposta combinada com Dudu M: s explica muito bem!
GordonLiddy