web-dev-qa-db-ja.com

他の列に基づく制約

行の他の値に基づいて、列に許可される値を制限することは可能ですか?

たとえば、私のテーブル:

ID  Test_mode  Active
--  ---------  ------
1   1          Null
2   0          1
3   1          0

Test_modeActiveに挿入されている場合、0の値を1に変更する方法はありますか

OR

Test_modeが1の場合、Activeの挿入/更新は許可されません

OR

Test_modeが1で、Activeの挿入/更新が試みられた場合、何らかのエラーをスローします。

ActiveはNULL、1、0、およびTest_modeを0として1のみにすることができます。

知らない場合は、これが理にかなっていると思います。質問を更新します。

7
Archangel33

まず、dba.stackexchange.comへようこそ。投稿ありがとうございます。

行の他の値に基づいて、列に許可される値を制限することは可能ですか?

はい CHECK CONSTRAINTS を使用 ここ

例:

create table myTable (ID int identity(1,1)
                        , Test_mode int
                        , Active int 
                        )
go

-- Active can only be NULL, 1, 0, AND only 1 with Test_mode as 0.
ALTER TABLE myTable WITH CHECK ADD 
   CONSTRAINT ck_active CHECK (active IS NULL OR active IN (1, 0)) 
   go

-- some test data
insert into myTable (test_mode, Active) values (1, null)
insert into myTable (test_mode, Active) values (0, null)
insert into myTable (test_mode, Active) values (1, 0)
insert into myTable (test_mode, Active) values (0, 1)
insert into myTable (test_mode, Active) values (1, 1)

select * from myTable

-- Is there a way to either change the value of Test_mode to 0 if a 1 is inserted into Active

update myTable
set Test_mode = case when Active = 1 then  0
        else Test_mode 
        end
where Active = 1

Test_mode is 1はActiveの挿入/更新を許可しません--OR-- Test_modeが1で、Activeの挿入/更新が試行された場合、何らかのエラーをスローします。

説明どおりにTRY/CATCHを使用 ここ

8
Kin Shah

無効なデータがテーブルに入るのを防ぐ最初の防御策は、列のデータ型です。

プロセスが列をデータ型の範囲外の値に挿入または更新しようとした場合(または列がNULLsを許可していない場合はNULL)、操作は即座に失敗します。追加の作業を行うため。

データ型の選択は、テーブル設計の最も重要な側面の1つです。

それで、あなたはスキーマを投稿しなかったので、あなたが提供した情報に基づいてスキーマを構築します:

CREATE TABLE [dbo].[Tests]
(
    ID int IDENTITY(1, 1) PRIMARY KEY,
    Test_mode bit NOT NULL, /* Based on only seeing 0/1. Maybe tinyint? */
    Active bit NULL
);

この設計に基づいて、使用可能な組み合わせはすでに以下に制限されています。

Test_mode Active 
 0 NULL 
 0 0 
 0 1 
 1 NULL 
 1 0 
 1 1

それ以外の場合はエラーが発生します。 (これは良いことです。)


Activeに1が挿入された場合にTest_modeの値を0に変更する方法はありますか

OR

Test_modeが1の場合、Activeの挿入/更新を許可しない

OR

Test_modeが1で、Activeの挿入/更新が試行された場合、何らかのエラーをスローします。

ActiveはNULL、1、0、およびTest_modeが0の1のみです。

4値の許容される組み合わせ(まあ、一種)に到達するためのさまざまな方法を指定しました。これらは非常に異なる戦略であり、実装動作は非常に異なります。

私は、いわゆるdeclarative制約を使用することを好みます。つまり、テーブルスキーマとそれに関連付けられたオブジェクトは、許可される値を明示的にdeclaring許可することで制限します(または、場合によってはnot許可することです)。実際、列のデータ型自体は一種の宣言的制約です。値を制限できるのはテーブルデータに近いほど、制限を簡単かつ確実に行うことができます。 (対照的に、non-declarativeまたはactive制約は、T-SQLの一部、通常はテーブルトリガー、またはストアドプロシージャの一部を記述することによって実装されます。 )

オプションの最初の3つは、非宣言的な手段でのみ実装できます。ただし、最後の1つは宣言型なので、それに焦点を当てましょう。

ActiveはNULL、1、0、およびTest_modeを0とする1のみにすることができます。

これは実際に必要なものを定義します。これは、テーブル内の値の許可された組み合わせです。有効な組み合わせは、列の値同じ行内にのみ依存することに注意してください。これは、制約を実装するために使用できるメカニズムを決定するため、重要です。

この場合、 CHECK constraint を使用できます。これは、行が有効か無効かをrow'sに基づいて判断するtrue/falseテストです。列の値1。テストが失敗した場合、行を変更しようとした操作はエラーで失敗します。

ALTER TABLE [dbo].[Tests] WITH CHECK
    ADD CONSTRAINT CC_Tests_TestMode_Active
        CHECK ((Test_mode != 0) OR ((Active IS NOT NULL) AND (Active = 1)));

Test_modeが実際には(nullにできない)整数型であっても機能し続けるように述語を作成したことに注意してください。 CHECK制約は、述語がundefinedと評価される行を許可するため、IS NOT NULL部分が必要です。

1 これらはcanを使用して現在の行の外側でチェックを行いますが、これは悪い習慣であり、ここでは詳しく説明しません。代わりにトリガーを使用してください。

3
Jon Seigel
CREATE UNIQUE INDEX [UNQ_IndexName]
  ON [dbo].[Table]([Column])
  WHERE   ([Status] in( 'A','D' ) );
2
Mohamed.Abdo