ここでの回答から、NOT IN (subquery)
nullを正しく処理しないと結論付けることができるため、を使用しないでくださいNOT EXISTS
。ただし、そのような結論は時期尚早かもしれません。次のシナリオでは、Chris Date(データベースプログラミングとデザイン、第2巻、第9号、1989年9月)の功績が認められておりNOT IN
、nullを正しく処理し、ではなく正しい結果を返しますNOT EXISTS
。
部品()を大量に供給することが知られているsp
サプライヤー(sno
)を表すテーブルを考えてみますpno
(qty
)。テーブルは現在次の値を保持しています。
VALUES ('S1', 'P1', NULL),
('S2', 'P1', 200),
('S3', 'P1', 1000)
数量はNULL可能であることに注意してください。つまり、サプライヤが部品を提供していることがわかっていても、その数量が不明であっても記録できます。
タスクは、供給部品番号「P1」がわかっているが、数量が1000ではないサプライヤーを見つけることです。
以下はNOT IN
、サプライヤー「S2」のみを正しく識別するために使用します。
WITH sp AS
( SELECT *
FROM ( VALUES ( 'S1', 'P1', NULL ),
( 'S2', 'P1', 200 ),
( 'S3', 'P1', 1000 ) )
AS T ( sno, pno, qty )
)
SELECT DISTINCT spx.sno
FROM sp spx
WHERE spx.pno = 'P1'
AND 1000 NOT IN (
SELECT spy.qty
FROM sp spy
WHERE spy.sno = spx.sno
AND spy.pno = 'P1'
);
ただし、以下のクエリは同じ一般的な構造を使用していますが、 NOT EXISTS
が、結果にサプライヤ 'S1'が誤って含まれています(つまり、数量がnullの場合)。
WITH sp AS
( SELECT *
FROM ( VALUES ( 'S1', 'P1', NULL ),
( 'S2', 'P1', 200 ),
( 'S3', 'P1', 1000 ) )
AS T ( sno, pno, qty )
)
SELECT DISTINCT spx.sno
FROM sp spx
WHERE spx.pno = 'P1'
AND NOT EXISTS (
SELECT *
FROM sp spy
WHERE spy.sno = spx.sno
AND spy.pno = 'P1'
AND spy.qty = 1000
);
だからNOT EXISTS
、それは登場したかもしれない銀の弾丸ではありません!
もちろん、問題の原因はヌルの存在にあります。したがって、「本当の」解決策はそれらのヌルを排除することです。
これは、(他の可能な設計の中でも)2つのテーブルを使用して実現できます。
sp
部品を供給することが知られているサプライヤー
spq
部品を既知の量で供給することが知られているサプライヤー
spq
参照がある外部キー制約があるはずであることに注意してくださいsp
です。
次に、「マイナス」関係演算子(EXCEPT
標準SQLのキーワード)を使用して結果を取得できます。
WITH sp AS
( SELECT *
FROM ( VALUES ( 'S1', 'P1' ),
( 'S2', 'P1' ),
( 'S3', 'P1' ) )
AS T ( sno, pno )
),
spq AS
( SELECT *
FROM ( VALUES ( 'S2', 'P1', 200 ),
( 'S3', 'P1', 1000 ) )
AS T ( sno, pno, qty )
)
SELECT sno
FROM spq
WHERE pno = 'P1'
EXCEPT
SELECT sno
FROM spq
WHERE pno = 'P1'
AND qty = 1000;
NOT IN
一連の<> and
変化に変換することで、このセットにないものの意味的動作が別のものに変わることを誰かが指摘したことがありますか?