web-dev-qa-db-ja.com

MySQL Left Joinが結合テーブルにnull値を返さない

2つのテーブル(AとB)を結合する次のMySQLクエリを手伝ってください:

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はテーブルAのフィールドであり、rYearはテーブルBのフィールドです

結果セットは期待どおりではありません。 185行のみが返されますが、テーブルAにはwhere条件に一致する629行があります。テーブルBに一致する行のない行は、Bフィールドにnull値で返されるべきではありませんか?

21
greg84

WHERE句にrYearを指定しないでください。これらは、結合後の結果を制限します。 ON句でrYearを指定して、テーブルBから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)
53
Jage

グレッグ、クエリにあるのは本当にそれだけですか?

サンプル表

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;

クエリ:

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

両方のクエリはまったく同じで、どちらも次を返します。

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

したがって、Jageのクエリ(2番目のオプション)が機能するが、元の機能は機能しないことに驚いています。内側のor rYear is nullがなければ別の話になります。

LEFT JOINは次のように考えてください[1]

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

すべてをAに保持し、ON句で一致した場合は、Bを保持します。それ以外の場合は、B列にNULLを埋め込みます。 WHERE句を追加します[2]

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

[1]からの出力を使用して、フィルターに基づいて切り取り、左結合の後に適用します。 rYear is nullを使用すると、rCodeフィルターが一致するという条件で、すべてのAレコードを保持する必要があります。ただし、rYearのフィルターが

AND (rYear in (2011,2012))

これは別の話です。Bが一致しなかった場合、rYearはNULLでパディングされ、rYearフィルターと一致しません-> Aレコードを含む行全体が削除されます。このようなrYearのフィルターは、以下に示すようにON句に入ります。そうでない場合は、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