次の両方がゼロを返すのはなぜですか?確かに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
それは否定です。ただし、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つのNULL
がeverythingNULL
。引数の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
に評価されます。
この「奇妙な」動作は、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
。これはまったく推奨されておらず、回避する必要があることに注意してください!
この値のいずれかが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
NOT
はalways否定です。 T-SQLのこの動作の理由は、null
値がデータベース構成設定(ansi_nulls
として知られる)に応じて特別な方法で処理されるという事実にあります。この設定に応じて、null
は他の値と同じ方法で処理されるか、「値が設定されていない」として処理されます。この場合、null値を含むすべての式は無効と見なされます。
さらに、式
(@a IS NULL AND @b IS NULL)
OR
@a = @b
両方の変数がNULL
である場合のみを対象とし、@a
または@b
はNULL
です。その場合、結果はansi_nulls
の設定に依存します:on
の場合、変数の1つが@a = @b
の結果は常にfalse
になりますNULL
。
ansi_nulls
がoff
の場合、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
戻ります:
つまり、このスクリプトではnull
は値として扱われるため、a='a'
およびb=null
は0
を返します。これは期待どおりです。両方の変数が等しい(または両方がnull
)場合にのみ、1
を返します。