278

この問題は、1 つはnot in where制約を使用し、もう 1 つはleft join. 制約内のテーブルにnot inは 1 つの null 値 (不正なデータ) が含まれていたため、そのクエリは 0 レコードのカウントを返しました。理由はある程度理解できますが、概念を完全に理解するためにいくつかの助けを借りることができます.

簡単に言うと、クエリ A は結果を返し、B は返さないのはなぜでしょうか?

A: select 'true' where 3 in (1, 2, 3, null)
B: select 'true' where 3 not in (1, 2, null)

set ansi_nulls offこれは SQL Server 2005 でのことでした。呼び出しによって B が結果を返すこともわかりました。

4

12 に答える 12

307

クエリAは次のものと同じです。

select 'true' where 3 = 1 or 3 = 2 or 3 = 3 or 3 = null

trueなので3 = 3、結果が得られます。

クエリBは次のものと同じです。

select 'true' where 3 <> 1 and 3 <> 2 and 3 <> null

ansi_nullsがオンの場合、3 <> nullはUNKNOWNであるため、述部はUNKNOWNと評価され、行は取得されません。

ansi_nullsがオフの場合、3 <> nullはtrueであるため、述語はtrueと評価され、行を取得します。

于 2008-09-24T19:01:50.323 に答える
57

NULL を使用するときはいつでも、実際には 3 値ロジックを扱っています。

最初のクエリは、WHERE 句が次のように評価される結果を返します。

    3 = 1 or 3 = 2 or 3 = 3 or 3 = null
which is:
    FALSE or FALSE or TRUE or UNKNOWN
which evaluates to 
    TRUE

2番目のもの:

    3 <> 1 and 3 <> 2 and 3 <> null
which evaluates to:
    TRUE and TRUE and UNKNOWN
which evaluates to:
    UNKNOWN

UNKNOWN は FALSE と同じではありません。次のように呼び出すことで簡単にテストできます。

select 'true' where 3 <> null
select 'true' where not (3 <> null)

どちらのクエリでも結果は得られません

UNKNOWN が FALSE と同じである場合、最初のクエリが FALSE を返すと仮定すると、2 番目のクエリは NOT(FALSE) と同じであるため、TRUE に評価する必要があります。
そうではありません。

SqlServerCentral には、このテーマに関する非常に優れた記事があります。

NULL と 3 値ロジックの問題全体は、最初は少し混乱する可能性がありますが、TSQL で正しいクエリを作成するには理解することが不可欠です。

私がお勧めするもう 1 つの記事は、SQL 集計関数と NULLです。

于 2008-09-25T09:54:41.723 に答える
49

NOT IN不明な値と比較した場合、0レコードを返します

NULLは不明であるため、可能な値のリストにaまたはsNOT INを含むクエリは、値がテストされている値ではないことを確認する方法がないため、常にレコードを返します。NULLNULL0NULL

于 2008-09-24T19:01:34.177 に答える
18

IS NULL を使用しない限り、null との比較は未定義です。

したがって、3 を NULL と比較すると (クエリ A)、undefined が返されます。

つまり、SELECT 'true' where 3 in (1,2,null) and SELECT 'true' where 3 not in (1,2,null)

NOT (UNDEFINED) はまだ定義されていませんが、TRUE ではないため、同じ結果が生成されます。

于 2008-09-24T18:53:18.503 に答える
9

執筆時点でのこの質問のタイトルは

SQL NOT IN 制約と NULL 値

質問のテキストから、問題はSELECTSQL DDL ではなく、SQL DML クエリで発生していたようCONSTRAINTです。

ただし、特にタイトルの文言を考えると、ここで行われているいくつかのステートメントは、誤解を招く可能性のあるステートメントであることに注意してください。

述語が UNKNOWN と評価されると、行は取得されません。

これは SQL DML の場合ですが、制約を考慮すると効果が異なります。

質問の述語から直接取得された2つの制約を持つこの非常に単純な表を検討してください(@Brannonによる優れた回答で対処されています):

DECLARE @T TABLE 
(
 true CHAR(4) DEFAULT 'true' NOT NULL, 
 CHECK ( 3 IN (1, 2, 3, NULL )), 
 CHECK ( 3 NOT IN (1, 2, NULL ))
);

INSERT INTO @T VALUES ('true');

SELECT COUNT(*) AS tally FROM @T;

@Brannon の回答によると、最初の制約 ( を使用IN) は TRUE に評価され、2 番目の制約 ( を使用NOT IN) は UNKNOWN に評価されます。しかし挿入成功!したがって、この場合、結果として実際に行が挿入されているため、「行を取得しない」と言うのは厳密には正しくありません。

上記の効果は、SQL-92 標準に関しては確かに正しいものです。SQL-92仕様の次のセクションを比較対照してください

7.6 where句

の結果は、検索条件の結果が真である T の行のテーブルです。

4.10 完全性制約

テーブル チェック制約は、指定された検索条件がテーブルのどの行でも false でない場合にのみ満たされます。

言い換えると:

WHERESQL DML では、が UNKNOWN と評価されると、「真である」という条件を満たさないため、結果から行が削除されます。

SQL DDL (つまり、制約) では、UNKNOWN と評価された場合、行は結果から削除されませ 。これは、"is not false" という条件を満たしているためです。

SQL DML と SQL DDL の効果はそれぞれ矛盾しているように見えるかもしれませんが、UNKNOWN の結果が制約を満たすことを可能にする (より正確には、制約を満たさないようにすることによって) '疑いの利益' を与える実際的な理由があります。 : この動作がなければ、すべての制約が null を明示的に処理する必要があり、言語設計の観点からは非常に不十分です (言うまでもなく、コーダーにとっては当然の苦痛です!)

ps私が書いているように、「不明であっても制約を満たすことに失敗しない」などのロジックに従うのが難しい場合は、SQL DDLのnull許容列とSQLの何かを避けるだけで、これらすべてを省くことができると考えてください。 null を生成する DML (例: 外部結合)!

于 2011-09-23T08:39:28.697 に答える
7

Aでは、3がセットの各メンバーに対して等しいかどうかがテストされ、(FALSE、FALSE、TRUE、UNKNOWN)が生成されます。要素の1つがTRUEであるため、条件はTRUEです。(ここで短絡が発生する可能性もあるため、最初のTRUEに達するとすぐに実際に停止し、3 = NULLと評価されることはありません。)

Bでは、条件をNOT(3 in(1,2、null))として評価していると思います。セットに対する同等性について3をテストすると、(FALSE、FALSE、UNKNOWN)が生成され、UNKNOWNに集約されます。NOT(UNKNOWN)はUNKNOWNを生成します。したがって、全体として、条件の真偽は不明であり、最終的には本質的にFALSEとして扱われます。

于 2008-09-24T18:58:15.730 に答える
7

ここでの回答から、NOT IN (subquery)ヌルを正しく処理しないと結論付けられる可能性があり、NOT EXISTS. しかし、そのような結論は時期尚早かもしれません。次のシナリオでは、Chris Date (Database Programming and Design、Vol 2 No 9、1989 年 9 月) の功績により、NOT INnull を正しく処理し、正しい結果を返しますNOT EXISTS

数量 ( ) の部品 ( ) を供給することが知られているspサプライヤ ( ) を表すテーブルを考えてみましょう。現在、テーブルには次の値が保持されています。snopnoqty

      VALUES ('S1', 'P1', NULL), 
             ('S2', 'P1', 200),
             ('S3', 'P1', 1000)

数量はヌル可能であることに注意してください。つまり、サプライヤが部品を供給することが知られているという事実を、数量が不明であっても記録できるようにするためです。

このタスクは、供給部品番号 '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、それが現れたかもしれない特効薬ではありません!

もちろん、問題の原因は null の存在にあるため、「実際の」解決策はこれらの null を削除することです。

これは、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;
于 2011-09-27T15:00:47.613 に答える
6

Null は、データが存在しないことを意味します。つまり、データ値が何もないことではなく、不明であることを意味します。C 型言語ではポインターを使用する場合、null は実際には何もないため、プログラミングのバックグラウンドを持つ人々がこれを混同するのは非常に簡単です。

したがって、最初のケースでは、3 は実際に (1,2,3,null) のセットに含まれるため、true が返されます。

ただし、2番目では、それを次のように減らすことができます

3 が含まれていない場合は「true」を選択します (null)

パーサーは比較対象のセットについて何も知らないため、何も返されません。これは空のセットではなく、未知のセットです。(1, 2, null) を使用しても (1,2) セットは明らかに false であるため、役に立ちません。

于 2008-09-24T19:08:44.380 に答える
6

NULL を含むサブクエリに対して NOT IN でフィルタリングする場合は、null でないことを確認するだけです

SELECT blah FROM t WHERE blah NOT IN
        (SELECT someotherBlah FROM t2 WHERE someotherBlah IS NOT NULL )
于 2014-10-31T22:06:22.550 に答える
1

これは男の子のためです:

select party_code 
from abc as a
where party_code not in (select party_code 
                         from xyz 
                         where party_code = a.party_code);

これは ansi 設定に関係なく機能します

于 2009-06-23T10:02:13.837 に答える
0

また、これは、結合、存在、およびhttp://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210.aspxの論理的な違いを知るのに役立つ場合があり ます

于 2008-09-24T22:47:33.403 に答える