Info2softは、ウェブサイトでより快適で適切な閲覧体験を提供するためにCookieを使用しています。 プライバシーポリシー
Loading...
SQL制約とは、データ品質を維持するためデータベースエンジンが直接強制するルールのことです。スキーマ層のガードレールとして機能し、各行がストレージに書き込まれる前に定められた基準を満たしているか検証します。
開発者が制約を活用する理由は、アプリケーション側のバリデーションだけでは完全な信頼性が確保できないからです。手動実行するSQLスクリプト、ミドルウェアの不具合、データベースへの直接接続など、あらゆる経路からアプリロジックを迂回してデータが登録される可能性があります。データベース層に定義した制約は強制的な遮断機構となり、データの流入経路を問わず正しいデータを保ちます。
制約には2つの定義方法があります。
また制約の適用範囲は2種類に分かれます。
SQL制約とインデックスの違い
制約とインデックスは混同されがちですが役割が異なります。制約は値の検証・制限によりデータ整合性を保ち、インデックスはデータ検索を高速化しクエリパフォーマンスを改善します。
簡単に言うと、制約はデータの品質を守り、インデックスはデータへのアクセスを最適化する機能です。
代表的なSQL制約の概要、役割、一般的な利用シナリオを一覧にまとめます。
| 制約名 | 役割 | 代表的な使用例 |
|---|---|---|
| NOT NULL | 値の欠損を禁止 | ユーザーメール、注文日時 |
| UNIQUE | 重複値を禁止 | ユーザー名、個人番号、商品SKU |
| PRIMARY KEY | 行の一意識別子 | 従業員ID、商品ID |
| FOREIGN KEY | テーブル間の関連性を維持 | 部門紐付け、顧客注文情報 |
| CHECK | 値のルールを強制 | 給与>0、年齢≧18 |
| DEFAULT | デフォルト値を設定 | アクティブステータス、作成日時 |
以降のサンプルでは共通のemployeesテーブル、departmentsテーブルのスキーマを使用し、各制約の実務的な動作を紹介します。
NOT NULL制約は列にNULL値を格納できないようにする制約で、常にデータが存在する状態を保ちます。識別子、ステータス項目、連絡先などアプリロジック上必須の列に適用する重要な制約です。
-- 標準SQL
CREATE TABLE departments (
dept_id INT NOT NULL,
dept_name VARCHAR(100) NOT NULL
);
代表的な利用場面:従業員名、メールアドレス、タイムスタンプ、ステータス項目。
よくあるミスとして外部キー列にNULLを許可する設定が挙げられます。これにより、親テーブルに存在しない・紐付け先のない孤立レコードが作成される恐れがあります。
UNIQUE制約は列内の値がすべて一意になるよう保証します。主キーと異なり1テーブルに複数のUNIQUE制約を定義可能で、NOT NULLと違いNULL値の登録を許可する仕様(データベース製品による細かな差異あり)です。
-- 標準SQL
CREATE TABLE employees (
employee_id INT NOT NULL,
email VARCHAR(255) UNIQUE,
social_security_number VARCHAR(11) CONSTRAINT unq_ssn UNIQUE
);
複数列を対象にテーブルレベルでUNIQUEを定義し、複数項目の組み合わせの一意性を強制することもできます。例:同一従業員が同一プロジェクトに重複してアサインされないよう制御する場合
-- 複合一意制約
ALTER TABLE project_assignments
ADD CONSTRAINT unq_emp_project UNIQUE (employee_id, project_id);

主キーはテーブル内の各レコードを一意に識別する制約で、NOT NULLとUNIQUEの性質を両方持ち合わせています。対象列には必ず値が存在し、全行で値が重複しません。
適切に設計されたテーブルには必ず1つの主キーを設定するべきです。主キーがないと特定の行を確実に参照・更新する手段が存在しなくなります。
-- 標準SQL
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL
);
外部キーは一方のテーブルの列を、別テーブルの主キーと紐付ける制約で、参照整合性を保ちます。親テーブルに存在しない値を登録することはできず、既定の設定では子レコードが存在する状態で親レコードを削除することも禁止されます。
-- 標準SQL
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
dept_id INT,
FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
ON DELETE CASCADE
);
削除時の動作として代表的な2種類を覚えておきましょう。
データモデルに合わせて選択してください。CASCADEは便利ですが、設定を誤ると意図しないデータ消失が発生するため注意が必要です。
CHECK制約は列の値を論理式で検証し、条件に違反する行の登録・更新を拒否します。アプリケーションコードに依存せず、データベース層で直接業務ルールを強制できます。
-- 標準SQL
ALTER TABLE employees
ADD CONSTRAINT chk_salary CHECK (salary > 0);
ALTER TABLE employees
ADD CONSTRAINT chk_hire_date CHECK (hire_date >= '2000-01-01');
DEFAULT制約はINSERT文で列の値が省略された際に適用される代替値を定義します。オプション項目の処理をデータベース側に委ね、アプリケーションコードを簡略化できます。
-- 標準SQL
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
is_active BOOLEAN DEFAULT TRUE,
country VARCHAR(50) DEFAULT 'USA'
);
ステータスフラグや固定の地域名など、予測可能な値に対してDEFAULTを活用しましょう。「1900-01-01」といった仮の日付や任意の数値をデフォルトに設定するのは避けてください。データが真に不明な場合はNULLを使用する方が集計結果に歪みが生じません。
データベーススキーマは静的なものではなく、アプリケーションの進化に伴い業務要件に合わせてルールの追加・修正が必要になります。
最もクリーンな手法はCREATE TABLE構文内で制約を事前定義することで、テーブル作成当初から不正なデータが登録されることを防ぎます。
-- 標準SQL
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
email VARCHAR(255) CONSTRAINT unq_emp_email UNIQUE,
salary DECIMAL(10, 2) CONSTRAINT chk_min_wage CHECK (salary > 0)
);
本番環境では既にデータが存在するテーブルに新たなルールを追加する場面が多く、ALTER TABLEコマンドを使用します。
既存の行が新しい制約ルールに違反している場合、データベースは処理全体を拒否するため、稼働中のテーブルに制約を追加する前に事前にデータ監査を実施する必要があります。
-- 既存テーブルに外部キーを追加
ALTER TABLE employees
ADD CONSTRAINT fk_employee_dept
FOREIGN KEY (dept_id) REFERENCES departments(dept_id);

業務ルールが変更になった際はDROP CONSTRAINTで制約を削除します。制約名を正確に指定する必要があるため、命名規則を統一することが重要な理由の一つです(次のセクションで解説)。
-- 標準SQL
ALTER TABLE employees
DROP CONSTRAINT chk_min_wage;
開発者が陥りがちなミスは、データベースに制約名を自動生成させることです。結果としてSYS_C001023のような名前が割り当てられ、エラーログに出力されてもどのルールが違反したか判別できません。
最初から統一した命名規則を使用しましょう。
pk_employees:employeesテーブルの主キーfk_employees_dept:employeesからdepartmentsへの外部キーchk_employees_salary:salary列のCHECK制約unq_employees_email:email列のUNIQUE制約本番環境で「制約違反」エラーが発生した際、可読性の高い制約名からどのテーブルのどのルールが破られたか即座に把握でき、調査の手間を削減します。
SQL規格で制約の基本的な動作が定められていますが、各エンジンに独自の仕様の癖が存在します。複数製品を運用したり、環境間でマイグレーションを実施する際にこれらの差異が重要になります。
一覧比較:
| 機能 | MySQL (8.0.16以上) | PostgreSQL | SQL Server |
|---|---|---|---|
| CHECK制約の強制 | 有効 | 有効 | 有効 |
| UNIQUE列への複数NULL登録 | 許可 | 許可 | 不可(1つのみ許可) |
| 遅延可能制約 | 非対応 | 対応 | 非対応 |
| 過去データの検証をスキップし制約追加 | 非対応 | NOT VALID | WITH NOCHECK |
| 主キーの必須化 | InnoDBで必須 | 任意(推奨) | 任意(推奨) |
以下に各製品の差異を詳しく解説します。
長年MySQLはCHECK構文を受け付けるだけで実際の検証を実施しませんでした。構文解析だけ行いエラーを返さず処理を続ける仕様でした。2019年リリースの8.0.16からCHECK制約が正しく強制されるように変更されています。
古いバージョンのMySQLを使用している場合は注意が必要です。バージョンアップ前に登録されたデータは、現在のスキーマのルールに適合していない可能性があります。
3製品の中でPostgreSQLが最も完全な制約機能を備えており、特徴的な機能が遅延可能制約です。トランザクション実行中は一時的に制約の検証を停止し、COMMIT時に一括で検証する動作が可能です。
大量データの一括インポートや、参照整合性が満たされる順番で行を登録する必要がある処理に有用です。
-- PostgreSQL:遅延可能な外部キーを作成
ALTER TABLE employees
ADD CONSTRAINT fk_employee_dept
FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
DEFERRABLE INITIALLY DEFERRED;
PostgreSQLはNOT VALIDオプションに対応し、既存テーブルに制約を追加する際に過去の全行を検証しない設定が可能です。大容量の本番テーブルで全行スキャンによる停止時間を回避したい場合に活用します。
-- PostgreSQL:既存データを検証せず制約追加
ALTER TABLE employees
ADD CONSTRAINT chk_salary CHECK (salary > 0) NOT VALID;
-- 負荷の低い時間帯に別途過去データを検証
ALTER TABLE employees VALIDATE CONSTRAINT chk_salary;
製品間の切り替え時に混乱しやすいポイントが、UNIQUE制約とNULL値の組み合わせです。
MySQLとPostgreSQLは一意列に複数のNULLを許可します。NULLは不明な値を表し、不明な値同士は同一とみなされないためです。一方SQL ServerのUNIQUE制約はNULLを1つだけ許可し、追加のNULLは重複と判定され登録が拒否されます。
SQL ServerはPostgreSQLのNOT VALIDに相当するWITH NOCHECKに対応し、既存の行を検証せず制約を追加できます。
-- SQL Server:既存データを検証せず制約追加
ALTER TABLE employees
WITH NOCHECK ADD CONSTRAINT chk_salary CHECK (salary > 0);
SQL制約は単一のデータベースインスタンス内のデータを保護しますが、サーバー障害、レプリケーションの遅延、複数システムに同一データを保有する際の不整合を防ぐことはできません。
分散データベースやリアルタイム分析パイプラインを運用する企業にとって、スキーマ層のルールは必要ですが十分ではありません。ここで活用するのがi2Streamです。
i2Streamは企業向けデータベースレプリケーションソリューションで、システム間を移動するデータの一貫性を保証します。災害復旧サイトへのレプリケーション、新環境へのマイグレーション、データウェアハウスへのデータ供給など幅広い用途に対応します。
i2Streamはレプリケーション層をカバーします。より包括的なデータ保護が必要な企業向けに、Info2softは物理・仮想・クラウド環境を統合バックアップするi2Backup、自動フェイルオーバーと高可用性を実現するi2Availabilityも提供しています。
SQL制約は正常なデータの定義を定め、i2Streamは連携するすべてのシステムでデータの状態を維持します。
Q1:主キーとUNIQUE制約の違いは何か
どちらも一意性を強制しますが2点の違いが存在します。1テーブルに定義できる主キーは1つのみでNULLを許可しません。UNIQUE制約は複数定義可能で、データベース製品によってNULLを1つまたは複数登録できます。
Q2:制約はデータベースのパフォーマンスに影響するか
影響はありますが軽微です。INSERT/UPDATE/DELETE実行時に毎回検証処理が走るため少しのオーバーヘッドが発生します。実運用では不正なデータの後始末にかかる時間と比較すると、このコストは十分に見合います。また主キー・UNIQUE制約は自動的にインデックスを作成するため、読み取りクエリの速度はむしろ高速化されます。
Q3:テーブルに設定されている制約を確認する方法
大半のデータベースではinformation_schema.table_constraintsビューをクエリすることで全ての有効な制約を一覧表示できます。SQL Serverの場合はsp_help ‘テーブル名’を実行すると、テーブルに適用されている全ルールの詳細一覧が出力されます。
Q4:アプリ側でデータ検証を実装している場合でも制約を使用すべきか
はい、必ず使用してください。アプリ側のバリデーションはユーザー操作性を向上させますが、データベースへの直接接続、手動SQLスクリプト、コードの不具合などで迂回される可能性があります。データベース層に定義した制約は、どの経路からデータが登録されてもデータを守る最後の防衛線となります。
Q5:既にデータが存在する列にNOT NULL制約を追加できるか
列内にNULL値が1つも存在しない場合のみ追加可能です。NULLが残っているとALTER TABLEコマンドが失敗するため、事前にUPDATE文でNULLを有効な値に置き換えてから制約を追加してください。
SQL制約はデータベース設計で活用されにくい便利な機能の一つです。正しく活用すると不正なデータが登録される前段階で検知し、データクリーンアップの手間を削減し、発見困難な静的な不具合を事前に防止します。
本ガイドで紹介した6種類の制約はそれぞれ固有の役割を持ち、NOT NULLによる必須項目の定義からFOREIGN KEYによるテーブル間関連の維持まで対応します。守るべき基本的な習慣は単純です。制約には必ず名前を付ける、稼働中テーブルにルールを追加する前に既存データを監査する、使用しているデータベース製品ごとのNULLやCHECK制約の挙動差異を把握することの3点です。
制約は単一インスタンス内のデータを保護します。複数システム・環境を管理するチームは、レプリケーションとバックアップ戦略と組み合わせることでエンドツーエンドのデータ保護を実現できます。Info2softはデータ保護、レプリケーション、マイグレーション向けのフルラインナップソリューションを提供し、データ整合性が妥協できない企業環境向けに設計されています。