CHECK 制約は、ある意味テーブルの入り口にいるガードマンみたいなもの。テーブルに追加するデータが、決められた条件に合ってるかどうかを保証してくれる。もし条件に合わないデータを入れようとしたら、データベースはそれを拒否するよ。
例えば、ドイツでお店を開きたいとする。でも法律で 日曜日は休み で、その日は営業禁止なんだ。これがデータベースの CHECK 制約みたいなもの。お店のスケジュールを 週7日営業 って設定しようとすると、システムがすぐに「"Nein, nein, nichiyoubi wa kinshi. Sonna schedule wa NG da yo!"」って返してくる。
データベースでも同じ。CHECK のルールに違反する値を入れようとしたら、システムがブロックして「ロジックエラー」を防いでくれる。
なんで CHECK が必要なの?
- データ品質の維持:
CHECKはテーブルに変なデータやロジックに合わない情報が入るのを防いでくれる。 - ミスの減少: データを追加する前に毎回手動でチェックしなくても、データベースに任せられる。
- ロジックの自立性: チェックルールをデータベース構造に組み込めるから、アプリ側で毎回書かなくてOK。
CHECK はどうやって動くの?
CHECK 制約はテーブル作成時に指定できるし、あとから ALTER TABLE コマンドで追加もできる。基本のシンタックスはこんな感じ:
CREATE TABLE テーブル (
カラム データ型 CHECK (条件)
);
条件 は、指定したカラムの値ごとに成り立つべきロジック式だよ。条件に違反したら、データベースがエラーを返す。
例1: 値の範囲チェック
例えば students テーブルを作って、学生の年齢(age)が16〜100の間だけOKにしたいとする:
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
age INTEGER CHECK (age >= 16 AND age <= 100)
);
これで、もし12歳の学生を追加しようとしたら、データベースが「ダメだよ!」って止めてくれる:
INSERT INTO students (name, age)
VALUES ('Maria Chi', 12);
エラー:
ERROR: new row for relation "students" violates check constraint "students_age_check"
DETAIL: Failing row contains (1, Maria Chi, 12).
そう、データベースはここで厳しい門番。16歳未満はダメだよ。
例2: データ形式のチェック
例えば emails テーブルがあって、メールアドレス一覧を保存してるとする。アドレスに @ が入ってるかだけでもチェックしたい(簡易バージョン):
CREATE TABLE emails (
id SERIAL PRIMARY KEY,
email VARCHAR(255) CHECK (email LIKE '%@%')
);
条件に合わないアドレスを追加しようとすると:
INSERT INTO emails (email)
VALUES ('notanemail.com');
エラー:
ERROR: new row for relation "emails" violates check constraint "emails_email_check"
DETAIL: Failing row contains (1, notanemail.com).
全部のデータに @ が入ってればエラーは出ない:
INSERT INTO emails (email)
VALUES ('example@student.com');
このクエリはちゃんと通るよ。
例3: 複数カラムの条件チェック
CHECK 制約は1つのカラムだけじゃなくて、複数カラムにまたがるロジック式もOK。例えば employees テーブルで、給料(salary)がボーナス(bonus)より大きくなきゃダメってルール:
CREATE TABLE employees (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
salary NUMERIC CHECK (salary > 0),
bonus NUMERIC CHECK (bonus >= 0),
CHECK (salary > bonus)
);
これで、ボーナスが給料より多い社員を追加しようとしたら、データベースが止めてくれる:
INSERT INTO employees (name, salary, bonus)
VALUES ('Otto Lin', 3000, 4000);
エラー:
ERROR: new row for relation "employees" violates check constraint "employees_salary_bonus_check"
DETAIL: Failing row contains (1, Otto Lin, 3000, 4000).
実践的な使い方
CHECK 制約は、ビジネスロジックがデータ制約と密接に関係してる時に超便利。例えば:
- ネットショップ: マイナス価格の商品を追加禁止。
- 教育プラットフォーム: コース参加者の年齢チェック。
- 医療システム: 患者の体温が許容範囲内かチェック。
こういうチェックは、ただのセキュリティレイヤーじゃなくて、開発者やユーザーの手間とストレスを減らしてくれるんだ。
特徴とよくあるミス
CHECK を使う時は、次のポイントに注意しよう:
CHECKのロジック式は、テーブルの全行でtrueにならなきゃダメ。もし1行でも違反してたら、制約追加前にその行を直す必要があるよ。挿入する値が
NULLの場合、チェックは実行されない。つまりCHECK (age >= 18)でもage = NULLならエラーにならない。これは、NULLを含む式は自動的に未定義になるから。NULLを禁止したいならNOT NULLも追加しよう。CHECKの条件が複雑すぎると、大きなテーブルでデータ追加・更新時にパフォーマンスが落ちることもある。
GO TO FULL VERSION