MySQL の CHECK 制約
CHECK 制約は、カラムに格納される値が特定の条件を満たすことをデータベースレベルで保証する仕組みです。MySQL 8.0.16 以降で正式にサポートされ、それ以前のバージョンでは構文は受け付けるものの実際には無視されていました。
基本的な使い方
CHECK 制約を使うと、INSERT や UPDATE の際に条件を満たさない値を拒否できます。
CREATE TABLE products (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
price DECIMAL(10,2) NOT NULL CHECK (price >= 0),
stock INT NOT NULL CHECK (stock >= 0),
discount_rate DECIMAL(3,2) CHECK (discount_rate BETWEEN 0 AND 1)
);
-- 負の価格はエラーになる
INSERT INTO products (name, price, stock) VALUES ('Widget', -100, 10);
-- ERROR: Check constraint 'products_chk_1' is violated.よく使う CHECK 制約のパターン
価格や在庫数が負にならないように CHECK (column >= 0) を設定するのが最も一般的な使い方です。年齢なら CHECK (age BETWEEN 0 AND 150) のように上限も指定できます。
VARCHAR の最大長とは別に、最小長を強制したい場合に使います。CHECK (CHAR_LENGTH(code) = 6) で固定長を強制することも可能です。
CHECK (status IN (‘active’, ‘inactive’, ‘pending’)) のように、許容される値のリストを定義できます。ENUM 型の代替として使えます。
名前付き CHECK 制約
制約に名前を付けると、エラーメッセージが分かりやすくなり、後から制約を削除・変更する際にも便利です。
CREATE TABLE employees (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
salary DECIMAL(12,2) NOT NULL,
hire_date DATE NOT NULL,
CONSTRAINT chk_salary_positive CHECK (salary > 0),
CONSTRAINT chk_hire_date_range CHECK (hire_date >= '2000-01-01')
);
-- 制約の削除
ALTER TABLE employees DROP CHECK chk_salary_positive;名前を付けない場合は MySQL が自動的に テーブル名_chk_N という名前を生成しますが、管理しにくくなるため、意味のある名前を明示的に付ける習慣をつけましょう。
CHECK 制約の制限事項
CHECK 制約にはいくつかの制限があります。
同じ行内のカラムを参照した条件式、リテラル値との比較、組み込み関数の一部(CHAR_LENGTH など)の使用。
サブクエリの使用、他のテーブルの参照、ストアドファンクションの呼び出し、変数の参照。テーブルをまたいだ制約はトリガーで実装する必要があります。
ENUM 型との使い分け
値のリストを制限する方法として、CHECK 制約のほかに ENUM 型もあります。それぞれ特徴が異なるため、用途に応じて使い分けるのがよいでしょう。
ENUM 型はテーブル定義に値のリストが埋め込まれるため、値を追加するには ALTER TABLE が必要になります。一方 CHECK 制約は、制約の削除・追加で柔軟に変更できます。ただし、ENUM は内部的に整数で格納されるためストレージ効率がよく、大量のデータを扱う場合に有利な面もあります。
CHECK 制約はアプリケーションのバリデーションを補完するものであり、置き換えるものではありません。アプリ側でもバリデーションを行いつつ、最終防衛線としてデータベースに CHECK 制約を設定しておくのが堅牢な設計です。



