導入
テーブルを作るとき、列に 制約(constraint) を付けられます。制約とは「この列に入れてよいデータのルール」。ルールをデータベース自身に守らせることで、間違ったデータ(必須の欄が空、ID の重複、負の年齢…)がそもそも入らないようにできます。アプリ側のチェックは書き忘れますが、DB の制約は忘れません。ここが「壊れにくい設計」の要です。
説明
代表的な制約を、CREATE TABLE の列定義に書き足していきます。
CREATE TABLE members (
id INTEGER PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
age INTEGER CHECK (age >= 0),
city TEXT DEFAULT '未設定'
);
flowchart TB
PK["PRIMARY KEY(主キー)<br/>行を一意に識別。重複×・空× 1表に1つ"]
NN["NOT NULL<br/>空(NULL)を禁止。必須の欄に"]
UQ["UNIQUE<br/>値の重複を禁止(メール等)"]
DF["DEFAULT 値<br/>INSERT で省略したときの初期値"]
CK["CHECK(条件)<br/>条件を満たす値だけ許す(age >= 0 等)"]
制約が働くか試してみましょう。同じメールを2回入れるとエラーになります。
INSERT INTO members (id, email, age) VALUES (1, 'a@example.com', 20);
INSERT INTO members (id, email, age) VALUES (2, 'a@example.com', 30);
2件目で「UNIQUE constraint failed」というエラーが出れば、制約が守ってくれた証拠です。
外部キー(FOREIGN KEY) は、別のテーブルの行を指す列に付けます。データを消したり変えたりするときに問題になる「参照整合性」を、DB に守らせるための制約です。「この user_id は必ず users に実在する id であること」を表明し、迷子のデータが生まれないようにします。
CREATE TABLE reviews (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
comment TEXT,
FOREIGN KEY (user_id) REFERENCES users(id)
);
SQLite では外部キーのチェックは既定でオフで、有効にするには PRAGMA foreign_keys = ON; を実行します(MySQL / PostgreSQL では既定で有効)。それでも、設計として FOREIGN KEY を書くことは「このテーブルはあの表につながっている」という意図を残す大切な記録になります。
やってみよう
初期表示の members テーブルを作成してから、上の2つの INSERT を続けて実行し、2件目で UNIQUE 制約のエラーが出ることを確かめましょう。次に city を省略した INSERT INTO members (id, email, age) VALUES (3, 'b@example.com', 40); を入れ、SELECT * FROM members; で city が「未設定」になっていることを見てください。
演習
id(整数・主キー)、name(文字列・NOT NULL)、stock(整数・0以上の CHECK・DEFAULT 0)を持つ items テーブルを作ってください。
ヒント1を見る
stock の列には INTEGER CHECK (stock >= 0) DEFAULT 0 のように制約を並べて書けます。
ヒント2を見る
CREATE TABLE items (id INTEGER PRIMARY KEY, name TEXT NOT NULL, stock INTEGER CHECK (stock >= 0) DEFAULT 0);