はじめに
本記事では、SQLiteのテーブル制約の種類と使い方を
備忘録としてまとめます。
制約はデータの整合性をDBレベルで保証するための仕組みです。
アプリ側のチェック処理を減らせるので積極的に活用しています。
1. 制約の種類一覧
| 制約 | キーワード | 内容 |
|---|---|---|
| 主キー制約 | PRIMARY KEY |
行を一意に識別する |
| NOT NULL制約 | NOT NULL |
NULL値を禁止する |
| UNIQUE制約 | UNIQUE |
重複値を禁止する |
| CHECK制約 | CHECK |
条件を満たす値のみ許可する |
| DEFAULT制約 | DEFAULT |
値が未指定のときのデフォルト値 |
| 外部キー制約 | FOREIGN KEY |
他テーブルとの参照整合性を保つ |
2. PRIMARY KEY(主キー制約)
行を一意に識別するための制約です。
NULLは禁止・重複も禁止になります。
-- 列レベルで設定
CREATE TABLE users (
id INTEGER PRIMARY KEY, -- 自動採番(ROWIDの別名)
name TEXT
);
-- テーブルレベルで設定(複合主キー)
CREATE TABLE order_parts (
order_id INTEGER,
part_id INTEGER,
PRIMARY KEY (order_id, part_id) -- 2列の組み合わせで一意
);
INTEGER PRIMARY KEYはAUTOINCREMENTと同等の動作をします。
値を省略すると自動的に連番が振られます。
厳密に欠番が出ないようにしたい場合のみ
INTEGER PRIMARY KEY AUTOINCREMENTを使います。
3. NOT NULL制約
NULL値の格納を禁止します。
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL, -- NULLを禁止
age INTEGER -- NULLを許可(デフォルト)
);
-- NULLを入れようとするとエラー
INSERT INTO users (name) VALUES (NULL);
-- Error: NOT NULL constraint failed: users.name
4. UNIQUE制約
重複した値の格納を禁止します。
NULLは複数格納できます(NULLはNULLと等しくないため)。
CREATE TABLE users (
id INTEGER PRIMARY KEY,
email TEXT UNIQUE, -- 重複禁止
name TEXT
);
-- テーブルレベルで複合UNIQUEも設定できる
CREATE TABLE schedules (
user_id INTEGER,
date TEXT,
UNIQUE (user_id, date) -- 同じユーザーの同じ日付は禁止
);
5. CHECK制約
指定した条件を満たす値のみ格納を許可します。
CREATE TABLE products (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
price INTEGER CHECK (price >= 0), -- 0以上のみ許可
stock INTEGER CHECK (stock >= 0), -- 0以上のみ許可
rank TEXT CHECK (rank IN ('A','B','C')) -- 指定値のみ許可
);
-- 条件を満たさない値はエラー
INSERT INTO products (name, price) VALUES ('商品A', -100);
-- Error: CHECK constraint failed: products
6. DEFAULT制約
値が指定されなかった場合に使われるデフォルト値を設定します。
CREATE TABLE logs (
id INTEGER PRIMARY KEY,
message TEXT NOT NULL,
level TEXT DEFAULT 'INFO', -- 固定値
created_at TEXT DEFAULT (datetime('now', 'localtime')), -- 関数も使える
is_read INTEGER DEFAULT 0 -- 0=未読
);
-- messageのみ指定するとlevel・created_at・is_readは自動設定
INSERT INTO logs (message) VALUES ('起動しました');
7. FOREIGN KEY(外部キー制約)
他テーブルの列を参照し、参照整合性を保ちます。
-- 親テーブル
CREATE TABLE parts (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
-- 子テーブル(外部キーあり)
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
part_id INTEGER NOT NULL,
qty INTEGER NOT NULL CHECK (qty > 0),
FOREIGN KEY (part_id) REFERENCES parts(id)
ON DELETE CASCADE -- 親削除時に子も削除
ON UPDATE CASCADE -- 親更新時に子も更新
);
SQLiteの外部キー制約はデフォルトで無効です。
接続のたびに以下を実行して有効化してください。
PRAGMA foreign_keys = ON;
Pythonでは:
conn = sqlite3.connect("data.db")
conn.execute("PRAGMA foreign_keys = ON;")
8. 制約の組み合わせ例
実際のテーブルでは複数の制約を組み合わせて使います。
CREATE TABLE employees (
id INTEGER PRIMARY KEY,
emp_code TEXT NOT NULL UNIQUE, -- 社員コード(必須・重複禁止)
name TEXT NOT NULL, -- 氏名(必須)
department TEXT NOT NULL DEFAULT '未配属', -- 部署(必須・デフォルトあり)
salary INTEGER NOT NULL CHECK (salary >= 0), -- 給与(必須・0以上)
created_at TEXT DEFAULT (datetime('now', 'localtime'))
);
まとめ
| 制約 | 目的 | 注意点 |
|---|---|---|
PRIMARY KEY |
行の一意識別 |
INTEGER PRIMARY KEYは自動採番 |
NOT NULL |
NULL禁止 | デフォルトはNULL許可 |
UNIQUE |
重複禁止 | NULLは複数OK |
CHECK |
値の条件指定 | 複雑な条件も書ける |
DEFAULT |
デフォルト値設定 | 関数も使える |
FOREIGN KEY |
参照整合性 | 有効化にPRAGMA foreign_keys = ONが必要 |
さいごに
テーブル制約を活用していないと
同じDBを扱う別のプログラムができた際、
プログラムごとに型チェックやNULL(None)になっていないか
等の確認処理が必要です。
テーブル制約を活用することで
それらの確認処理をDB側に行なってもらえるので
確認処理を省くことができ、冗長的なプログラムを防止できます。