Comment faire une FULL OUTER JOIN dans MySQL?

654

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?

Spencer
la source
2
possible doublon de MySQL Full Outer Join Syntax Error
Joe Stefanelli
4
Cette question a de meilleures réponses
Julio Marins
Méfiez-vous des réponses ici. La norme SQL indique que la jointure complète est la jointure interne sur les lignes union toutes les lignes de table gauche sans correspondance étendues par nulls union toutes les lignes de table droite étendues par nulls. La plupart des réponses ici sont fausses (voir les commentaires) et celles qui ne le sont pas ne gèrent pas le cas général. Même s'il y a beaucoup de votes positifs (injustifiés). (Voir ma réponse.)
philipxy
Qu'en est-il lorsque vous essayez de vous joindre par des clés non primaires / des colonnes groupées? comme j'ai une requête de ventes par état "état", "vend" et une autre de dépenses par état "état", "dépenses", les deux requêtes utilisent le groupe par ("état"). Lorsque je fais l'union entre les jointures gauche et droite entre deux requêtes, j'obtiens quelques lignes avec des ventes mais pas de dépenses, quelques autres avec des dépenses mais pas de ventes, tout jusqu'à ce point, mais j'en reçois également quelques-unes avec les deux vend et dépenses et une colonne "état" répétée ... pas beaucoup de problème mais ne se sent pas bien ...
Jairo Lozano
1
@JairoLozano Les contraintes ne sont pas nécessaires pour interroger. Bien que lorsque les contraintes contiennent des requêtes supplémentaires, renvoyez la réponse souhaitée qui ne le serait pas autrement. Les contraintes n'affectent pas la jointure complète des retours pour les arguments donnés. Le problème que vous décrivez est que la requête que vous avez écrite est incorrecte. (Vraisemblablement l'erreur courante où les gens veulent des jointures, chacune impliquant éventuellement une clé différente, de certaines sous-requêtes, chacune impliquant éventuellement une jointure et / ou une agrégation, mais ils essaient à tort de faire toutes les jointures puis toutes les agrégations ou d'agréger les agrégations précédentes .)
philipxy

Réponses:

669

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:

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 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' UNIONopé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:

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
Pablo Santa Cruz
la source
33
En fait, la chose que vous avez écrite n'est pas correcte. Parce que lorsque vous effectuez un UNION, vous supprimez les doublons, et parfois lorsque vous joignez deux tables différentes, il doit y avoir des doublons.
Pavle Lekic
158
Voici l'exemple correct:(SELECT ... FROM tbl1 LEFT JOIN tbl2 ...) UNION ALL (SELECT ... FROM tbl1 RIGHT JOIN tbl2 ... WHERE tbl1.col IS NULL)
Pavle Lekic
8
Donc, la différence est que je fais une jointure gauche incluse et puis droite exclusive en utilisant UNION ALL
Pavle Lekic
5
et je vois maintenant que vous le dites vous-même, désolé. Peut-être pourriez-vous mettre à jour votre réponse, étant donné qu'il y a ce cas où elle se trompe et que l'UNION ALL va toujours être plus efficace?
ysth
10
@ypercube: S'il n'y a pas de lignes en double dans t1et t2, 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 a FULL OUTER JOIN. Pour obtenir une émulation plus fidèle, nous aurions besoin d'un UNION ALLopé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 .
spencer7593
351

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:

-- t1
id  name
1   Tim
2   Marta

-- t2
id  name
1   Tim
3   Katarina

Jointures internes

Une jointure interne, comme ceci:

SELECT *
FROM `t1`
INNER JOIN `t2` ON `t1`.`id` = `t2`.`id`;

Nous obtiendrait seulement 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.

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 JOINet RIGHT JOINsont un raccourci pour LEFT OUTER JOINetRIGHT 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:

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

Jointure extérieure droite

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

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:

1    Tim   1    Tim
2    Marta NULL NULL
NULL NULL  3    Katarina

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:

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 UNIONqui 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 UNIONdans MySQL éliminera les doublons exacts: Tim apparaîtrait dans les deux requêtes ici, mais le résultat de la UNIONseule 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 une WHEREclause à 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 des doublons pour une raison quelconque, vous pouvez utiliser UNION ALL.

Nathan Long
la source
4
Pour MySQL, vous voulez vraiment éviter d'utiliser UNION au lieu de UNION ALL s'il n'y a pas de chevauchement (voir le commentaire de Pavle ci-dessus). Si vous pouviez ajouter plus d'informations à cet effet dans votre réponse ici, je pense que ce serait la réponse préférée pour cette question car elle est plus approfondie.
Garen
2
La recommandation du "collègue gourou de la base de données" est correcte. En termes de modèle relationnel (tous les travaux théoriques effectués par Ted Codd et Chris Date), une requête de la dernière forme émule une FULL OUTER JOIN, car elle combine deux ensembles distincts, la deuxième requête n'introduit pas de "doublons" ( lignes déjà retournées par la première requête) qui ne seraient pas produites par a 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 un FULL OUTER JOIN, nous avons besoin que l'une des requêtes soit un anti-jointure.
spencer7593
1
@IstiaqueAhmed: le but est d'émuler une opération FULL OUTER JOIN. Nous avons besoin de cette condition dans la deuxième requête pour qu'elle ne renvoie que les lignes qui n'ont pas de correspondance (un modèle anti-jointure.). Sans cette condition, la requête est une jointure externe ... elle renvoie des lignes qui correspondent ainsi que celles sans correspondance. Et les lignes qui correspondent ont déjà été renvoyées par la première requête. Si la deuxième requête renvoie ces mêmes lignes (encore une fois), nous avons des lignes dupliquées et notre résultat ne sera pas équivalent à une FULL OUTER JOIN.
spencer7593
1
@IstiaqueAhmed: Il est vrai qu'une UNIONopé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 émuler a FULL JOIN b, le motif correct est (a LEFT JOIN b) UNION ALL (b ANTI JOIN a).
spencer7593
1
Réponse très concise avec une grande explication. Merci pour cela.
Najeeb
35

L'utilisation d'une unionrequête supprimera les doublons, ce qui est différent du comportement full outer joinqui ne supprime 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

C'est le résultat de l'utilisation leftet right Joinavec union:

value | value
------+-------
Null  | 5 
1     | 1
2     | 2
4     | Null

[SQL Fiddle]

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 au résultat attendu:

value | value
------+-------
1     | 1
2     | 2
2     | 2
4     | NULL
4     | NULL
NULL  | 5

[SQL Fiddle]


@Steve Chambers : [D'après les commentaires, merci beaucoup!]
Remarque: Cela peut être la meilleure solution, à la fois pour l'efficacité et pour générer les mêmes résultats qu'un a 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 à la UNION ALLplace de plain UNION, ce qui éliminerait les doublons que je veux conserver. Cette peut être beaucoup plus efficace sur les grands ensembles de résultats, car il n'est pas nécessaire de trier et de supprimer les doublons. "


J'ai décidé d'ajouter une autre solution qui vient de la full outer joinvisualisation et des mathématiques, ce n'est pas mieux que ci-dessus mais plus lisible:

La jointure externe complète signifie (t1 ∪ t2): tout en t1ou en t2
(t1 ∪ t2) = (t1 ∩ t2) + t1_only + t2_only: tous les deux t1et t2plus tous ceux t1qui ne sont pas t2et plus tous ceux t2qui ne sont pas 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)

[SQL Fiddle]

shA.t
la source
Nous faisons les mêmes temps de remorquage des tâches, s'il y a une sous-requête pour t1 et t2, alors mysql doit faire la même tâche plusieurs fois, n'est-ce pas? Pouvons-nous supprimer cela en utilisant un alias dans cette situation?:
Kabir Hossain
Je vous suggère d'utiliser des tables temporaires;).
shA.t
5
Cette méthode semble être la meilleure solution, à la fois pour l'efficacité et pour générer les mêmes résultats qu'un a 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. "
Steve Chambers du
2
@SteveChambers c'est trop tard, mais merci pour votre commentaire. J'ai ajouté votre commentaire pour ensuite répondre à plus de surbrillance, si vous n'êtes pas d'accord, veuillez le restaurer;).
shA.t
Pas de problème @ shA.t - OMI, cela devrait vraiment avoir plus de votes positifs et / ou être la réponse acceptée.
Steve Chambers
6

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 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 FROMand ONde la requête. Ainsi, l'optimiseur de requête MySql traduit la requête d'origine 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'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 ONclause, 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 . Définition et exemples non rejetés 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 -

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 comme suit par l'Optimiseur de requête -

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'

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.

ARCHE
la source
4

Dans SQLite, vous devez 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
Rami Jamleh
la source
Pouvons-nous l'utiliser? comme: SELECT * FROM leftTable lt LEFT JOIN rightTable rt ON lt.id = rt.lrid UNION SELECT lt. *, rl. * - Pour faire correspondre le jeu de colonnes FROM leftTable lt RIGHT JOIN rightTable rt ON lt.id = rt.lrid ;
Kabir Hossain
oui mais SQLite ne prend pas en charge les jointures droites mais oui dans MYSQL oui
Rami Jamleh
4

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 ):

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 des NULLvaleurs (qui peuvent également être nécessaires), utilisez plutôt l' NULLopérateur de comparaison -safe .<=>=

Gordon Linoff
la source
3
c'est souvent une bonne solution, mais cela peut donner des résultats différents de a FULL OUTER JOINchaque fois que la namecolonne est nulle. La union allrequê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.
fthiella
@fthiella. . . C'est un bon point. J'ai ajusté la réponse.
Gordon Linoff
1
ok, mais l'opérateur de comparaison null-safe fera réussir la jointure, ce qui est différent du comportement de jointure externe complète au cas où vous auriez des noms nuls en t1 et en t2
fthiella
@fthiella. . . Je vais devoir réfléchir à la meilleure façon de procéder. Mais étant donné à quel point la réponse acceptée est fausse, presque tout est plus proche de la bonne réponse. (Cette réponse est tout simplement fausse s'il y a plusieurs touches de chaque côté.)
Gordon Linoff
1
oui, la réponse acceptée est fausse, en tant que solution générale, je pense qu'elle est correcte à utiliser 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.
fthiella
3

Modification de la requête 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 
a20
la source
3

Vous pouvez effectuer les opérations suivantes:

(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);
lolo
la source
1

que pensez-vous de la solution de jointure croisée

SELECT t1.*, t2.*
FROM table1 t1
INNER JOIN table2 t2 
ON 1=1;
Super mario
la source
2
Non, c'est une jointure croisée. Il fera correspondre chaque ligne de t1 à chaque ligne de t2, donnant l'ensemble de toutes les combinaisons possibles, avec des select (select count(*) from t1) * (select count(*) from t2))lignes dans l'ensemble de résultats.
Marc L.
Bien que ce code puisse répondre à la question, fournir un contexte supplémentaire sur la manière et la raison pour laquelle il résout le problème améliorerait la valeur à long terme de la réponse.
Alexander
Quel ajout peut être utile? peut-être en exemple?
Super Mario
0
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
Alex Pliutau
la source
0

C'est également possible, mais vous devez mentionner les mêmes noms de champs dans select.

SELECT t1.name, t2.name FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT t1.name, t2.name FROM t2
LEFT JOIN t1 ON t1.id = t2.id
alamelu
la source
Il s'agit simplement de dupliquer les résultats d'une jointure gauche.
Matthew Read
-1

Je corrige la réponse, et les travaux incluent toutes les lignes (basé sur la réponse de Pavle Lekic)

    (
    SELECT a.* FROM tablea a
    LEFT JOIN tableb b ON a.`key` = b.key
    WHERE b.`key` is null
    )
    UNION ALL
    (
    SELECT a.* FROM tablea a
    LEFT JOIN tableb b ON a.`key` = b.key
    where  a.`key` = b.`key`
    )
    UNION ALL
    (
    SELECT b.* FROM tablea a
    right JOIN tableb b ON b.`key` = a.key
    WHERE a.`key` is null
    );
Rubén Ruíz
la source
Non, il s'agit d'un type de jointure "externe uniquement", qui ne renverra que les lignes tableaqui n'ont pas de correspondance tablebet vice versa. Vous essayez de UNION 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.
Marc L.
ça marche, je crée sur la base de données temp tablea (1,2,3,4,5,6) et tableb (4,5,6,7,8,9) ses lignes ont 3 cols "id", "number" et "name_number" comme texte, et fonctionne en résultat seulement (1,2,3,7,8,9)
Rubén Ruíz
1
Ce n'est pas une jointure externe. Une jointure externe comprend également les membres correspondants.
Marc L.
cette nouvelle phrase a tous les résultats 1,2, ..., 9
Rubén Ruíz
-2

Réponse:

SELECT * FROM t1 FULL OUTER JOIN t2 ON t1.id = t2.id;

Peut être recréé comme suit:

 SELECT t1.*, t2.* 
 FROM (SELECT * FROM t1 UNION SELECT name FROM t2) tmp
 LEFT JOIN t1 ON t1.id = tmp.id
 LEFT JOIN t2 ON t2.id = tmp.id;

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:

 WITH cte_t1 AS
 (
       SELECT 1 AS id1
       UNION ALL SELECT 2
       UNION ALL SELECT 5
       UNION ALL SELECT 6
       UNION ALL SELECT 6
 ),
cte_t2 AS
(
      SELECT 3 AS id2
      UNION ALL SELECT 4
      UNION ALL SELECT 5
      UNION ALL SELECT 6
      UNION ALL SELECT 6
)
SELECT  *  FROM  cte_t1 t1 FULL OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2;

This gives us this answer:

id1  id2
1  NULL
2  NULL
NULL  3
NULL  4
5  5
6  6
6  6
6  6
6  6

La solution UNION:

SELECT  * FROM  cte_t1 t1 LEFT OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2
UNION    
SELECT  * FROM cte_t1 t1 RIGHT OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2

Donne une réponse incorrecte:

 id1  id2
NULL  3
NULL  4
1  NULL
2  NULL
5  5
6  6

La solution UNION ALL:

SELECT  * FROM cte_t1 t1 LEFT OUTER join cte_t2 t2 ON t1.id1 = t2.id2
UNION ALL
SELECT  * FROM  cte_t1 t1 RIGHT OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2

Est également incorrect.

id1  id2
1  NULL
2  NULL
5  5
6  6
6  6
6  6
6  6
NULL  3
NULL  4
5  5
6  6
6  6
6  6
6  6

Attendu que cette requête:

SELECT t1.*, t2.*
FROM (SELECT * FROM t1 UNION SELECT name FROM t2) tmp 
LEFT JOIN t1 ON t1.id = tmp.id 
LEFT JOIN t2 ON t2.id = tmp.id;

Donne ce qui suit:

id1  id2
1  NULL
2  NULL
NULL  3
NULL  4
5  5
6  6
6  6
6  6
6  6

L'ordre est différent, mais correspond sinon à la bonne réponse.

Angelos
la source
C'est mignon, mais dénature la UNION ALLsolution. En outre, il présente une solution UNIONqui serait plus lente sur les grandes tables source en raison de la déduplication requise. Enfin, il ne serait pas compilé, car le champ idn'existe pas dans la sous-requête tmp.
Marc L.
Je n'ai jamais revendiqué la vitesse, et l'OP n'a pas non plus mentionné la vitesse. En supposant que l'UNION ALL (vous ne comptez pas spécifier lequel) et que les deux donnent la bonne réponse, si nous voulions affirmer que l'un est plus rapide, nous aurions besoin de fournir des repères, et ce serait s'écarter de l'OP question.
Angelos
En ce qui concerne l'observation selon laquelle l'ID ne figure pas dans la sous-requête, j'ai corrigé la faute de frappe - merci de l'avoir signalé. Votre allégation de fausses déclarations est vague - si vous pouvez peut-être fournir plus d'informations, je peux y répondre. Sur votre dernière observation sur la gentillesse, je n'ai pas de commentaire, je préfère me concentrer sur la logique du sql.
Angelos
3
Représente faussement: "La UNION ALLsolution: ... 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 le UNION 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.
Marc L.
-3

Le standard SQL dit full join onest inner join onlignes union allinégalées lignes de la table gauche prolongée par nulls union alllignes de table droite étendue par nulls. C'est-à-dire des inner join onrangées de union allrangées left join onmais pas de inner join on union allrangées right join onmais pas inner join on.

C'est-à-dire que les left join onrangées union all right join onne sont pas dedans inner join on. Ou si vous savez que votre inner join onrésultat ne peut pas avoir la valeur null dans une colonne de tableau de droite particulière, alors "les right join onlignes qui ne inner join onsont pas " sont des lignes right join onavec la oncondition étendue par andcette colonne is null.

C'est-à-dire right join on union alldes left join onlignes appropriées de manière similaire .

De Quelle est la différence entre «INNER JOIN» et «OUTER JOIN»? :

(SQL Standard 2006 SQL / Foundation 7.7 Syntaxe Rules 1, General Rules 1 b, 3 c & d, 5 b.)

philipxy
la source