web-dev-qa-db-ja.com

「否定」が否定ではない場合

次の両方がゼロを返すのはなぜですか?確かに2番目は1番目の否定ですか? SQL Server 2008を使用しています。

DECLARE 
    @a VARCHAR(10) = NULL ,
    @b VARCHAR(10) = 'a'

SELECT  
    CASE WHEN ( ( @a IS NULL
                      AND @b IS NULL
                    )
                    OR @a = @b
                  ) THEN 1
         ELSE 0
    END , -- Returns 0
    CASE WHEN NOT ( ( @a IS NULL
                      AND @b IS NULL
                    )
                    OR @a = @b
                  ) THEN 1
         ELSE 0
    END -- Also returns 0
45
ChrisN

それは否定です。ただし、ANSI NULLを理解する必要があります。NULLの否定もNULLです。 NULLは偽の真理値です。

したがって、引数のいずれかがヌルの場合、_@a = @b_の結果はヌル(偽)になり、その否定もヌル(偽)になります。

否定を希望する方法で使用するには、NULLを取り除く必要があります。ただし、代わりに単純に比較の結果を逆にする方が簡単な場合があります。

_case when (...) then 1 else 0 end,
case when (...) then 0 else 1 end
_

常に_1, 0_または_0, 1_のいずれかが得られます。

編集:

Jpmc26が述べたように、1つのNULLeverythingNULL。引数の1つがnullの場合、常にnullを返すとは限らない演算子があります-最も明白な例はもちろん_is null_です。

より広範な例では、T-SQLの論理演算子はKleeneの代数(または類似のもの)を使用し、OR式の真理値を次のように定義します。

_  | T | U | F
T | T | T | T
U | T | U | U
F | T | U | F
_

ANDは他の演算子と同様に類似しています)

したがって、少なくとも一方の引数がtrueであれば、もう一方が不明(「null」)であっても結果はtrueになることがわかります。また、not(T or U)は偽の真理値を返し、not(F or U)alsoを返します_F or U_が偽であるにもかかわらず、偽の真理値-_F or U_はUであり、not(U)Uであるため、偽です。

これは、両方の引数がnullの場合に式が期待どおりに動作する理由を説明するために重要です。_@a is null and @b is null_はtrueに評価され、_true or unknown_はtrueに評価されます。

50
Luaan

この「奇妙な」動作は、NULL値が原因です。

NOT (Something that returns NULL)の否定はTRUEではなく、依然としてNULLです。

例えば。

SELECT * FROM <Table> WHERE <Column> = null -- 0 rows 
SELECT * FROM <Table> WHERE NOT (<Column> = null) -- Still 0 rows

ここで述べたことに加えて、この動作を避けるには

SET ANSI_NULLS OFF

これにより、オプティマイザーはNULLを通常の値として扱い、TRUE\FALSE。これはまったく推奨されておらず、回避する必要があることに注意してください!

8
sagi

この値のいずれかがnullの場合、@ a = @ bの問題です

以下のコードを試してみると、正しい結果が得られます

DECLARE 
    @a VARCHAR(10) = NULL ,
    @b VARCHAR(10) = 'a'

SELECT  
    CASE WHEN ( ( @a IS NULL
                      AND @b IS NULL
                    )
                    OR @a = @b
                  ) THEN 1
         ELSE 0
    END , -- returns 0
    CASE WHEN NOT ( ( @a IS NULL
                      AND @b IS NULL
                    )
                    OR ISNULL(@a,-1) = ISNULL(@b,-1)
                  ) THEN 1
         ELSE 0
    END -- also returns 0
4

NOTalways否定です。 T-SQLのこの動作の理由は、null値がデータベース構成設定(ansi_nullsとして知られる)に応じて特別な方法で処理されるという事実にあります。この設定に応じて、nullは他の値と同じ方法で処理されるか、「値が設定されていない」として処理されます。この場合、null値を含むすべての式は無効と見なされます。

さらに、式

(@a IS NULL AND @b IS NULL)
OR 
@a = @b

両方の変数NULLである場合のみを対象とし、@aまたは@bNULLです。その場合、結果はansi_nullsの設定に依存します:onの場合、変数の1つが@a = @bの結果は常にfalseになりますNULL

ansi_nullsoffの場合、NULLは値として扱われ、期待どおりに動作します。

このような予期しない動作を回避するには、次のようにすべてのケースをカバーする必要があります。

DECLARE 
    @a VARCHAR(10) = 'a',
    @b VARCHAR(10) = null

SELECT  
    CASE 
        WHEN (@a IS NOT null AND @b IS null) THEN 0
        WHEN (@a IS null AND @b IS NOT null) THEN 0
        WHEN (@a IS null AND @b IS null) THEN 1
        WHEN (@a=@b) THEN 1
    ELSE 0
    END 

この例では、@a=@bケースがチェックされる前にすべてのnullケースが処理されます(CASEステートメントで、 WHENは表示された順に処理され、条件が一致した場合、処理は終了し、指定された値が返されます)。


すべての可能な(関連する)組み合わせをテストするには、次のスクリプトを使用できます。

DECLARE @combinations TABLE (
    a VARCHAR(10),b VARCHAR(10)
    )

INSERT INTO @combinations
    SELECT 'a', null 
    UNION SELECT null, 'b'
    UNION SELECT 'a', 'b'
    UNION SELECT null, null
    UNION SELECT 'a', 'a'

SELECT a, b,
    CASE 
        WHEN (a IS NOT null AND b IS null) THEN 0
        WHEN (a IS null AND b IS NOT null) THEN 0
        WHEN (a IS null AND b IS null) THEN 1
        WHEN (a=b) THEN 1
    ELSE 0
    END as result
from @combinations
order by result 

戻ります:

result of query

つまり、このスクリプトではnullは値として扱われるため、a='a'およびb=null0を返します。これは期待どおりです。両方の変数が等しい(または両方がnull)場合にのみ、1を返します。

0
Matt