2012-02-07 9 views
11

誰でも次のコードの第3の挿入物(クエリデータ)がSQL Serverで許可されている理由を説明できますか?チェックイン制約がこのnull挿入を停止しないのはなぜですか?

私の知る限り、チェック制約はのみ許可する必要があります。

  • Codeがnullで、Systemはnullです。
  • Codeはnullではなく、System1です。

私が最初に考えたのはANSI NULLSだったが、それらの設定onまたはoffには差は行われません。

これは、アプリケーションで見つかったより大きな問題の簡略化した例です(システムは数字のリストと照合してチェックされました - IN(1, 2, etc.))。このチェックを、外部キー(INではなく)と置き換えました。新しい検査制約は、nullまたはnullでないどちらかを許可しました。そうすることで3番目のインサートができなくなりました。

IF EXISTS (SELECT * FROM sys.check_constraints WHERE object_id = OBJECT_ID(N'[dbo].[CK_TestCheck]') AND parent_object_id = OBJECT_ID(N'[dbo].[TestCheck]')) 
    ALTER TABLE [dbo].[TestCheck] DROP CONSTRAINT [CK_TestCheck] 
GO 

IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TestCheck]') AND type in (N'U')) 
    DROP TABLE [dbo].[TestCheck] 
GO 

SET ANSI_NULLS ON 
GO 

CREATE TABLE TestCheck(
    [Id] [int] IDENTITY(1,1) NOT NULL, 
    [Code] [varchar](50) NULL, 
    [System] [tinyint] NULL, 
    PRIMARY KEY CLUSTERED ([Id] ASC)) 
GO 

ALTER TABLE [dbo].[TestCheck] WITH CHECK ADD CONSTRAINT [CK_TestCheck] CHECK 
(
    ([Code] IS NULL AND [System] IS NULL) --Both null 
    OR 
    ([Code] IS NOT NULL AND [System] = 1) --Both not null ???? 
) 
GO 

ALTER TABLE [dbo].[TestCheck] CHECK CONSTRAINT [CK_TestCheck] 
GO 

--Good Data 
insert TestCheck (Code, [System]) Values(null, null); 
insert TestCheck (Code, [System]) Values('123', 1); 

--Query Data 
insert TestCheck (Code, [System]) Values('123', null); 

--Bad data stopped 
insert TestCheck (Code, [System]) Values(null, 1); 
insert TestCheck (Code, [System]) Values('123', 4); 

select * from TestCheck 
Where 
    case when 
    (
     ([Code] IS NULL AND [System] IS NULL)   --Both null 
     OR 
     ([Code] IS NOT NULL AND [System] in (1, 2, 3)) --Both not null ???? 
    ) 
    then 0 else 1 end 
    = 1 

答えて

11

123, NULLの現在の制約を評価した結果は未定義です。

  • ([Code] IS NULL AND [System] IS NULL)False
  • ([Code] IS NOT NULL AND [System] IN (1, 2, 3))と評価さ

    Undefined

に結果が Undefined

Check Constraint

CHECK制約が評価に値を拒否している評価FALSE。 null の値がUNKNOWNに評価されるため、式内のそれらの存在が 制約を上書きすることがあります。

[System] IN (1, 2, 3)からISNULL([System], 0) IN (1, 2, 3)にチェックを変更する必要があります。

あなたのチェック制約は、SQLの素晴らしい3つの値論理に

ALTER TABLE [dbo].[TestCheck] WITH CHECK ADD CONSTRAINT [CK_TestCheck] CHECK 
(
    ([Code] IS NULL AND [System] IS NULL) --Both null 
    OR 
    ([Code] IS NOT NULL AND ISNULL([System], 0) IN (1, 2, 3)) --Both not null ???? 
) 
+2

未定義の後に(偽)をかっこで囲みません。それは間違いではありません。 –

+0

@Damien_The_Unbeliever - 私はあなたが何を意味しているのか知っていますが、最終結果に向かって「未定義」が何をしているかについては括弧で追加しました。私は答えにこのコメントを追加します。 –

+2

しかし、あなたが追加したものは真実ではありません。チェック制約の最終結果が「UNKNOWN」の場合は、TRUEと評価された場合と同じように扱われます。つまり、OPを驚かせました。 –

13

ようこそなりました。ご存じないかもしれませんが、nullとの標準比較の結果はTRUE、またはFALSEではなく、UNKNOWNです。

WHERE句では、句全体がTRUEと評価されなければなりません。

CHECKの制約では、制約全体がFALSEでないと評価する必要があります。

だから、我々が持っている:

(クエリデータ用)となり
([Code] IS NULL AND [System] IS NULL) --Both null 
OR 
([Code] IS NOT NULL AND [System] = 1) --Both not null ???? 

(FALSE AND TRUE) 
OR 
(TRUE AND UNKNOWN) 

そしてUNKNOWNとしてUNKNOWN片側または他の評価さと任意のオペレーター、その全体的な結果はUNKNOWNです。これはFALSEではないため、チェック制約の評価は成功です。


あなたはSystemがnullではない場合は、追加の明示的な要件としてそれを追加した場合、それは私には明確なのです。

([Code] IS NULL AND [System] IS NULL) --Both null 
OR 
([Code] IS NOT NULL AND [System] IS NOT NULL AND [System] = 1) --Both not null ???? 

は奇数TADこれが定義されているように見えるかもしれないが、それは他の制約が動作する方法と一致している - 例えば外部キー制約にはNULL可能な列があり、それらの列のいずれかがNULLの場合は、参照先の表に一致する行がある必要はありません。

+0

ありがとう、私はANSI_NULLの設定はこの動作を制御する方法だったという印象を受けていましたか? – DaveShaw

+0

@DaveShaw - あなたはそれを他の副作用を持つことができる「オフ」に設定する必要があると思います – JNK

+0

@JNK私はオンとオフの両方を試みて、同じ結果を得ました。 – DaveShaw

関連する問題