チェック制約の活用ヒント
この記事を書いたのには、3つの目的があります。データベースでチェック制約を使用している方に向けては、チェック制約をもっとうまく活用する手助けをすることが目的です。チェック制約を使用していない方に向けては、チェック制約の使用を勧めて、適切な使い方を案内することが目的です。すでにチェック制約についてよくご存じだという方に向けては、私が知らないことを メール で教えていただけるよう促すことが目的です。
チェック制約の目的は、無効なデータをデータベースに入れないようにすることです。それは必要ないように思えるかもしれません。アプリケーションが行うべきことではないでしょうか。そのとおりですが、実際には、多くのデータベースには数多くの不正なデータがあります。私は、不正なデータによって引き起こされた非常に多くの問題を追い掛けてきましたが、皆さんのデータベースにも存在している可能性は大いにあります。
以下では、チェック制約について知っておくべきいくつかのポイントについて見て行きます。
チェック制約はWHEREのないWHERE節のようなもの。
条件が偽と判定された場合、システムはINSERTまたはUPDATEを拒絶します(MERGEも、INSERTとUPDATEを実行できることに留意してください)。条件が真または未知と判定された場合、処理は許可されます。
次の表定義を見てみましょう。2つのチェック制約があります。
create table EmployeeMaster for system name EmpMast ( ID dec(5) primary key check (ID > 0), Name varchar(16), Type char(1) check (Type in ('S', 'H', 'T', 'P')), Salary dec(9,2), Wage dec(5,2) )
1つ目のチェック制約は、従業員に割り当てられるID番号が正の数であることが保証されるようにします。2つ目のチェック制約は、従業員種別の値が4つの値のうちの1つになることができるようにします。また、従業員種別はヌルでも構いませんが、ヌルは値ではありません。このことが次のポイントへつながります。
ヌル値はデータベース マネージャーが許容できる未知の条件を作成する。ヌルを許容しない場合は、NOT NULL制約を追加して指定する必要がある。
create table EmployeeMaster for system name EmpMast ( ID dec(5) primary key check (ID > 0), Name varchar(16), Type char(1) not null check (Type in ('S', 'H', 'T', 'P')), Salary dec(9,2), Wage dec(5,2) )
Type(従業員種別)は、S、H、T、またはPでなければなりません。また、ヌルであってはなりません。
CREATE TABLEおよびALTER TABLEステートメントでチェック制約を指定できる。
CREATE TABLEの例については、前の例を参照してください。ALTER TABLEの例については、次の例を参照してください。
alter table EmployeeMaster add constraint EmployeeMaster_valid_types check (Type in ('S', 'H', 'T', 'P'))
CREATE TABLEとは異なり、ALTER TABLEでは制約に名前を付ける必要があります。ここでは、EmployeeMaster_valid_typesという名前を付けました。ここから次のポイントにつながります。
制約の名前は自分で付けることも、システムに指定させることもできる。
前の例は、ALTER TABLEでチェック制約に名前を付ける方法を示しています。以下は、CREATE TABLEでチェック制約に名前を付ける方法です。
create table EmployeeMaster for system name EmpMast ( ID dec(5) primary key, Name varchar(16), Type char(1), Salary dec(9,2), Wage dec(5,2), constraint EmployeeMaster_ID_is_positive check (ID > 0), constraint EmployeeMaster_valid_types check (Type in ('S', 'H', 'T', 'P')) )
EmployeeMaster_ID_is_positiveとEmployee_valid_typesという名前が付けられた2つのチェック制約があります。私は、制約には名前を付けるようにしています。自分で制約に名前を付けない場合は、システムが名前を作成しますが、たいていは、私が思い付くものほど説明的でない名前になりがちです。説明的な名前は、物理ファイル制約の処理(WRKPFCST)CLコマンドまたはSYSCSTおよびSYSCHKCSTビューのようなインターフェースを使用しているときに制約名を確認するのに便利です。
制約が列定義の後に置かれていることにお気付きでしょうか。ここから次のポイントにつながります。
チェック制約が複数の列を参照している場合は、すべての列定義の後で定義する必要がある。
チェック制約が1つの列のみを参照している場合は、列定義で制約を定義しても、すべての列定義の後で定義しても構いません。しかし、チェック制約が複数の列を参照している場合は、次のように、列定義の後に配置する必要があります。
create table EmployeeMaster for system name EmpMast ( ID dec(5) primary key, Name varchar(16), Type char(1), Salary dec(9,2), Wage dec(5,2), constraint EmployeeMaster_ID_is_positive check (ID > 0), constraint EmployeeMaster_valid_types check ( case when Type = 'S' and Salary > 0 then 1 when Type in ('H', 'T', 'P') and Wage > 0 then 1 else 0 end = 1 ) );
EmployeeMaster_valid_typesというチェック制約は、Type、Salary、およびWageという3つの列を参照します。TypeがS(月給制)の場合、Salary(月給)は正の数である必要があります。TypeがH(時給制)、T(一時雇用)、またはP(パートタイム)の場合、Wage(賃金)の列は正の数である必要があります。テストがどのような動作になるか興味深いものがあります。さらに詳しく見てみましょう。
CASE式はチェック制約に大きな可能性をもたらす。
これはJoe Celko氏から学んだことです(他にも多くのことを教わりました)。CASE式は、従業員が月給制であり、Salary(給料)が正の数である場合に「1」の値を返します。また、従業員が別の従業員種別であり、Wage(賃金)が正の数である場合に「1」の値を返します。それら以外の場合、CASE式は「0」を返します。チェック制約は、CASEからの結果を1と比較して、データを承認するべきかどうか判定します。また、これにより無効な従業員種別コードも捕捉されます。CASE式を使用することで、多くのことを行うことができます。
チェック制約を使用することで多くのことを行える。
チェック制約により、アプリケーションがデータを検証する必要性がなくなりますが、そのことは、アプリケーションがデータを検証するべきでないということを意味するのでしょうか。これは意見の分かれる問題であり、私は両方の観点から思うところがあります。
アプリケーション プログラムに、従業員種別の値が許容される値であるかどうかチェックさせることがよくないとは思いません。メリットとしては、アプリケーション プログラムが検証を行う場合は、エラー処理がより簡単になります。そのようなケースでは、チェック制約は最後の砦ということになります。デメリットとしては、許容される別の従業員種別がデータベースに定義された場合に、アプリケーションに修正が必要になることです。
チェック制約は、データベースに無効なデータが保存されるのを防ぐ効果的な手法です。できる限り活用しましょう。