【初級編⑨】テーブルに設定するキーの種類や様々な制約(CONSTRAINT)

SQL Serverのテーブルには、格納する値に各種制約を設けることができます。この機能を使うことにより、おかしなデータを格納することはできなくなり、データの整合性が保証されます。

本来、SQL Serverを利用するアプリケーション側(データを格納する処理を実行する側)で制御すべきですが、SQL Server側にもこの制約を設けておくことで、確実にデータの整合が取れた状態にできます。

制約とは

制約(英語では「Constraint」)とは、SQL Serverのテーブルに存在する列に対して、格納できる値のルールを定め、データ入力時にはそのルールと照合し、ルール違反のデータは格納できないようにする機能のことを言います。テーブルに対してではなく、あくまで列一つ一つに対して設定するものだと言うことを理解しましょう。

列の制約には以下の6種類が存在します。

No 制約の種類 説明
1 NOT NULL 制約 NULL値の挿入を不可とする制約
2 PRIMARY KEY 制約 主キー列であり、重複、NULL値挿入を不可とする制約
3 UNIQUE 制約 一意である必要があり、重複値の格納を不可とする制約
4 CHECK 制約 指定した条件に合致しない値の格納を不可とする制約
5 DEFAULT 制約 値を入力しなかった際は、予め指定した初期値を格納する制約
6 FOREIGN KEY 制約 別テーブルの主キーと参照整合性が取れている状態を保つ制約

このように、格納できる値に制約(ルール)を設けることで、ルールに基づいた正しいデータしか格納されていない状態を保つことが可能になります。この状態のことを、「データの整合姓が保たれている」と表現したりもします。

それでは、順番に説明していきます。

NOT NULL 制約

NOT NULL 制約とは、以前のエントリでManagement Studioでテーブルを作成する際にも説明しましたが、格納する値にNULL値を許可しない制約です。基本的にNULLは許容しないようにしておき、必要な場合のみ許容するのがオススメです。

例えば以下の様な社員テーブルがあったとします。

社員テーブル

このテーブルでは、「生年月日」と「給料」のみNULLが許容されているので、NULLを挿入することができます。

 INSERT INTO Table_Syain VALUES('00006', '前田 六実', 22, '女', NULL, NULL) -------------------------------------------------------------------------- (1 行処理されました) 

NULLを許可する

しかし、「性別」はNULLを許容していないので、NULLを挿入しようとするとエラーが出て登録ができません。

 INSERT INTO Table_Syain VALUES('00007', '七道 剛', 29, NULL, NULL, NULL) -------------------------------------------------------------------------- メッセージ 515、レベル 16、状態 2、行 1 テーブル 'TestDB.dbo.Table_Syain' の列 '性別' に値 NULL を挿入できません。この列では NULL 値が許可されていません。INSERT
は失敗します。 ステートメントは終了されました。 

テーブルの中身を表示させてみると、登録されていないことが分かります。

NULL挿入エラー

NULLの挿入方法

INSERT文でNULLを挿入するには、VALUESの中でそのまま「NULL」と書きます。NULLは文字列ではないので”で囲んではだめです。

一方、Management Studioでの操作では、「Ctrl」 + 「0 (ゼロ)」でNULLの挿入が可能です。

NULLの挿入方法

「NOT NULL 制約」ですので、NULLを許容しないことを表す制約になります。

NOT NULL 制約の設定方法

NOT NULL 制約を設定するには、Management Studio のテーブルデザイナで行います。初期値は「NULLを許容する」にチェックが入る(NULLを許容することになる)ので、このチェックを外せばNOT NULL になります。

NULLを許容するのチェックを外す

PRIMARY KEY 制約

PRIMARY KEY 制約は、主キー制約とも言い、主キーとしての条件(重複不可、NOT NULL)を満たしているかどうかをチェックする制約です。PRIMARY KEY 制約には、NOT NULL 制約が含まれていますので、PRIMARY KEY 制約を設定した列に対してNOT NULL 制約を指定する必要はありません。

先ほどの社員テーブルの場合、主キーは設定されていないので、重複したデータを登録できてしまいます。

重複が可能な状態

 INSERT INTO Table_Syain VALUES('00006', '前田 六実', 22, '女', NULL, NULL) -------------------------------------------------------------------------- (1 行処理されました) 

重複した状態

3-1 PRIMARY KEY 制約の設定方法

それでは、「社員番号」列にPRIMARY KEY 制約を設定してみましょう。Management Studio で「社員番号」列を主キーに設定すれば自動的に「社員番号」列にPRIMARY KEY 制約が設定されます。

既に重複したレコードが存在している場合、その重複した列を主キーに設定することはできません。今回の場合は「社員番号」が[00006]のレコードが2件ありますので、エラーが発生してしまいます。

PRIMARY KEYの設定

保存すると、次のようなエラーが出ます。

 テーブル 'Table_Syain' - インデックス 'PK_Table_Syain' を作成できません。 オブジェクト名 'dbo.Table_Syain' およびインデックス名 'PK_Table_Syain' に重複したキーが見つかったので、CREATE UNIQUE INDEX ステートメントは終了しました。重複したキーの値は (00006) です。 制約を作成できませんでした。以前のエラーを調べてください。 ステートメントは終了されました。 

6行目と7行目が重複していますが、この状態ではどちらも全く同じ値なので区別ができません。「社員番号」が[00006]のデータを削除しようとすると、6行目も7行目もどちらも削除されてしまいます。値の重複があるとこういった弊害があります。今回は、2行とも削除して新たに登録し直します。

 DELETE FROM Table_Syain WHERE 社員番号 = '00006' INSERT INTO Table_Syain VALUES('00006', '前田 六実', 22, '女', NULL, NULL) -------------------------------------------------------------------------- (2 行処理されました) (1 行処理されました) 

これで重複は無くなったので、再度「社員番号」に対して主キーの設定を行えば、問題無く保存できると思います。設定したPRIMARY KEY 制約はManagement Studio上からも確認することができます。

PRIMARY KEYの確認

PRIMARY KEY 制約の効果

「社員番号」列は主キーとなり、PRIMARY KEY 制約が設定されましたので、「社員番号」列のみ重複が不可、かつNULL 不可になりましたので実際に試してみましょう。

 INSERT INTO Table_Syain VALUES('00006', '前田 六実', 22, '女', NULL, NULL) -------------------------------------------------------------------------- メッセージ 2627、レベル 14、状態 1、行 1 制約 'PK_Table_Syain' の PRIMARY KEY 違反。オブジェクト 'dbo.Table_Syain' には重複したキーを挿入できません。重複したキーの値は
(00006) です。 ステートメントは終了されました。 

「社員番号」が重複している為、PRIMARY KEY 制約に違反した旨のエラーが出ました。「社員番号」さえ重複していなければそれ以外の項目は重複しても大丈夫です。

 INSERT INTO Table_Syain VALUES('00007', '前田 六実', 22, '女', NULL, NULL) -------------------------------------------------------------------------- (1 行処理されました) 

テーブルの中身を表示してみると、確かに登録できていることが確認できます。

主キー以外は重複可能

次は、NULLの挿入を試してみましょう。「社員番号」列にNULLを指定したINSERT文を実行すると、エラーとなり登録できないようになっています。

 INSERT INTO Table_Syain VALUES(NULL, '七道 剛', 28, '男', NULL, NULL) -------------------------------------------------------------------------- メッセージ 515、レベル 16、状態 2、行 1 テーブル 'TestDB.dbo.Table_Syain' の列 '社員番号' に値 NULL を挿入できません。この列では NULL 値が許可されていません。INSERT
は失敗します。 ステートメントは終了されました。 

これが、PRIMARY KEY 制約の効果です。

UNIQUE 制約

UNIQUE 制約は、その名のとおり列の値がユニーク(一意)となり、重複を禁止する制約です。PRIMARY KEY 制約は、NOT NULL 制約とUNIQUE 制約を合わせたものになります。

PRIMARY KEYの内訳

UNIQUE 制約の設定方法

UNIQUE 制約の設定は、PRIMARY KEY 制約と同様、Management Studioの操作で行えます。今回は社員テーブルの「名前」列にUNIQUE 制約を付けてみたいと思います。

対象のテーブルをデザイナで開き、どれでもいいので列を選択して右クリックし、現れたメニューから「インデックス/キー」を選択します。

UNIQUE KEYの作成1

「インデックス/キー」画面が現れるので、左下の「追加」ボタンをクリックし、右側の「種類」を[一意キー]に変更し、任意で「名前」を変更します。(私は今回「Unique_名前」としました。)最後に「閉じる」ボタンをクリックします。

UNIQUE KEYの作成2

最初の画面に戻って来るので、「Ctrl + S」もしくは保存ボタンをクリックして、UNIQUE 制約設定を保存します。

UNIQUE KEYの作成3

作成したUNIQUE 制約は、PRIMARY KEY 制約と同じように確認できます。

UNIQUE KEYの作成3

CHECK 制約

CHECK 制約は、その名のとおり列の値が条件に該当するかどうかをチェックする制約になります。NOT NULL や UNIQUE と違い、「年齢が20以上」などの条件を指定することになります。

CHECK制約の例

CHECK 制約の設定方法

CHECK 制約も、テーブルのデザイナから設定を行います。UNIQUE 制約と同様に該当テーブルのデザイナを開き、どれでもいいので列を選択した上で右クリック、表示されるメニューから「制約のチェック」を選択します。

CHECK制約の作成1

「CHECK 制約」画面が表示されますので、左下の「追加」ボタンをクリックし、右側の「式」に条件となる式を入力します。今回は「年齢は20以上であること」という条件にしますので、[年齢 >= 20]と入力します。

「名前」は任意で変更すればいいのですが、今回は(CK_年齢)と入力しました。入力が終われば、「閉じる」ボタンをクリックします。

CHECK制約の作成2

最初の画面に戻ってきますので、「Ctrl + S」もしくは「保存」ボタンをクリックして保存を行います。

CHECK制約の作成3

作成したCHECK 制約は、PRIMARY KEY 制約やUNIQUE 制約と違い、「制約」というカテゴリに分類されます。

CHECK制約の作成4

CHECK制約の効果

それでは、年齢は20以上という制約を設けましたので、実際に試してみましょう。年齢が20未満のレコードを登録してみます。その前に一旦、下のSQLを実行して、汚れたデータは削除しておきます。

 DELETE FROM Table_Syain WHERE 社員番号 = '00006' DELETE FROM Table_Syain WHERE 社員番号 = '00007' -------------------------------------------------------------------------- (1 行処理されました) (1 行処理されました) 

改めて、年齢が20未満のレコードを登録してみましょう。

 INSERT INTO Table_Syain VALUES('00006', '前田 六実', 19, '女', '1994-01-06', 180000) -------------------------------------------------------------------------- メッセージ 547、レベル 16、状態 0、行 1 INSERT ステートメントは CHECK 制約 "CK_年齢" と競合しています。競合が発生したのは、データベース "TestDB"、テーブル
"dbo.Table_Syain", column '年齢' です。 ステートメントは終了されました。 

CHECK 制約と競合しています、という旨のエラーですが、要するに制約に合わないからダメということです。もちろん年齢が20以上なら問題なく登録できます。

 INSERT INTO Table_Syain VALUES('00006', '前田 六実', 20, '女', '1993-01-06', 180000) -------------------------------------------------------------------------- (1 行処理されました) 






DEFAULT 制約

DEFAULT制約は、レコードを登録する際に値の指定をしなかった場合に初期値を設定する制約です。「値の指定をしない」とは、NULLを指定することではなく、INSERT文で項目を省略して登録することです。

通常、INSERT文で項目を省略した場合は、その項目にはNULLがセットされます。NULLを許容していた場合はそのままNULLが格納されますが、許容していなかった場合はエラーになります。この時、DEFAULT 制約により初期値を設定していた場合は、その初期値として指定した値が格納されることになります。

明示的にNULLを指定した場合は、(NULLを許容する場合のみですが)DEFAULT制約の有無に関わらずNULLが挿入されます。表に纏めるとこんな感じです。

DEFAULT制約の仕様

DEFAULT 制約の設定方法

DEFAULT 制約もテーブルのデザイナから設定を行います。該当テーブルのデザイナを開き、DEFAULT 制約を設定したい列を選択し、画面下の「列のプロパティ」から「規定値またはバインド」に、初期値として設定したい値を直接入力します。

DEFAULT制約の設定2

入力が終わったら「Ctrl + S」または保存ボタンをクリックし、保存します。

DEFAULT制約の設定3

設定したDEFAULT 制約は、CHECK 制約と同様に「制約」カテゴリーに表示されます。

DEFAULT制約の設定4

DEFAULT 制約の効果

DEFAULT 制約は初期値が設定されることを期待して積極的に利用(わざと省略する)はしない方がよく、毎回きちんと値を指定した方がいいのですが、開発時には非常に便利になります。

よくあるパターンとして、例えば数値型なら[0]を、日付型なら[GETDATE()](現在の日付)を、文字列型なら[”](ブランク)を設定することが多いです。

それでは実際に試してみましょう。本来ありえないのですが、「生年月日」には[GETDATE()]を、「給料」には[0]を指定した状態でレコードを登録します。

DEFAULT制約の効果1

「生年月日」と「給料」を省略したINSERT文を実行してみます。

 INSERT INTO Table_Syain(社員番号, 名前, 年齢, 性別) VALUES('00007', '七道 剛', 28, '男') -------------------------------------------------------------------------- (1 行処理されました) 

確かに、初期値が設定されていることが分かります。

DEFAULT制約の効果2

また、GUIでテーブルを編集している最中も、この初期値は便利な場面があります。

DEFAULT制約の効果3

FOREIGN KEY 制約

「FOREIGN KEY」とは外部キーという意味で、別のテーブルとの参照整合性を保つ目的で使用されます。外部キーを作成したら、その列に対して「外部キー制約」が設定されます。

例えば、商品マスタと売上テーブルがあり、売上テーブルにはその日何の商品が何個売れたのかを記録しているので商品コードを保有しています。この場合、商品マスタは商品コードを主キーとし、売上テーブルでは商品コードを外部キーとして設定し、商品マスタを参照するようにします。

外部キー制約の特徴として、外部キーとなっている列の値は、外部キー列が参照するテーブル(今回は商品マスタ)に存在する値しか格納できない。かつ、外部キー列に格納されている値を、外部キーが参照するテーブル側で削除することはできないことが挙げられます。要するに、常に整合性が取れた状態を保証する仕組みということです。

外部キー制約1

FOREIGN KEY 制約の設定方法

FOREIGN KEY 制約を設定するには、これまでの社員テーブルとは別に、部署テーブルも必要です。Management Studioで新たに部署テーブルを作成し、既存の社員テーブルも部署コード列を追加しましょう。

社員テーブルの修正

部署テーブルの新設

次に、各テーブルを編集モードで開き、図のとおりデータを入力します。

社員テーブルと部署テーブルにデータを入力

これで準備はOKです。そうしましたら、まず「社員テーブル」をデザイナで開きます。どれでもいいので列を選択して右クリックし、現れたメニューより「リレーションシップ」を選択します。

外部キーの設定方法1

「テーブルと列」画面が現れるので、左下の追加ボタンをクリックします。左側のリストにリレーションシップが追加されますので、右側の「テーブルと列の指定」の[…]ボタンをクリックします。

外部キーの設定方法2

「テーブルと列」画面が現れるので、画像のとおり各項目を入力します。主キーとなるのは「部署テーブル」であり、外部キーとなるのは「社員テーブル」であることに注意です。入力が終われば「OK」ボタンをクリックして画面を閉じます。

外部キーの設定方法3

「外部キー リレーションシップ」画面に戻りますので、「閉じる」ボタンをクリックして画面を閉じます。

外部キーの設定方法4

保存を確認するメッセージが表示されますので、「はい」をクリックします。

外部キーの設定方法5

設定したFOREIGN KEY 制約は、外部キーですので、PRIMARY KEY 制約と同様に「キー」カテゴリーに表示されます。

外部キーの設定方法6

FOREIGN KEY 制約の効果

FOREIGN KEY 制約の効果を試してみましょう。今回の設定は、「社員テーブル」の部署コードは「部署テーブル」に存在するものしか登録できない、かつ、「社員テーブル」に存在している部署コードは、「部署テーブル」から削除できないというものになります。

まずは、「社員テーブル」に既に登録されてあるレコードの部署コードを、「部署テーブル」に存在しない値に更新してみます。

外部キーの効果1

 UPDATE Table_Syain SET 部署コード = 'A999' WHERE 社員番号 = '00001' -------------------------------------------------------------------------- メッセージ 547、レベル 16、状態 0、行 1 UPDATE ステートメントは FOREIGN KEY 制約 "FK_部署コード" と競合しています。競合が発生したのは、データベース "TestDB"、テーブル "dbo.Table_Busyo",
column '部署コード' です。 ステートメントは終了されました。 

はい、確かに外部キー制約に違反しているエラーが出ましたね。次は反対に、「社員テーブル」に存在している部署コードを、「部署テーブル」から削除してみます。

 DELETE Table_Busyo WHERE 部署コード = 'A001' -------------------------------------------------------------------------- メッセージ 547、レベル 16、状態 0、行 1 DELETE ステートメントは REFERENCE 制約 "FK_部署コード" と競合しています。競合が発生したのは、データベース "TestDB"、テーブル "dbo.Table_Syain", column
'部署コード' です。 ステートメントは終了されました。 

外部キーは「社員テーブル」側で、「部署テーブル」側は主キーとして”「社員テーブル」から参照されている”ことになりますので、この場合のエラーは「REFERENCE制約エラー」としてエラーが発生しています。

以上が、テーブルに設定できる各種制約の説明です。PRIMAEY KEY 制約は必ずと言っていいほど使用しますし、それ以外の制約についても場合によっては有効になりますので、良く覚えておきましょう。

この記事と関連性の高い記事