この質問のSQLはOracle用です。ただし、単にFROM dual
は、SQL Server(Oracleと同じ結果を返す)で動作するようにします。
次のSQLがあります。
SELECT 1
FROM dual
WHERE NULL IN (SELECT 1 FROM dual WHERE 1 = 0);
SELECT 1
FROM dual
WHERE NULL NOT IN (SELECT 1 FROM dual WHERE 1 = 0);
空のセットと1をそれぞれ返します。 NULL
を他の値に置き換えると、まったく同じ結果が得られます。
SELECT 1
FROM dual
WHERE 33 IN (SELECT 1 FROM dual WHERE 1 = 0);
SELECT 1
FROM dual
WHERE 33 NOT IN (SELECT 1 FROM dual WHERE 1 = 0);
では、NULLと空の結果セットの比較はここでどのように機能しますか?
次の2つの条件で何が起こるかを見てみましょう。
WHERE NULL IN (SELECT 1 FROM dual WHERE 1 = 0);
WHERE NULL NOT IN (SELECT 1 FROM dual WHERE 1 = 0);
サブクエリは空のテーブルを生成するので、それらを疑似コードで書くことができます:
WHERE NULL IN (<empty_table>);
WHERE NULL NOT IN (<empty_table>);
したがって、最初の質問では、いくつかの(NULL)値が空のテーブルにあるかどうかを尋ねます。 テーブルが空であるため、そのような条件の結果は、値がnullかどうかに関係なく、常にFALSE
になります。
2番目の条件で同じ推論を適用すると、値とは無関係に、常にTRUE
になります。
ブール型なので、これらの条件の結果の値がPostgresで簡単に確認できます。 dbfiddle.uk を参照してください。最初の行はf
(FALSE)を示し、2番目の行はt
(TRUE)を示します。
上記の結果、2つのクエリを実行すると、次のようになります。
SELECT 1
FROM dual
WHERE FALSE ;
空の結果を返す(WHERE
条件はFALSE
であるため、正しい)
そして
SELECT 1
FROM dual
WHERE TRUE ;
1行を与える(再び正しい)。
これは、SQLに固有の、いわゆる「3値ロジック」の問題です。 TRUE
およびFALSE
で機能する他のプログラミング言語とは異なり、SQLにもUNKNOWN(NULL)があります。また、SQLのさまざまな要素がUNKNOWNを処理する方法には一貫性がありません。
ON
、WHERE
およびHAVING
はすべてUNKNOWNをFALSEと見なします。空の結果セットはSQLによってNULLと見なされます。したがって、NOT IN
は偽のステートメントを探しており、WHEREはNULLをFALSEとして解釈するため、出力を受け取ります。 2つのNULL値を比較すると、UNKNOWNも返されます。
一方、CHECK
制約はNULLをTRUEと見なします。たとえば、売上高が0より大きい場合にCHECK
制約を作成すると、NULL> 0はUNKNOWNと評価されますが、ルールは、制約がFALSEと評価されてはならないため、最終的にはTRUEとなるというルールです。 (Lennartのコメントに基づいて編集)。
詳細な説明:
SELECT 1
WHERE NULL IN (SELECT 1 WHERE 1 = 0);
(SQL Server構文)
空のセットを返します。空のデータセットはSQLによってNULLと見なされるため、実際に行っていることは次のとおりです。
SELECT 1
WHERE NULL IN (NullSet/EmptySet);
具体的には、比較対象はすべての行のテーブル= NULLです。
https://www.oreilly.com/library/view/sql-and-relational/9781449319724/ch12s07.html
If a row subquery evaluates to an empty table, that empty table is coerced to a row of all nulls.
あなたはそれの数学の部分も持っています:
http://www.solving-math-problems.com/null-set-empty-set.html
2つのNULLを比較すると、結果は常にUNKNOWNとなり、WHEREステートメントはUNKNOWNをFALSEと見なします。
したがって、最初のコードでは:
SELECT 1
WHERE NULL IN (NullSet/EmptySet);
WHEREステートメントは基本的にNULLがNULLセット内に存在するかどうかをチェックします。NULLの比較はUNKNOWNであり、UNKNOWNはFALSEと見なされるため、結果は得られません。
しかし、ここで:
SELECT 1
WHERE NULL NOT IN (NULL);
あなたは、WHERE NULLがNULLセット内に存在しないと言っています。この場合も、UNKNOWNはFALSEと見なされるため、出力が得られます。
これは、比較した理由も説明します。
SELECT 1
FROM dual
WHERE 33 NOT IN (SELECT 1 FROM dual WHERE 1 = 0);
出力を受け取りました。 33がNULLセット/空セット内の要素にないからです。