PostgreSQL 入門

PostgreSQL データベース制約

制約(Constraints)は、PostgreSQL データベースにおけるデータの整合性(Integrity)と一貫性(Consistency)を保証するための基礎です。これらはデータが遵守すべきルールとして機能し、不正確、不整合、または不完全な情報がストレージに保存されるのを防ぎます。制約がなければ、データベースはすぐに信頼性を失い、管理が困難になります。本章では、4つの主要な制約タイプである NOT NULLUNIQUEPRIMARY KEY、および FOREIGN KEY について、その目的、実装方法、そしてデータベース設計への影響を深く掘り下げていきます。

1. 制約の理解

制約は、データテーブルのカラム(列)に強制的に適用されるルールです。これらはテーブルに挿入できるデータのタイプを制限することで、データの正確性と信頼性を維持します。制約はテーブル作成時(CREATE TABLE)に定義することも、後から ALTER TABLE ステートメントを使用して追加することも可能です。制約は主に以下の2つのレベルに分けられます。

  • カラムレベル制約 (Column Constraints): 単一のカラムに適用され、その特定のカラムに許可されるデータのみに影響します。
  • テーブルレベル制約 (Table Constraints): テーブル全体に適用され、通常は複数のカラムにまたがる制約条件に使用されます。

2. NOT NULL 制約 (非空制約)

NOT NULL 制約は、カラムに NULL(空)値が含まれないことを保証します。すべての行でデータを提供しなければならない「必須項目」のカラムにおいて、この制約は極めて重要です。

2.1 目的と用法

NOT NULL 制約の主な目的は、重要なカラムに欠損値や未定義の値が入るのを防ぐことです。例えば、顧客テーブルにおいて、顧客のメールアドレスは必須情報とされるべきであり、その場合は NOT NULL として定義します。

2.2 構文と例

NOT NULL 制約は、テーブル作成時に定義するか、既存のテーブルに追加することができます。

テーブル作成時に NOT NULL を使用する:

CREATE TABLE employees (
    employee_id SERIAL PRIMARY KEY,
    first_name VARCHAR(50) NOT NULL, -- 名は必須
    last_name VARCHAR(50) NOT NULL,  -- 姓は必須
    email VARCHAR(100) UNIQUE NOT NULL, -- メールアドレスはユニークかつ必須
    hire_date DATE,
    job_id INTEGER
);

この例では、first_namelast_name、および emailNOT NULL として定義されています。これは、各従業員のレコードにおいて、これらのカラムに具体的な値を必ず提供しなければならないことを意味します。

既存のテーブルに NOT NULL を追加する:

ALTER TABLE employees ALTER COLUMN hire_date SET NOT NULL;

このステートメントは、hire_date カラムに NOT NULL 制約を追加します。ただし、hire_date カラムにすでに NULL 値が含まれている場合、この実行は失敗します。制約を追加する前に、まず適切なデータでこれらの NULL 値を更新(Update)する必要があります。

-- すべての空の入社日を現在の日付に更新する
UPDATE employees SET hire_date = CURRENT_DATE WHERE hire_date IS NULL;

NULL 値の更新が完了すれば、正常に NOT NULL 制約を追加できるようになります。

3. UNIQUE 制約 (唯一制約)

UNIQUE 制約は、カラム内のすべての値が互いに異なる(ユニークである)ことを保証します。1つのテーブルに複数の UNIQUE 制約を設定できますが、PRIMARY KEY(プライマリキー)制約は1つしか設定できません。

3.1 目的と用法

UNIQUE 制約は、重複が許されないカラムに対して一意性を強制するために使用されます。ただし、それらのカラムが必ずしも行を識別するためのメインの識別子である必要はありません。

3.2 構文と例

NOT NULL と同様に、UNIQUE 制約もテーブル作成時に定義するか、後から追加することができます。

テーブル作成時に UNIQUE を使用する:

CREATE TABLE users (
    user_id SERIAL PRIMARY KEY,
    username VARCHAR(50) UNIQUE NOT NULL, -- ユーザー名はユニークかつ必須
    email VARCHAR(100) UNIQUE NOT NULL   -- メールアドレスもユニークかつ必須
);

ここでは usernameemailUNIQUE として定義されており、各ユーザーは世界に一つだけのユーザー名とメールアドレスを持つ必要があります。デフォルトでは UNIQUE 制約は1つの NULL 値を許容するため、ここでは NULL を防ぐために NOT NULL 制約も併用しています。

既存のテーブルに UNIQUE を追加する:

ALTER TABLE users ADD CONSTRAINT unique_username UNIQUE (username);

これにより、username カラムに UNIQUE 制約が追加されます。コード例のように制約に名前(例:unique_username)を付けることで、将来的な管理が容易になります。

4. PRIMARY KEY 制約 (主键制約)

PRIMARY KEY(プライマリキー)制約は、テーブル内の各レコードを一意に識別します。これは事実上、NOT NULLUNIQUE の組み合わせであり、プライマリキーのカラムは NULL 値を含むことができず、かつ各行の値はユニークでなければなりません。1つのテーブルには1つのプライマリキーしか存在できず、1つまたは複数のカラムで構成されます(複数カラムの場合は複合キーと呼ばれます)。

4.1 目的と用法

PRIMARY KEY 制約は、リレーショナルデータベース設計の要(かなめ)です。テーブル内の各行をユニークに特定できることを保証し、それによって他のテーブルとの関連付け(リレーション)を可能にします。

4.2 構文と例

テーブル作成時に PRIMARY KEY を使用する:

CREATE TABLE products (
    product_id SERIAL PRIMARY KEY, -- 自動採番されるユニークなID
    product_name VARCHAR(100) NOT NULL,
    price DECIMAL,
    description TEXT
);

この例では、product_idproducts テーブルのプライマリキーです。SERIAL キーワードは、そのカラムに対して一意かつ増分(インクリメント)する整数を自動生成します。

複合プライマリキー (Composite Primary Key):

CREATE TABLE order_items (
    order_id INTEGER,
    product_id INTEGER,
    quantity INTEGER,
    PRIMARY KEY (order_id, product_id) -- 注文IDと製品IDの組み合わせで一意性を担保
);

ここでは、order_idproduct_id の組み合わせがプライマリキーとなっており、「特定の注文における特定の製品」の組み合わせが必ずユニークになることを保証しています。

既存のテーブルに PRIMARY KEY を追加する:

ALTER TABLE products ADD CONSTRAINT pk_products PRIMARY KEY (product_id);

5. FOREIGN KEY 制约 (外部キー制約)

FOREIGN KEY(外部キー)制約は、2つのテーブル間のデータにリンクを作成するために使用されます。これにより、1つのテーブル(参照テーブル/子テーブル)内の値が、別のテーブル(被参照テーブル/親テーブル)に必ず存在することを保証します。子テーブルの外部キーカラムは、親テーブルのプライマリキーカラムを指し、これによって 参照整合性 (Referential Integrity) が強制されます。

5.1 目的と用法

FOREIGN KEY 制約は、リレーショナルデータベースの各テーブル間で関係性を構築するために不可欠です。関係の一貫性を保証し、孤立したレコード(親テーブルに存在しない値を参照している子テーブルのレコード)の発生を防ぎます。

5.2 構文と例

テーブル作成時に FOREIGN KEY を使用する:

CREATE TABLE departments (
    department_id SERIAL PRIMARY KEY,
    department_name VARCHAR(50) NOT NULL,
    location VARCHAR(50)
);

CREATE TABLE employees (
    employee_id SERIAL PRIMARY KEY,
    first_name VARCHAR(50) NOT NULL,
    last_name VARCHAR(50) NOT NULL,
    department_id INTEGER,
    -- department_id カラムを departments テーブルの id に紐付ける
    FOREIGN KEY (department_id) REFERENCES departments(department_id)
);

この例では、employees テーブルの department_id カラムが外部キーであり、departments テーブルの department_id(プライマリキー)を参照しています。これにより、すべての従業員が実在する部署に配属されていることが保証されます。

既存のテーブルに FOREIGN KEY を追加する:

ALTER TABLE employees ADD CONSTRAINT fk_department
FOREIGN KEY (department_id) REFERENCES departments(department_id);

5.3 ON DELETE および ON UPDATE の振る舞い

外部キーを定義する際、参照される行(親テーブルの行)が削除または更新されたときに実行するアクションを指定できます。

アクション説明
ON DELETE CASCADE親テーブルの行が削除されたとき、子テーブルの一致する行も自動的に削除されます。
ON UPDATE CASCADE親テーブルのプライマリキー値が更新されたとき、子テーブルの外鍵値も自動的に更新されます。
ON DELETE SET NULL親テーブルの行が削除されたとき、子テーブルの外部キーカラムに NULL が設定されます(カラムが NULL を許可している必要があります)。
ON DELETE SET DEFAULT親テーブルの行が削除されたとき、子テーブルの外部キーカラムにデフォルト値が設定されます。
ON DELETE RESTRICT子テーブルに一致する行がある場合、親テーブルの行の削除をブロックします(デフォルトの挙動)。

ON DELETE CASCADE を使用する例:

CREATE TABLE employees (
    employee_id SERIAL PRIMARY KEY,
    first_name VARCHAR(50) NOT NULL,
    last_name VARCHAR(50) NOT NULL,
    department_id INTEGER,
    -- 部署が削除されたら、その部署に属する従業員も自動的に削除する
    FOREIGN KEY (department_id) REFERENCES departments(department_id) ON DELETE CASCADE
);

もし departments テーブルから特定の部署を削除すると、employees テーブル内でその部署に所属していたすべての従業員のレコードも一括で削除されます。