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?
Vous n’avez pas de FULL JOINS sur MySQL, mais vous pouvez certainement les émuler .
Pour un échantillon de code transcrit à partir de this SO question vous avez:
avec deux tables t1, t2:
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
La requête ci-dessus fonctionne dans les cas spéciaux où une opération FULL OUTER JOIN ne produirait aucune ligne en double. La requête ci-dessus dépend de l'opérateur UNION
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 en utilisant un opérateur UNION ALL pour combiner les deux ensembles. Dans le cas plus général, où FULL OUTER JOIN renvoie des lignes en double, nous pouvons procéder comme suit:
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION ALL
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
WHERE t1.id IS NULL
La réponse que Pablo Santa Cruz a donnée est correcte; Cependant, au cas où quelqu'un trébucherait sur cette page et voudrait plus de précisions, voici une ventilation détaillée.
Supposons que nous ayons les tables suivantes:
-- t1
id name
1 Tim
2 Marta
-- t2
id name
1 Tim
3 Katarina
Une jointure interne, comme ceci:
SELECT *
FROM `t1`
INNER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
Nous obtiendrions uniquement les enregistrements qui apparaissent dans les deux tableaux, comme ceci:
1 Tim 1 Tim
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.
Les jointures externes, quant à elles, permettent de rechercher des enregistrements qui ne correspondent peut-être pas à l'autre table. En tant que tel, vous devez spécifier quel côté de la jointure est autorisé à avoir un enregistrement manquant.
LEFT JOIN
et RIGHT JOIN
sont un raccourci pour LEFT OUTER JOIN
et RIGHT OUTER JOIN
; Je vais utiliser leurs noms complets ci-dessous pour renforcer le concept de jointure externe par opposition à une jointure interne.
Une jointure externe gauche, comme ceci:
SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
... nous obtiendrait tous les enregistrements de la table de gauche, qu'ils aient ou non une correspondance dans la table de droite, comme ceci:
1 Tim 1 Tim
2 Marta NULL NULL
Une jointure externe droite, comme ceci:
SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
... nous obtiendrait tous les enregistrements de la table de droite, qu'ils aient ou non une correspondance dans la table de gauche, comme ceci:
1 Tim 1 Tim
NULL NULL 3 Katarina
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 NULL des deux côtés sans correspondance. Le résultat ressemblerait à ceci:
1 Tim 1 Tim
2 Marta NULL NULL
NULL NULL 3 Katarina
Cependant, comme l'a souligné Pablo Santa Cruz, MySQL ne le supporte pas. Nous pouvons l'imiter en faisant une UNION composée de gauche et de droite, comme ceci:
SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`
UNION
SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
Vous pouvez penser à un UNION
comme signifiant "exécutez ces deux requêtes, puis empilez 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 est à noter qu’une variable UNION
dans MySQL éliminera les doublons exacts: Tim apparaîtrait dans les deux requêtes ici, mais le résultat de la UNION
ne le liste qu’une seule fois. Mon collègue gourou de la base de données pense qu'il ne faut pas compter sur ce comportement. Donc, pour être plus explicite à ce sujet, nous pourrions ajouter une clause WHERE
à la deuxième requête:
SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`
UNION
SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`
WHERE `t1`.`id` IS NULL;
D'autre part, si vous vouliez voir les doublons pour une raison quelconque, vous pourriez utiliser UNION ALL
.
L'utilisation d'une requête union
supprimera les doublons, ce qui est différent du comportement de _full outer join
_ qui n'élimine jamais les doublons:
_[Table: t1] [Table: t2]
value value
------- -------
1 1
2 2
4 2
4 5
_
C'est le résultat attendu de _full outer join
_:
_value | value
------+-------
1 | 1
2 | 2
2 | 2
Null | 5
4 | Null
4 | Null
_
Ceci est le résultat de l'utilisation de left
et _right Join
_ avec union
:
_value | value
------+-------
Null | 5
1 | 1
2 | 2
4 | Null
_
Ma requête suggérée est:
_select
t1.value, t2.value
from t1
left outer join t2
on t1.value = t2.value
union all -- Using `union all` instead of `union`
select
t1.value, t2.value
from t2
left outer join t1
on t1.value = t2.value
where
t1.value IS NULL
_
Résultat de la requête ci-dessus identique à celui attendu:
_value | value
------+-------
1 | 1
2 | 2
2 | 2
4 | NULL
4 | NULL
NULL | 5
_
@ Steve Chambers : [De commentaires, avec beaucoup merci!]
Remarque: Il peut s'agir de la meilleure solution, tant pour l'efficacité que pour générer les mêmes résultats qu'un _FULL OUTER JOIN
_. Cet article de blog explique aussi bien - pour citer la méthode 2: "Ceci gère correctement les lignes en double et n'inclut pas ce qu'il ne devrait pas. Il est nécessaire d'utiliser _UNION ALL
_ au lieu de plainUNION
, ce qui éliminerait les doublons que je veux conserver. Cela peut s'avérer beaucoup plus efficace sur les ensembles de résultats volumineux, car il n'est pas nécessaire de trier ni de supprimer les doublons. "
J'ai décidé d'ajouter une autre solution issue de la visualisation _full outer join
_ et des mathématiques, ce n'est pas mieux que ci-dessus mais plus lisible:
Une jointure externe complète signifie _
(t1 ∪ t2)
_: tout en _t1
_ ou en _t2
_
_(t1 ∪ t2) = (t1 ∩ t2) + t1_only + t2_only
_: tous les deux dans _t1
_ et _t2
_ plus tous dans _t1
_ qui ne sont pas dans _t2
_ et plus tous dans _t2
_ qui ne sont pas dans _t1
_:
_-- (t1 ∩ t2): all in both t1 and t2
select t1.value, t2.value
from t1 join t2 on t1.value = t2.value
union all -- And plus
-- all in t1 that not exists in t2
select t1.value, null
from t1
where not exists( select 1 from t2 where t2.value = t1.value)
union all -- and plus
-- all in t2 that not exists in t1
select null, t2.value
from t2
where not exists( select 1 from t1 where t2.value = t1.value)
_
Dans SQLite, vous devriez faire ceci:
SELECT *
FROM leftTable lt
LEFT JOIN rightTable rt ON lt.id = rt.lrid
UNION
SELECT lt.*, rl.* -- To match column set
FROM rightTable rt
LEFT JOIN leftTable lt ON lt.id = rt.lrid
Aucune des réponses ci-dessus n'est en réalité correcte, car elles ne suivent pas la sémantique lorsqu'il existe des valeurs dupliquées.
Pour une requête telle que (de this duplicate ):
SELECT * FROM t1 FULL OUTER JOIN t2 ON t1.Name = t2.Name;
L'équivalent correct est:
SELECT t1.*, t2.*
FROM (SELECT name FROM t1 UNION -- This is intentionally UNION to remove duplicates
SELECT name FROM t2
) n LEFT JOIN
t1
ON t1.name = n.name LEFT JOIN
t2
ON t2.name = n.name;
Si vous avez besoin que cela fonctionne avec les valeurs NULL
(qui peuvent également l'être), utilisez l'opérateur de comparaison sécurisé NULL
-, <=>
plutôt que =
.
MySql n'a pas de syntaxe FULL-OUTER-JOIN. Vous devez émuler en faisant à la fois LEFT JOIN et RIGHT JOIN comme suit-
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
Mais MySql n’a pas non plus de syntaxe RIGHT JOIN. Selon MySql simplification de la jointure externe , la jointure droite est convertie en jointure gauche équivalente en commutant t1 et t2 dans les clauses FROM
et ON
de la requête. Ainsi, l’optimiseur de requêtes MySql traduit la requête initiale en ce qui suit -
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT * FROM t2
LEFT JOIN t1 ON t2.id = t1.id
Maintenant, il n’ya aucun mal à écrire la requête originale telle quelle, mais disons si vous avez des prédicats comme la clause WHERE, qui est un avant-join prédicat ou AND sur la clause ON
, qui est un pendant une jointure = prédicat, alors vous voudrez peut-être jeter un regard sur le diable; qui est dans les détails.
L’optimiseur de requêtes MySql vérifie régulièrement les prédicats s’ils sont , null-rejetés . Maintenant, si vous avez fait la jointure droite, mais avec le prédicat WHERE sur la colonne de t1, vous courez le risque de tomber dans un scénario null-rejeté .
Par exemple, la requête suivante -
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
WHERE t1.col1 = 'someValue'
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
WHERE t1.col1 = 'someValue'
est traduit en suivant par l’optimiseur de requêtes
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
WHERE t1.col1 = 'someValue'
UNION
SELECT * FROM t2
LEFT JOIN t1 ON t2.id = t1.id
WHERE t1.col1 = 'someValue'
Donc, 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 en tant que colonne NOT NULL
, cette requête sera null-rejetée .
Toute jointure externe (gauche, droite, complète) qui est null-rejeté est convertie en jointure interne par MySql.
Ainsi, les résultats que vous pouvez attendre pourraient être complètement différents de ce que MySQL renvoie. Vous pourriez penser que c'est un bogue avec RIGHT JOIN de MySQL, mais ce n'est pas correct. C'est exactement comment fonctionne l'optimiseur de requêtes MySql. Le développeur responsable doit donc tenir compte de ces nuances lors de la construction de la requête.
Vous pouvez faire ce qui suit:
(SELECT
*
FROM
table1 t1
LEFT JOIN
table2 t2 ON t1.id = t2.id
WHERE
t2.id IS NULL)
UNION ALL
(SELECT
*
FROM
table1 t1
RIGHT JOIN
table2 t2 ON t1.id = t2.id
WHERE
t1.id IS NULL);
Requête modifiée de shA.t pour plus de clarté:
-- t1 left join t2
SELECT t1.value, t2.value
FROM t1 LEFT JOIN t2 ON t1.value = t2.value
UNION ALL -- include duplicates
-- t1 right exclude join t2 (records found only in t2)
SELECT t1.value, t2.value
FROM t1 RIGHT JOIN t2 ON t1.value = t2.value
WHERE t2.value IS NULL
SELECT
a.name,
b.title
FROM
author AS a
LEFT JOIN
book AS b
ON a.id = b.author_id
UNION
SELECT
a.name,
b.title
FROM
author AS a
RIGHT JOIN
book AS b
ON a.id = b.author_id
qu'avez-vous dit à propos de Cross join solution?
SELECT t1.*, t2.*
FROM table1 t1
INNER JOIN table2 t2
ON 1=1;