ここに理論的な質問:
Table.field IS NULLまたはtable.field IS NOT NULLを指定すると、結合条件(たとえば、左または右の結合)では機能しませんが、どこ条件?
動作しない例:
-これにより、返品(null値以外)が除外されたすべての出荷が返されます。ただし、これは、[r.id is null]ステートメントに一致するものがあるかどうかにかかわらず、すべての貨物を返します。
SELECT
*
FROM
shipments s
LEFT OUTER JOIN returns r
ON s.id = r.id
AND r.id is null
WHERE
s.day >= CURDATE() - INTERVAL 10 DAY
作業例:
-これは、返品に関連するもの(非NULL値)を差し引いた、総出荷数である正しい行数を返します。
SELECT
*
FROM
shipments s
LEFT OUTER JOIN returns r
ON s.id = r.id
WHERE
s.day >= CURDATE() - INTERVAL 10 DAY
AND r.id is null
これはなぜですか?結合される2つのテーブル間の他のすべてのフィルター条件は正常に機能しますが、何らかの理由でIS NULLおよびIS NOT NULLフィルターはwhereステートメント以外で機能しません。
この理由は何ですか?
テーブルAとBの例:
A (parent) B (child)
============ =============
id | name pid | name
------------ -------------
1 | Alex 1 | Kate
2 | Bill 1 | Lia
3 | Cath 3 | Mary
4 | Dale NULL | Pan
5 | Evan
親とその子供を見つけたい場合は、INNER JOIN
を実行します。
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent INNER JOIN child
ON parent.id = child.pid
結果は、左側のテーブルのparent
のid
と2番目のテーブルのchild
のpid
のすべての一致が結果の行として表示されます。
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
+----+--------+------+-------+
さて、上記では子供のいない親は表示されません(子供のIDに一致するIDがないため、どうしますか?代わりに外部結合を実行します。外部結合には、左、右、完全な外部結合左のテーブル(親)から「余分な」行が必要なため、左の結合が必要です。
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent LEFT JOIN child
ON parent.id = child.pid
結果は、以前の一致に加えて、一致していない(読み取り:子供がいない)すべての親も表示されます。
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
| 2 | Bill | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
+----+--------+------+-------+
それらのNULL
はどこから来たのですか?まあ、MySQL(または使用する他のRDBMS)は、これらの親には一致(子供)がないため、そこに何を配置するかわかりません。したがって、これらの親と一致するpid
も_child.name
もありません。したがって、NULL
と呼ばれるこの特別な非値を配置します。
私のポイントは、これらのNULLs
はLEFT OUTER JOIN
の間に(結果セットに)作成されるということです
したがって、子供のいない親のみを表示する場合は、上記のWHERE child.pid IS NULL
にLEFT JOIN
を追加できます。 WHERE
節は、JOIN
の実行後に評価(チェック)されます。したがって、上記の結果から、pid
がNULLである最後の3行のみが表示されることが明らかです。
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent LEFT JOIN child
ON parent.id = child.pid
WHERE child.pid IS NULL
結果:
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 2 | Bill | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
+----+--------+------+-------+
では、IS NULL
チェックをWHERE
から結合ON
句に移動するとどうなりますか?
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent LEFT JOIN child
ON parent.id = child.pid
AND child.pid IS NULL
この場合、データベースはこれらの条件に一致する2つのテーブルから行を見つけようとします。つまり、parent.id = child.pid
[〜#〜] and [〜#〜]child.pid IN NULL
である行。しかし、それはそのような一致はありませんを見つけることができます。なぜなら、child.pid
は何か(1、2、3、4、または5)に等しく、同時にNULLになることができないからです!
したがって、条件:
ON parent.id = child.pid
AND child.pid IS NULL
以下と同等です:
ON 1 = 0
これは常にFalse
です。
では、なぜ左のテーブルからすべての行を返すのですか? LEF JOINだから!そして、左結合は戻ります一致する行(この場合はなし)そして一致しない左側のテーブルの行 =チェック(この場合はすべて):
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 1 | Alex | NULL | NULL |
| 2 | Bill | NULL | NULL |
| 3 | Cath | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
+----+--------+------+-------+
上記の説明が明確であることを願っています。
サイドノート(あなたの質問に直接関係ない):なぜ私たちのJOINのどれにもPan
が表示されないのはなぜですか?彼のpid
はNULL
であり、SQLの(一般的ではない)ロジックのNULLは何にも等しくないため、親ID(1、2、3、 4および5)。そこにNULLがあったとしても、NULL
は何にも等しくなく、NULL
自体にも等しくないため、一致しません(実際、非常に奇妙なロジックです!)。そのため、IS NULL
チェックではなく、特別なチェック= NULL
を使用します。
したがって、RIGHT JOIN
を実行するとPan
が表示されますか?はい、そうです! RIGHT JOINには、一致するすべての結果(最初に行ったINNER JOIN)に加えて、一致しないRIGHTテーブルのすべての行(この場合は1つ、(NULL, 'Pan')
行)が表示されるためです。
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent RIGHT JOIN child
ON parent.id = child.pid
結果:
+------+--------+------+-------+
| id | parent | pid | child |
+---------------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
| NULL | NULL | NULL | Pan |
+------+--------+------+-------+
残念ながら、MySQLにはFULL JOIN
がありません。他のRDBMSで試してみると、次のように表示されます。
+------+--------+------+-------+
| id | parent | pid | child |
+------+--------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
| 2 | Bill | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
| NULL | NULL | NULL | Pan |
+------+--------+------+-------+
NULL
部分は実際の結合の後に計算されるため、where句に含める必要があります。
実際には、NULLフィルターは無視されていません。これが、2つのテーブルを結合する方法です。
データベースサーバーが実行するステップを理解して理解するために、データベースサーバーが実行する手順を説明します。たとえば、NULL条件を無視していると言ったクエリを実行するとします。 SELECT * FROM貨物s LEFT OUTER JOINはrを返します
ON s.id = r.id AND r.idはnull WHERE s.day> = CURDATE()-間隔10日
最初に起こったのは、表SHIPMENTSのすべての行が選択されることです
次のステップで、データベースサーバーは2番目の(RETURNS)テーブルからレコードを1つずつ選択し始めます。
3番目のステップで、RETURNSテーブルのレコードは、クエリで指定した結合条件に対して修飾されます。この場合は(s.id = r.idおよびr.idはNULL)です
3番目のステップで適用されるこの条件は、サーバーがRETURNSテーブルの現在のレコードを受け入れるか拒否して、SHIPMENTテーブルの選択された行に追加するかを決定するだけであることに注意してください。 SHIPMENTテーブルからのレコードの選択には影響しません。
そして、SHIPMENTテーブルのすべての行とRETURNSテーブルの選択された行を含む2つのテーブルの結合が完了したら、中間結果にwhere句を適用します。そのため、r.id = nullの中間結果のすべてのレコードよりもwhere句に条件(r.idがNULL)を入れると、フィルターで除外されます。
WHERE
句は、JOIN
条件が処理された後に評価されます。
あなたはLEFT OUTTER JOIN
これは、RIGHTテーブルに一致するレコードがあるかどうかに関係なく、ステートメントのLEFT上のテーブルのすべてのTupleが必要であることを示します。この場合、結果はRIGHTテーブルからプルーニングされますが、ON句内にANDをまったく含めなかった場合と同じ結果になります。
WHERE句でANDを実行すると、LEFT JOINの実行後にプルーンが発生します。
実行計画でこれを明確にする必要があります。 JOINが優先され、その後、結果がフィルタリングされます。