Eu quero fazer uma junção externa completa no MySQL. Isso é possível? Uma junção externa completa é suportada pelo MySQL?
sql
mysql
join
outer-join
full-outer-join
Spencer
fonte
fonte
Respostas:
Você não possui JOYS FULL no MySQL, mas com certeza pode imitá-los .
Para um código SAMPLE transcrito desta questão SO, você tem:
com duas tabelas t1, t2:
A consulta acima funciona para casos especiais em que uma operação FULL OUTER JOIN não produziria nenhuma linha duplicada. A consulta acima depende do
UNION
operador set para remover linhas duplicadas introduzidas pelo padrão de consulta. Podemos evitar a introdução de linhas duplicadas usando um padrão anti-junção para a segunda consulta e, em seguida, usar um operador de conjunto UNION ALL para combinar os dois conjuntos. No caso mais geral, onde um JOGO EXTERNO CHEIO retornaria linhas duplicadas, podemos fazer isso:fonte
(SELECT ... FROM tbl1 LEFT JOIN tbl2 ...) UNION ALL (SELECT ... FROM tbl1 RIGHT JOIN tbl2 ... WHERE tbl1.col IS NULL)
t1
et2
, a consulta nesta resposta retornará um conjunto de resultados que emula a junção externa completa . Porém, no caso mais geral, por exemplo, a lista SELECT não contém colunas / expressões suficientes para tornar as linhas retornadas únicas; esse padrão de consulta é insuficiente para reproduzir o conjunto que seria produzido por aFULL OUTER JOIN
. Para obter uma emulação mais fiel, precisaríamos de umUNION ALL
operador set e uma das consultas precisaria de um padrão anti-junção . O comentário de Pavle Lekic (acima) fornece o padrão de consulta correto .A resposta que Pablo Santa Cruz deu está correta; no entanto, caso alguém tropece nesta página e deseje mais esclarecimentos, aqui está um detalhamento detalhado.
Tabelas de exemplo
Suponha que tenhamos as seguintes tabelas:
Junções internas
Uma junção interna, assim:
Nos levaria apenas os registros que aparecem nas duas tabelas, assim:
As junções internas não têm uma direção (como esquerda ou direita) porque são explicitamente bidirecionais - exigimos uma correspondência dos dois lados.
Junções externas
As junções externas, por outro lado, são para localizar registros que podem não ter uma correspondência na outra tabela. Como tal, você deve especificar qual lado da associação pode ter um registro ausente.
LEFT JOIN
eRIGHT JOIN
são abreviação deLEFT OUTER JOIN
eRIGHT OUTER JOIN
; Usarei seus nomes completos abaixo para reforçar o conceito de junções externas versus junções internas.Junção externa esquerda
Uma junção externa esquerda, assim:
... nos forneceria todos os registros da tabela esquerda, independentemente de eles corresponderem ou não à tabela correta, assim:
Junção externa direita
Uma junção externa direita, assim:
... nos obteria todos os registros da tabela da direita, independentemente de haver ou não uma correspondência na tabela da esquerda, assim:
Junção externa completa
Uma junção externa completa nos forneceria todos os registros de ambas as tabelas, independentemente de terem ou não correspondência na outra tabela, com NULLs nos dois lados onde não há correspondência. O resultado ficaria assim:
No entanto, como Pablo Santa Cruz apontou, o MySQL não suporta isso. Podemos emular isso fazendo uma UNIÃO de uma junção esquerda e uma junção direita, assim:
Você pode pensar
UNION
no significado de "executar ambas as consultas e empilhar os resultados uns sobre os outros"; algumas das linhas virão da primeira consulta e outras da segunda.Deve-se notar que a
UNION
no MySQL eliminará duplicatas exatas: Tim apareceria nas duas consultas aqui, mas o resultado daUNION
única lista dele uma vez. Meu colega de guru de banco de dados considera que esse comportamento não deve ser invocado. Para ser mais explícito, podemos adicionar umaWHERE
cláusula à segunda consulta:Por outro lado, se você quiser ver duplicatas por algum motivo, poderá usar
UNION ALL
.fonte
FULL OUTER JOIN
. Não há nada errado em fazer consultas dessa maneira e usar o UNION para remover essas duplicatas. Mas, para realmente replicar umFULL OUTER JOIN
, precisamos de uma das consultas para ser uma anti-junção.UNION
operação removerá essas duplicatas; mas também remove TODAS as linhas duplicadas, incluindo linhas duplicadas que seriam retornadas por uma junção externa completa. Para emulara FULL JOIN b
, o padrão correto é(a LEFT JOIN b) UNION ALL (b ANTI JOIN a)
.O uso de uma
union
consulta removerá duplicatas, e isso é diferente do comportamento defull outer join
nunca remover duplicatas:Este é o resultado esperado de
full outer join
:Este é o resultado do uso
left
eright Join
comunion
:[SQL Fiddle]
Minha consulta sugerida é:
Resultado da consulta acima igual ao resultado esperado:
[SQL Fiddle]
Eu decidi adicionar outra solução que vem da
full outer join
visualização e matemática, não é melhor que acima, mas mais legível:[SQL Fiddle]
fonte
FULL OUTER JOIN
. Esta postagem no blog também explica bem - para citar o Método 2: "Isso manipula linhas duplicadas corretamente e não inclui nada que não deveria. É necessário usar UNION ALL em vez de UNION simples, o que eliminaria as duplicatas que eu quero Isso pode ser significativamente mais eficiente em grandes conjuntos de resultados, pois não há necessidade de classificar e remover duplicatas ".O MySql não possui sintaxe FULL-OUTER-JOIN. Você deve emular executando ESQUERDA JOIN e DIREITA JOIN da seguinte maneira:
Mas o MySql também não possui uma sintaxe RIGHT JOIN. De acordo com a simplificação da junção externa do MySql , a junção direita é convertida na junção esquerda equivalente alternando t1 e t2 na cláusula
FROM
eON
na consulta. Assim, o MySql Query Optimizer converte a consulta original no seguinte -Agora, não há nenhum problema em escrever a consulta original como está, mas digamos que se você tiver predicados como a cláusula WHERE, que é um predicado antes da junção ou um predicado AND na
ON
cláusula, que é um predicado durante a junção , então você pode querer dar uma olhada no diabo; que está em detalhes.O otimizador de consultas do MySql rotineiramente verifica os predicados se eles são nulos . Agora, se você tiver feito o DIREITO JOIN, mas com o predicado WHERE na coluna de t1, poderá correr o risco de encontrar um cenário rejeitado por nulo .
Por exemplo, a consulta a seguir -
é traduzido para o seguinte pelo Query Optimizer-
Portanto, a ordem das tabelas mudou, mas o predicado ainda é aplicado a t1, mas agora está na cláusula 'ON'. Se t1.col1 for definido como
NOT NULL
coluna, essa consulta será rejeitada por nulo .Qualquer junção externa (esquerda, direita, cheia) que seja rejeitada por nulo é convertida em uma junção interna pelo MySql.
Portanto, os resultados que você pode esperar podem ser completamente diferentes do que o MySql está retornando. Você pode pensar que é um bug com o RIGHT JOIN do MySql, mas isso não está certo. É assim que o otimizador de consultas do MySql funciona. Portanto, o desenvolvedor responsável deve prestar atenção a essas nuances quando estiver construindo a consulta.
fonte
No SQLite, você deve fazer isso:
fonte
Nenhuma das respostas acima está realmente correta, porque elas não seguem a semântica quando há valores duplicados.
Para uma consulta como (desta duplicata ):
O equivalente correto é:
Se você precisar que isso funcione com
NULL
valores (o que também pode ser necessário), use oNULL
operador de comparação -safe, em<=>
vez de=
.fonte
FULL OUTER JOIN
sempre que aname
coluna for nula. Aunion all
consulta com padrão anti-junção deve reproduzir o comportamento da junção externa corretamente, mas qual solução é mais apropriada depende do contexto e das restrições ativas nas tabelas.union all
, mas essa resposta perde um padrão de anti-junção na primeira ou na segunda consulta que manterá as duplicatas existentes, mas impede a adição de novas. Dependendo do contexto, outras soluções (como esta) podem ser mais apropriadas.Consulta do shA.t modificada para maior clareza:
fonte
Você pode fazer o seguinte:
fonte
o que você disse sobre a solução de junção cruzada ?
fonte
select (select count(*) from t1) * (select count(*) from t2))
linhas no conjunto de resultados.fonte
Também é possível, mas você deve mencionar os mesmos nomes de campo em select.
fonte
Corrijo a resposta e os trabalhos incluem todas as linhas (com base na resposta de Pavle Lekic)
fonte
tablea
que não possuem correspondênciatableb
e vice-versa. Você tentaUNION ALL
, o que só funcionaria se essas duas tabelas tivessem colunas ordenadas equivalentemente, o que não é garantido.Responda:
Pode ser recriado da seguinte maneira:
O uso de uma resposta UNION ou UNION ALL não cobre o caso de borda em que as tabelas base têm entradas duplicadas.
Explicação:
Há um caso extremo que uma UNION ou UNION ALL não pode cobrir. Nós não podemos testar isso no mysql, pois ele não suporta JOINS EXTERIORES COMPLETOS, mas podemos ilustrar isso em um banco de dados que o suporta:
A solução UNION:
Dá uma resposta incorreta:
A solução UNION ALL:
Também está incorreto.
Considerando que esta consulta:
Fornece o seguinte:
A ordem é diferente, mas corresponde à resposta correta.
fonte
UNION ALL
solução. Além disso, ele apresenta uma soluçãoUNION
que seria mais lenta em grandes tabelas de origem devido à deduplicação necessária. Finalmente, não seria compilado, porque o campoid
não existe na subconsultatmp
.UNION ALL
incorretamente : "A solução: ... Também está incorreta." O código que você apresenta deixa de fora a exclusão de interseção da join-right (where t1.id1 is null
) que deve ser fornecida noUNION ALL
. Ou seja, sua solução supera todas as outras, somente quando uma dessas outras soluções é implementada incorretamente. Sobre "fofura", ponto em questão. Isso foi gratuito, minhas desculpas.O padrão SQL diz
full join on
éinner join on
linhasunion all
ímpares linhas da tabela esquerda estendidos por nulosunion all
linhas da tabela direita estendida por valores nulos. Ou seja,inner join on
linhasunion all
linhas,left join on
mas nãoinner join on
union all
linhas,right join on
mas nãoinner join on
.Ou seja,
left join on
linhasunion all
right join on
linhas forainner join on
. Ou, se você souber que seuinner join on
resultado não pode ter nulo em uma coluna específica da tabela à direita, "right join on
linhas que nãoinner join on
estão" são linhasright join on
com aon
condição estendida porand
essa colunais null
.Ou seja, linhas igualmente
right join on
union all
apropriadasleft join on
.De Qual é a diferença entre "INNER JOIN" e "OUTER JOIN"? :
fonte