Je veux faire une jointure externe complète dans MySQL. Est-ce possible? Une jointure externe complète est-elle prise en charge par MySQL?
sql
mysql
join
outer-join
full-outer-join
Spencer
la source
la source
Réponses:
Vous n'avez pas FULL JOINS sur MySQL, mais vous pouvez certainement les émuler .
Pour un exemple de code transcrit à partir de cette question SO, vous avez:
avec deux tables t1, t2:
La requête ci-dessus fonctionne pour les cas spéciaux où une opération FULL OUTER JOIN ne produira pas de lignes en double. La requête ci-dessus dépend de l'
UNION
opérateur set pour supprimer les lignes en double introduites par le modèle de requête. Nous pouvons éviter d'introduire des lignes en double en utilisant un modèle anti-jointure pour la deuxième requête, puis utiliser un opérateur d'ensemble UNION ALL pour combiner les deux ensembles. Dans le cas plus général, où une FULL OUTER JOIN renverrait des lignes en double, nous pouvons le faire:la source
(SELECT ... FROM tbl1 LEFT JOIN tbl2 ...) UNION ALL (SELECT ... FROM tbl1 RIGHT JOIN tbl2 ... WHERE tbl1.col IS NULL)
t1
ett2
, la requête dans cette réponse renvoie un jeu de résultats qui émule FULL OUTER JOIN. Mais dans le cas plus général, par exemple, la liste SELECT ne contient pas suffisamment de colonnes / expressions pour rendre les lignes retournées uniques, alors ce modèle de requête est insuffisant pour reproduire l'ensemble qui serait produit par aFULL OUTER JOIN
. Pour obtenir une émulation plus fidèle, nous aurions besoin d'unUNION ALL
opérateur d'ensemble et l'une des requêtes aurait besoin d'un modèle anti-jointure . Le commentaire de Pavle Lekic (ci-dessus) donne le modèle de requête correct .La réponse que Pablo Santa Cruz a donnée est correcte; cependant, au cas où quelqu'un serait tombé sur cette page et voudrait plus de précisions, voici une ventilation détaillée.
Exemples de tableaux
Supposons que nous ayons les tableaux suivants:
Jointures internes
Une jointure interne, comme ceci:
Nous obtiendrait seulement les enregistrements qui apparaissent dans les deux tableaux, comme ceci:
Les jointures internes n'ont pas de direction (comme gauche ou droite) car elles sont explicitement bidirectionnelles - nous avons besoin d'une correspondance des deux côtés.
Jointures externes
Les jointures externes, d'autre part, servent à rechercher des enregistrements qui peuvent ne pas avoir de correspondance dans l'autre table. En tant que tel, vous devez spécifier de quel côté de la jointure est autorisé à avoir un enregistrement manquant.
LEFT JOIN
etRIGHT JOIN
sont un raccourci pourLEFT OUTER JOIN
etRIGHT OUTER JOIN
; J'utiliserai leurs noms complets ci-dessous pour renforcer le concept de jointures externes vs jointures internes.Jointure externe gauche
Une jointure externe gauche, comme ceci:
... nous obtiendrait tous les enregistrements de la table de gauche, qu'ils aient ou non une correspondance dans la table de droite, comme ceci:
Jointure extérieure droite
Une jointure externe droite, comme ceci:
... nous obtiendrait tous les enregistrements de la table de droite, qu'ils aient ou non une correspondance dans la table de gauche, comme ceci:
Jointure externe complète
Une jointure externe complète nous donnerait tous les enregistrements des deux tables, qu'ils aient ou non une correspondance dans l'autre table, avec des valeurs NULL des deux côtés où il n'y a pas de correspondance. Le résultat ressemblerait à ceci:
Cependant, comme l'a souligné Pablo Santa Cruz, MySQL ne le prend pas en charge. Nous pouvons l'imiter en faisant UNION d'une jointure gauche et d'une jointure droite, comme ceci:
Vous pouvez penser à un
UNION
qui signifie «exécuter ces deux requêtes, puis empiler les résultats les uns sur les autres»; certaines des lignes proviendront de la première requête et d'autres de la seconde.Il convient de noter qu'un
UNION
dans MySQL éliminera les doublons exacts: Tim apparaîtrait dans les deux requêtes ici, mais le résultat de laUNION
seule liste une fois. Mon collègue gourou de la base de données estime que ce comportement ne doit pas être invoqué. Donc, pour être plus explicite, nous pourrions ajouter uneWHERE
clause à la deuxième requête:D'autre part, si vous vouliez voir des doublons pour une raison quelconque, vous pouvez utiliser
UNION ALL
.la source
FULL OUTER JOIN
. Il n'y a rien de mal à faire des requêtes de cette façon et à utiliser UNION pour supprimer ces doublons. Mais pour vraiment répliquer unFULL OUTER JOIN
, nous avons besoin que l'une des requêtes soit un anti-jointure.UNION
opération supprimera ces doublons; mais il supprime également TOUTES les lignes en double, y compris les lignes en double qui seraient renvoyées par une FULL OUTER JOIN. Pour émulera FULL JOIN b
, le motif correct est(a LEFT JOIN b) UNION ALL (b ANTI JOIN a)
.L'utilisation d'une
union
requête supprimera les doublons, ce qui est différent du comportementfull outer join
qui ne supprime jamais les doublons:C'est le résultat attendu de
full outer join
:C'est le résultat de l'utilisation
left
etright Join
avecunion
:[SQL Fiddle]
Ma requête suggérée est:
Résultat de la requête ci-dessus identique au résultat attendu:
[SQL Fiddle]
J'ai décidé d'ajouter une autre solution qui vient de la
full outer join
visualisation et des mathématiques, ce n'est pas mieux que ci-dessus mais plus lisible:[SQL Fiddle]
la source
FULL OUTER JOIN
. Ce billet de blog l' explique également bien - pour citer la méthode 2: «Cela gère correctement les lignes en double et n'inclut rien de ce qu'il ne devrait pas. Il est nécessaire d'utiliser UNION ALL au lieu de plain UNION, ce qui éliminerait les doublons que je veux Cela peut être nettement plus efficace sur les grands ensembles de résultats, car il n'est pas nécessaire de trier et de supprimer les doublons. "MySql n'a pas de syntaxe FULL-OUTER-JOIN. Vous devez émuler en faisant à la fois LEFT JOIN et RIGHT JOIN comme suit-
Mais MySql n'a pas non plus de syntaxe RIGHT JOIN. Selon la simplification de la jointure externe de MySql , la jointure droite est convertie en la jointure gauche équivalente en commutant les t1 et t2 dans la clause
FROM
andON
de la requête. Ainsi, l'optimiseur de requête MySql traduit la requête d'origine en ce qui suit -Maintenant, il n'y a aucun mal à écrire la requête d'origine telle quelle, mais dites que si vous avez des prédicats comme la clause WHERE, qui est un prédicat avant jointure ou un prédicat AND sur la
ON
clause, qui est un prédicat pendant la jointure , alors vous pourrait vouloir jeter un oeil au diable; ce qui est en détail.L'optimiseur de requêtes MySql vérifie régulièrement les prédicats s'ils sont rejetés par null . Maintenant, si vous avez effectué le RIGHT JOIN, mais avec le prédicat WHERE sur la colonne de t1, vous risquez de courir dans un scénario rejeté par null .
Par exemple, la requête suivante -
est traduit comme suit par l'Optimiseur de requête -
Ainsi, l'ordre des tables a changé, mais le prédicat est toujours appliqué à t1, mais t1 est maintenant dans la clause 'ON'. Si t1.col1 est défini comme
NOT NULL
colonne, cette requête sera rejetée par null .Toute jointure externe (gauche, droite, pleine) qui est rejetée par null est convertie en jointure interne par MySql.
Ainsi, les résultats que vous attendez peuvent être complètement différents de ce que le MySql renvoie. Vous pourriez penser que c'est un bug avec RIGHT JOIN de MySql, mais ce n'est pas correct. C'est juste comment fonctionne l'optimiseur de requêtes MySql. Le développeur en charge doit donc faire attention à ces nuances lors de la construction de la requête.
la source
Dans SQLite, vous devez faire ceci:
la source
Aucune des réponses ci-dessus n'est réellement correcte, car elles ne suivent pas la sémantique lorsqu'il y a des valeurs dupliquées.
Pour une requête telle que (à partir de ce doublon ):
L'équivalent correct est:
Si vous avez besoin que cela fonctionne avec des
NULL
valeurs (qui peuvent également être nécessaires), utilisez plutôt l'NULL
opérateur de comparaison -safe .<=>
=
la source
FULL OUTER JOIN
chaque fois que laname
colonne est nulle. Launion all
requête avec un motif anti-jointure doit reproduire correctement le comportement de jointure externe, mais la solution la plus appropriée dépend du contexte et des contraintes actives sur les tables.union all
, mais cette réponse manque un modèle anti-jointure dans la première ou la deuxième requête qui conservera les doublons existants mais empêche d'en ajouter de nouveaux. Selon le contexte, d'autres solutions (comme celle-ci) pourraient être plus appropriées.Modification de la requête de shA.t pour plus de clarté:
la source
Vous pouvez effectuer les opérations suivantes:
la source
que pensez-vous de la solution de jointure croisée
la source
select (select count(*) from t1) * (select count(*) from t2))
lignes dans l'ensemble de résultats.la source
C'est également possible, mais vous devez mentionner les mêmes noms de champs dans select.
la source
Je corrige la réponse, et les travaux incluent toutes les lignes (basé sur la réponse de Pavle Lekic)
la source
tablea
qui n'ont pas de correspondancetableb
et vice versa. Vous essayez deUNION ALL
, ce qui ne fonctionnerait que si ces deux tables ont des colonnes ordonnées de manière équivalente, ce qui n'est pas garanti.Réponse:
Peut être recréé comme suit:
L'utilisation d'une réponse UNION ou UNION ALL ne couvre pas le cas de bord où les tables de base ont des entrées en double.
Explication:
Il y a un cas de bord qu'un UNION ou UNION ALL ne peut pas couvrir. Nous ne pouvons pas tester cela sur mysql car il ne prend pas en charge FULL OUTER JOINs, mais nous pouvons illustrer cela sur une base de données qui le prend en charge:
La solution UNION:
Donne une réponse incorrecte:
La solution UNION ALL:
Est également incorrect.
Attendu que cette requête:
Donne ce qui suit:
L'ordre est différent, mais correspond sinon à la bonne réponse.
la source
UNION ALL
solution. En outre, il présente une solutionUNION
qui serait plus lente sur les grandes tables source en raison de la déduplication requise. Enfin, il ne serait pas compilé, car le champid
n'existe pas dans la sous-requêtetmp
.UNION ALL
solution: ... est également incorrecte." Le code que vous présentez exclut l'intersection-exclusion de la jointure droite (where t1.id1 is null
) qui doit être fournie dans leUNION ALL
. Autrement dit, votre solution l'emporte sur toutes les autres, uniquement lorsque l'une de ces autres solutions est incorrectement implémentée. Sur la "gentillesse", point pris. C'était gratuit, mes excuses.Le standard SQL dit
full join on
estinner join on
lignesunion all
inégalées lignes de la table gauche prolongée par nullsunion all
lignes de table droite étendue par nulls. C'est-à-dire desinner join on
rangées deunion all
rangéesleft join on
mais pas deinner join on
union all
rangéesright join on
mais pasinner join on
.C'est-à-dire que les
left join on
rangéesunion all
right join on
ne sont pas dedansinner join on
. Ou si vous savez que votreinner join on
résultat ne peut pas avoir la valeur null dans une colonne de tableau de droite particulière, alors "lesright join on
lignes qui neinner join on
sont pas " sont des lignesright join on
avec laon
condition étendue parand
cette colonneis null
.C'est-à-dire
right join on
union all
desleft join on
lignes appropriées de manière similaire .De Quelle est la différence entre «INNER JOIN» et «OUTER JOIN»? :
la source