web-dev-qa-db-fra.com

MySQL Left Join ne renvoie pas de valeurs nulles pour la table jointe

Veuillez m'aider avec la requête MySQL suivante, qui joint deux tables (A et B):

SELECT * from A
left join B on A.sid = B.sid
where (rCode = 1 Or rCode = 2 Or rCode = 3 Or rCode = 5)
AND (rYear = 2011 or rYear is null)

roleCode est un champ du tableau A et rYear est un champ du tableau B

L'ensemble de résultats n'est pas comme prévu. Seules 185 lignes sont retournées, mais il y a 629 lignes dans le tableau A qui correspondent à la condition where. Les lignes sans ligne correspondante dans le tableau B ne doivent-elles pas être retournées avec des valeurs nulles pour leurs champs B?

21
greg84

Vous ne devez pas spécifier Year dans une clause WHERE. Ceux-ci limitent vos résultats après la jointure. Vous devez spécifier Year dans une clause ON pour récupérer les enregistrements avec NULL de la table B.

SELECT * from A
left join B 
on A.sid = B.sid 
AND (rYear = 2011 or rYear is null)
where (rCode = 1 Or rCode = 2 Or rCode = 3 Or rCode = 5)
53
Jage

Greg, est-ce vraiment tout ce qu'il y a dans la requête?

Exemples de tableaux

create table A(rCode int, sid int);
insert A select 1,1;
insert A select 2,3;
insert A select 3,2;
insert A select 5,4;
insert A select 1,5;
create table B(rYear int, sid int);
insert B select 2011,1;
insert B select null,3;
insert B select 2011,2;
insert B select 2015,2;

Requêtes:

SELECT * from A
left join B on A.sid = B.sid
where (rCode = 1 Or rCode = 2 Or rCode = 3 Or rCode = 5)
AND (rYear = 2011 or rYear is null);

SELECT * from A
left join B on A.sid = B.sid AND (rYear = 2011 or rYear is null)
where (rCode = 1 Or rCode = 2 Or rCode = 3 Or rCode = 5);

Les deux requêtes sont exactement les mêmes, les deux retournant:

rCode       sid         rYear       sid
----------- ----------- ----------- -----------
1           1           2011        1
2           3           NULL        3
3           2           2011        2
5           4           NULL        NULL
1           5           NULL        NULL

Je suis donc surpris que la requête de Jage (la 2ème option) fonctionne pour vous mais pas votre original. Ce serait une histoire différente sans le or rYear is null Intérieur.

Pensez à LEFT JOIN comme ceci [1]

SELECT * from A
left join B on A.sid = B.sid

Gardez tout dans A, et là où il y a correspondance dans la clause ON, conservez B sinon remplissez les colonnes B avec NULL. Ajoutez la clause WHERE [2]

where (rCode = 1 Or rCode = 2 Or rCode = 3 Or rCode = 5)
AND (rYear = 2011 or rYear is null);

En utilisant la sortie de [1], COUPEZ en fonction du filtre, appliqué APRÈS la jointure gauche. Avec le rYear is null, Il devrait toujours conserver tous les enregistrements A, à condition que le filtre rCode soit mis en correspondance. Cependant, si le filtre de rYear n'est

AND (rYear in (2011,2012))

C'est une autre histoire, car là où B n'a pas été apparié, le rYear a été rempli avec NULL, ce qui ne correspondra pas au filtre rYear -> la ligne entière est supprimée, y compris l'enregistrement A. Un tel filtre sur rYear serait entré dans la clause ON comme indiqué ci-dessous, sinon il pourrait aussi bien en faire un INNER JOIN.

SELECT * from A
left join B on A.sid = B.sid AND (rYear in (2011,2012))
where (rCode = 1 Or rCode = 2 Or rCode = 3 Or rCode = 5)
1
RichardTheKiwi