PostgreSQL: PostgreSQLのテーブル作成と変更
最終更新:2026-08-26
テーブルはデータベースで最も中心的なオブジェクトです。データを格納する「スプレッドシート」であ��、各行がレコード、各カラムがフィールドです。
1. 学習目標
- CREATE TABLEによるテーブル作成(カラム定義、制約、デフォルト値)
- PostgreSQLの一般的なデータ型の概要
- PRIMARY KEY / FOREIGN KEY / UNIQUE / NOT NULL / CHECK制約
- ALTER TABLEによるテーブル構造の変更
- DROP TABLEによるテーブル削除
- psqlの
\dによるテーブル構造の確認
2. ECチームの実話
(1) 課題: 3つのコアテーブルの設計方法
Aliceのチームに新しい要件が来ました。ECシステムに3つのコアテーブル——users(ユーザー)、products(商品)、orders(注文)——を作成することです。要件は以下の通りです。
- ユーザーテーブル: emailは一意、パスワードはNOT NULL、登録時刻は自動記録
- 商品テーブル: 価格は0より大きい、在庫はマイナス不可
- 注文テーブル: ユーザーと商品にリンク、ステータスは固定値のセットに制限
Aliceは迷いました。制約はどこに置くべきか?データ型はどう選ぶか?外部キーのカスケード削除はどう設定す��か?
(2) 解決策: CREATE TABLE + 制約
PostgreSQLではテーブル作成時にすべての制約を宣言でき、データベースがデータ品質を保証してくれます。
-- emailの一意性と自動タイムスタンプ付きのユーザーテーブル作��
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL,
password_hash CHAR(60) NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 価格 > 0 チェック付きの商品テーブル作成
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(200) NOT NULL,
price DECIMAL(10, 2) CHECK (price > 0),
stock INTEGER DEFAULT 0 CHECK (stock >= 0),
attributes JSONB DEFAULT '{}'::jsonb
);
-- 外部キーとステータスチェック付きの注文テーブル作成
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
product_id INTEGER REFERENCES products(id),
quantity INTEGER NOT NULL CHECK (quantity > 0),
status VARCHAR(20) DEFAULT 'pending'
CHECK (status IN ('pending', 'paid', 'shipped', 'delivered', 'cancelled')),
created_at TIMESTAMPTZ DEFAULT NOW()
);
(3) 成果
- データ品質がデータベースレベルで保証(価格 < 0の商品は挿入不可)
- 外部キーカスケード削除(ユーザー削除でそのユーザーの全注文も自動削除)
- デフォルト値でアプリケー��ョン層のコードを削減(
created_atが自動入力)
3. CREATE TABLE構文
(1) 完全な構文構造
CREATE TABLE [IF NOT EXISTS] table_name (
column_name data_type [column_constraint ...],
...
[, table_constraint ...]
);
(2) カラムレベル制約
| 制約 | 構文 | 説明 |
|---|---|---|
| PRIMARY KEY | column_name type PRIMARY KEY |
主キー(一意+NOT NULL) |
| NOT NULL | column_name type NOT NULL |
NULL不可 |
| UNIQUE | column_name type UNIQUE |
値は一意である必要 |
| DEFAULT | column_name type DEFAULT value |
デフォルト値 |
| CHECK | column_name type CHECK (condition) |
条件チェック |
| REFERENCES | column_name type REFERENCES table(col) |
外部キー参照 |
(3) テーブルレベル制約
-- 複合主キー
CREATE TABLE order_items (
order_id INTEGER,
product_id INTEGER,
quantity INTEGER,
PRIMARY KEY (order_id, product_id)
);
-- カスケード付きの名前付き外部キー
CREATE TABLE comments (
id SERIAL PRIMARY KEY,
user_id INTEGER,
content TEXT,
CONSTRAINT fk_user
FOREIGN KEY (user_id)
REFERENCES users(id)
ON DELETE SET NULL
);
4. データ型の概要
PostgreSQLは40以上のデータ型をサポートしています。最も一般的なものを以下に示します。
| カテゴリ | 型 | 説明 | 例 |
|---|---|---|---|
| 整数 | SMALLINT |
2バイト、-32768~32767 | 年齢、少量 |
INTEGER (INT) |
4バイト、約±21億 | 一般的な整数(推奨デフォルト) | |
BIGINT |
8バイト、約±9.2兆 | 大規模ID、金額(セント単位) | |
| 自動採番 | SERIAL |
自動採番INTEGER | 主キーID(推奨) |
BIGSERIAL |
自動採番BIGINT | 大規模テーブルの主キー | |
IDENTITY |
SQL標準の自動採番(PG 10+) | 新規プロジェクトに推奨 | |
| 浮動小数点 | REAL |
4バイト、6桁精度 | 科学計算 |
DOUBLE PRECISION |
8バイト、15桁精度 | 科学計算 | |
| 固定小数点 | DECIMAL(p,s) |
正確な小数 | 価格、金額(推奨) |
NUMERIC(p,s) |
DECIMALと同▶ | 同上 | |
| 文字列 | VARCHAR(n) |
可変長、上限あり | 名前、email |
TEXT |
可変長、上限なし | 説明、本文 | |
CHAR(n) |
固定長 | ハッシュ値、コード | |
| 真偽値 | BOOLEAN |
true / false / null | フラグ |
| 日付 | DATE |
日付 | 誕生日 |
TIMESTAMP |
日時(タイムゾーンなし) | ローカル時刻 | |
TIMESTAMPTZ |
日時+タイムゾーン | グローバルアプリ(推奨) | |
INTERVAL |
時間間隔 | 期間計算 | |
| UUID | UUID |
128ビットUUID | 分散ID |
| JSON | JSON |
テキストJSON | 互換性良好 |
JSONB |
バイナリJSON(推奨) | 高速インデックス付きクエリ |
5. 制約の詳細
(1) PRIMARY KEY
graph TB
PK[PRIMARY KEY] --> U[UNIQUE<br/>重複値不可]
PK --> NN[NOT NULL<br/>空にできない]
PK --> IDX[自動インデックス<br/>B-Treeインデックスが自動生成]
▶ サンプル: 単一カラムと複合主キー
-- 単一カラム主キー(最も一般的)
CREATE TABLE categories (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
-- 複合主キー(ジャンクションテーブル用)
CREATE TABLE product_tags (
product_id INTEGER REFERENCES products(id),
tag_id INTEGER REFERENCES tags(id),
PRIMARY KEY (product_id, tag_id)
);
Output:
CREATE TABLE
(2) FOREIGN KEY
| カスケードアクション | ON DELETEの動作 | ON UPDATEの動作 |
|---|---|---|
CASCADE |
カスケード削除(ユーザー削除→その注文も削除) | カスケード更新 |
SET NULL |
NULLに設定 | NULLに設定 |
SET DEFAULT |
デフォルト値に設定 | デフォルト値に設定 |
RESTRICT |
削除拒否(デフォルト動作) | 更新拒否 |
NO ACTION |
RESTRICTと同じ(SQL標準) | RESTRICTと同じ |
▶ サンプル: 外部キーとカスケードアクション
-- 注文明細: 注文削除→その全明細も削除(CASCADE)
-- 商品: 商品削除→product_idをNULLに設定(SET NULL)
CREATE TABLE order_items (
id SERIAL PRIMARY KEY,
order_id INTEGER REFERENCES orders(id) ON DELETE CASCADE,
product_id INTEGER REFERENCES products(id) ON DELETE SET NULL,
quantity INTEGER NOT NULL DEFAULT 1
);
Output:
CREATE TABLE
(3) CHECK制約
▶ サンプル: CHECK制約によ��データ品質保護
-- 価格は正の値、割引額は価格を超えない
CREATE TABLE promotions (
id SERIAL PRIMARY KEY,
product_id INTEGER REFERENCES products(id),
discount_price DECIMAL(10, 2) CHECK (discount_price > 0),
start_date DATE NOT NULL,
end_date DATE NOT NULL,
-- PGのCHECKは同じ行の他のカラムを参照可能!
CHECK (end_date > start_date)
-- 注意: CHECKはテーブルをまたぐサブクエリを参照できません。テーブル間の検証にはトリガーが必要です
);
Output:
CREATE TABLE
6. ALTER TABLE
(1) 一般的な変更操作
| 操作 | 構文 | 説明 |
|---|---|---|
| カラム追加 | ALTER TABLE t ADD COLUMN col type |
カラムを追加 |
| カラム削除 | ALTER TABLE t DROP COLUMN col |
カラムを削除 |
| カラム名変更 | ALTER TABLE t RENAME COLUMN old TO new |
カラム名を変更 |
| 型変更 | ALTER TABLE t ALTER COLUMN col TYPE new_type |
データ型を変更 |
| デフォルト設定 | ALTER TABLE t ALTER COLUMN col SET DEFAULT val |
デフォルト値を設定 |
| デフォルト削除 | ALTER TABLE t ALTER COLUMN col DROP DEFAULT |
デフォルト値を削除 |
| NOT NULL設定 | ALTER TABLE t ALTER COLUMN col SET NOT NULL |
NOT NULLに設定 |
| NOT NULL削除 | ALTER TABLE t ALTER COLUMN col DROP NOT NULL |
NULL許可 |
| 制約追加 | ALTER TABLE t ADD CONSTRAINT name CHECK (...) |
CHECKを追加 |
| テーブル名変更 | ALTER TABLE old_name RENAME TO new_name |
テーブル名を変更 |
▶ サンプル: テーブル構造の変更
-- 新規カラム追加
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
-- デフォルト値付きで追加
ALTER TABLE users ADD COLUMN is_active BOOLEAN DEFAULT true;
-- カラム型の変更(互換性のない型にはUSINGが必要)
ALTER TABLE users ALTER COLUMN phone TYPE TEXT;
-- カラム名変更
ALTER TABLE users RENAME COLUMN phone TO phone_number;
-- デフォルト値設定
ALTER TABLE users ALTER COLUMN is_active SET DEFAULT true;
-- CHECK制約の追加
ALTER TABLE products ADD CONSTRAINT price_reasonable
CHECK (price < 100000);
-- カラム削除
ALTER TABLE users DROP COLUMN IF EXISTS phone_number;
-- テーブル名変更
ALTER TABLE users RENAME TO customers;
Output:
-- SQL statement executed successfully
ALTER TABLE ... ALTER COLUMN ... TYPE ... USING ...を使用し、USING句で正しく変換してください。
7. DROP TABLE
▶ サンプル: テーブル削除
-- 安全な削除(テーブルが存在しなくてもエラーにならない)
DROP TABLE IF EXISTS test_table;
-- カスケード削除(ビューなどの依存オブジェクトも��除)
DROP TABLE IF EXISTS users CASCADE;
Output:
-- SQL statement executed successfully
| オプション | 説明 |
|---|---|
IF EXISTS |
テーブルが存在しなくてもエラーにならない |
CASCADE |
このテーブルに依存するオブジェクト(ビュー、外部キー参照など)も削除 |
RESTRICT |
依存オブジェクトがある場合削除を拒否(デフォルト動作) |
pg_dumpでバックアップを取ってください。
8. psqlでテーブル構造を確認する
| コマンド | 機能 | 例 |
|---|---|---|
\dt |
全テーブル一覧 | \dt |
\dt+ |
テーブル一覧(サイズ情報付き) | \dt+ |
\d tablename |
テーブル構造表示 | \d users |
\d+ tablename |
テーブル構造表��(詳細) | \d+ users |
▶ サンプル: テーブル構造の確認
# 現在のデータベースの全テーブル一覧
\dt
# usersテーブルの構造表示
\d users
Output:
Table "public.users"
Column | Type | Collation | Nullable | Default
-------------+------------------------+-----------+----------+-----------------------------------
id | integer | | not null | nextval('users_id_seq'::regclass)
email | character varying(255) | | not null |
name | character varying(100) | | not null |
password_hash | character(60) | | not null |
created_at | timestamp with time zone | | | now()
Indexes:
"users_pkey" PRIMARY KEY, btree (id)
"users_email_key" UNIQUE CONSTRAINT, btree (email)
9. 完全なサンプル: ECコア3テーブル
-- ============================================
-- 完全なサンプル: ECコアテーブル
-- 適切な制約付きのユーザー、商品、注文テーブル
-- ============================================
-- 1. ユーザーテーブル
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL,
password_hash CHAR(60) NOT NULL,
role VARCHAR(20) DEFAULT 'customer'
CHECK (role IN ('customer', 'admin', 'manager')),
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- 2. 商品テーブル
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(200) NOT NULL,
description TEXT,
price DECIMAL(10, 2) NOT NULL CHECK (price > 0),
stock INTEGER DEFAULT 0 CHECK (stock >= 0),
category VARCHAR(50),
attributes JSONB DEFAULT '{}'::jsonb,
is_available BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 3. 外部キー付き注文テーブル
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
status VARCHAR(20) DEFAULT 'pending'
CHECK (status IN ('pending', 'paid', 'shipped', 'delivered', 'cancelled')),
total_amount DECIMAL(12, 2) DEFAULT 0 CHECK (total_amount >= 0),
shipping_address TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- 4. 注文明細テーブル(ジャンクションテーブル)
CREATE TABLE order_items (
id SERIAL PRIMARY KEY,
order_id INTEGER NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
product_id INTEGER NOT NULL REFERENCES products(id) ON DELETE RESTRICT,
quantity INTEGER NOT NULL CHECK (quantity > 0),
unit_price DECIMAL(10, 2) NOT NULL CHECK (unit_price > 0),
subtotal DECIMAL(12, 2) GENERATED ALWAYS AS (quantity * unit_price) STORED
);
-- 5. サンプルデータ挿入
INSERT INTO users (email, name, password_hash) VALUES
('alice@example.com', 'Alice', '$2a$12$dummyhashforalice1234567890abcdefghijklmnopqr'),
('bob@example.com', 'Bob', '$2a$12$dummyhashforbob1234567890abcdefghijklmnopqrstuv');
INSERT INTO products (name, price, stock, category, attributes) VALUES
('Running Shoes', 89.99, 150, 'Footwear', '{"color": "red", "size": 42}'::jsonb),
('Laptop Backpack', 49.99, 300, 'Accessories', '{"color": "black", "material": "nylon"}'::jsonb);
-- 6. テーブル構造の確認
\d users
\d products
\d orders
\d order_items
❓ よくある質問
CREATE INDEX CONCURRENTLY(後続のレッスンで説明)を使用するか、メンテナンスウィンドウ中に実行してください。📖 まとめ
- CREATE TABLEでテーブル構造を定義。カラム定義にはデータ型+制約+デフォルト値を含む
- データ型選択: 整数はINTEGER/SERIAL、金額はDECIMAL、時刻はTIMESTAMPTZ、柔軟な構造はJSONB
- 5つの主要制約: PRIMARY KEY / FOREIGN KEY / UNIQUE / NOT NULL / CHECK
- 外部キーカスケード: CASCADE(カスケード)/ SET NULL / RESTRICT(デフォルト)——ビジネスニーズに応じて選択
- ALTER TABLEでカラムの追加/削除/名前変更、型や制約の変更が可能
- DROP TABLEは不可逆。IF EXISTSでエラー防止、CASCADEで依存オブジェクトも削除
- psqlの
\dでテーブル構造表示、\dtで全テーブル一覧
📝 練習問題
-
基本(★):
categoriesテーブル(id, name, 説明, created_at)を作成し、nameはNOT NULLかつUNIQUEにしてください。3行のテストデータを挿入後、\d categoriesでテーブル構造を確認してください。 -
中級(★★): このレッスンで作成したEC 3テーブルを基に、ALTER TABLEを使用して
usersテーブルにlast_login_at TIMESTAMPTZカラムとlogin_count INTEGER DEFAULT 0カラムを追加してください。さらにlogin_countが負にならないCHECK制約を追加してください。 -
チャレンジ(★★★):
product_reviewsテーブルを作成してください。id(主キー)、product_id(productsを参照する外部キー)、user_id(usersを参照する外部キー)、rating(1~5、CHECK制約)、タイトル、content、created_at。要件: ユーザー削除時はレビューを保持するがuser_idをNULLに設定、商品削除時はその商品の全レビューをカスケード削除してください。