テーブル設計と制約(CREATE TABLE)
概要
- 日程: Day 2 / セッション 9
- 時間: 14:40-15:10
- 形式: 座学
- ゴール: PRIMARY KEY/FOREIGN KEY/NOT NULL/UNIQUE/CHECK 各制約の役割を説明し、簡単なテーブル設計を提案できる
- 学習形式: 対話型解説
導入(5分)
ここまで皆さんは、誰かが作ってくれたテーブルにアクセスして読んだり書いたりしてきました。ここからは「テーブルそのものを自分で作る側」に回ります。
ここで一つ問いです。もし皆さんが「社員管理テーブル」をゼロから設計するとしたら、何を一番気にしますか?列の順番?データ型?……実は、ベテランがまず気にするのは「不正なデータが入らないようにするルール」です。これを SQL では「制約(Constraint)」と呼びます。
このセッションでは、CREATE TABLE と各種制約を通じて「データの嘘を防ぐ仕組み」を学びます。
本編(20分)
1. CREATE TABLEの基本形
テーブル作成のSQLは、形式さえつかめば難しくありません。
コード例・実例
CREATE TABLE departments (
department_id SERIAL PRIMARY KEY,
department_name VARCHAR(100) NOT NULL,
location VARCHAR(100)
);
読み方は「テーブル名、その中に列名・データ型・制約をカンマで並べる」だけです。
ここがポイント
- 列名は
snake_case(小文字+アンダースコア区切り)が業界の主流 - データ型は前日学んだとおり、内容に合うものを選ぶ
- 制約は列の後ろにスペース区切りで何個でも書ける
2. 制約は「データの嘘を防ぐルール」
制約とは、「このテーブルに入れていいデータの決まり」です。これは、いわば入り口に立つガードマンのようなものです。ルールに合わないデータが来たら、データベースが「それはダメ」と弾いてくれます。
主な制約は5つです。
| 制約 | 役割 | 例 |
|---|---|---|
| PRIMARY KEY | 一意かつNOT NULL。行の身分証明書 | department_id |
| FOREIGN KEY | 他テーブルへの参照を保証 | employees.department_id → departments.department_id |
| NOT NULL | 空(NULL)を許さない | 氏名、メールアドレス |
| UNIQUE | 重複を許さない(NULLは複数可) | メールアドレス、社員番号 |
| CHECK | 任意の条件を満たすことを要求 | salary >= 0 |
コード例・実例
これらを盛り込んだ employees テーブルの例(sandbox の実物に近い形)。
CREATE TABLE employees (
employee_id SERIAL PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE,
hire_date DATE NOT NULL,
salary NUMERIC(10, 2) CHECK (salary >= 0),
department_id INTEGER REFERENCES departments(department_id)
);
employee_id SERIAL PRIMARY KEY:自動で1, 2, 3...と採番、かつ一意email VARCHAR(100) UNIQUE:他の社員と重複不可salary NUMERIC(10,2) CHECK (salary >= 0):マイナス給与は禁止(さすがに無いと困りますね)department_id INTEGER REFERENCES departments(department_id):存在しない部署IDは入れられない
ここがポイント
- 主キーは「行を1つに特定できる列」。1テーブルに必ず1つ
- 外部キーは「向こうの主キーを指してね」というルール。これでテーブル同士のつながりが守られる
- NOT NULLは「必ず入れて」、UNIQUEは「他の人と被らないで」、CHECKは「条件に合うものだけ」
コラム
制約をサボると何が起きるか。実話に近い話をひとつ。あるECサイトで「お客様メールアドレス」を UNIQUE にせずに運用していたら、同じメアドの顧客が4人に増殖し、ポイントが分散して「私のポイント、消えた!」というクレームが大発生したそうです。制約は「設計時にちょっと考えるだけで、運用時の数千件の不幸を防ぐ」割のいい仕事。書くのは1行、効果は数年、というわけです。
3. SERIALとDEFAULT:地味だけど超便利
SERIAL(自動採番)
毎回IDを自分で考えなくても、DBが勝手に連番を振ってくれます。
order_id SERIAL PRIMARY KEY
INSERTのときは order_id を指定しなくてよく、1, 2, 3... と自動で増えます。
DEFAULT(初期値)
値を指定しなかったときに使われる「お決まりの値」を設定できます。
status VARCHAR(20) DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
INSERTで status を書かなければ自動で 'pending'、created_at は今この瞬間の時刻が入ります。
ここがポイント
- SERIAL は数値の自動連番。文字列IDが欲しい場合は別の仕組みが必要
CURRENT_TIMESTAMPはDBサーバの時計の今。アプリ側の時計とは別物- 「DEFAULTのおかげで毎回書かなくて済むこと」は、書き手にも読み手にも優しい
4. 命名規則:地味だけど一番効くマナー
実務でぐっと差がつくのが命名です。基本ルール:
- テーブル名は 複数形 の
snake_case(employees,order_items) - 列名も
snake_case(first_name,created_at) - 主キーは
{テーブル単数}_id(employee_id) - 外部キー列は参照先の主キー名と揃える(
departments.department_idを指す列はdepartment_id) - ビューは
v_接頭辞、インデックスはidx_接頭辞(チームのルールに従う)
「自分一人の今のため」ではなく「半年後の自分と隣の同僚のため」に名付ける。これだけで、SQLを書く時間は半分になります。
💬 AIに聞いてみよう
- 「主キーは1つの列じゃないとダメ?複数列の主キー(複合主キー)はどう書くの?」
- 「ON DELETE CASCADEって何?外部キーと組み合わせると何が起こる?」
- 「VARCHAR(100) と TEXT、どう使い分けるべき?」
まとめ(5分)
CREATE TABLE はテーブル名と列定義を並べるだけ。そこに 5つの制約(PRIMARY KEY / FOREIGN KEY / NOT NULL / UNIQUE / CHECK)と SERIAL・DEFAULT を組み合わせて、「不正なデータが入らないテーブル」を作ります。命名規則は地味ですが効果絶大です。
次のセッションでは、皆さん自身に「研修コース」と「受講履歴」のテーブルを設計してもらいます。「どんな列が必要か」「どこに制約をかけるか」をイメージしながら臨んでください。
🔄 振り返りチェック
- 5つの制約をそれぞれ1文で説明できますか?
- 主キーと外部キーの違いと関係を、自分の言葉で言えますか?
- SERIAL と DEFAULT を使うと何が嬉しいか、例を挙げられますか?
補足資料
- 参考リンク: PostgreSQL公式 - CREATE TABLE / 制約
- 発展課題: 自分の身の回り(部活、サークル、家計簿など)から1つ題材を選び、テーブル設計を箇条書きで提案してみる
学習ガイド
想定される質問と回答例
| 質問 | ヒント |
|---|---|
| 主キーがない表ってアリ? | 物理的には作れるが推奨されない。行を一意に特定できないとUPDATE/DELETEで事故る |
| UNIQUE と PRIMARY KEY の違いは? | PRIMARY KEYはUNIQUE+NOT NULL+1テーブル1つ。UNIQUEは複数列に付けられNULLも許す |
| CHECKってどんな式が書けるの? | 1行内で評価できる式ならOK。status IN ('pending','shipped','completed') のような列挙も可 |
| 外部キーを後から付けたい | ALTER TABLE ... ADD CONSTRAINT fk_xxx FOREIGN KEY (...) REFERENCES ... で追加可能 |
つまずきやすいポイント
| つまずきポイント | ヒント |
|---|---|
| 外部キーで「親テーブルに該当する値がない」エラー | 先に親(参照先)にデータを入れてから子にINSERTする |
NOT NULL 列に値を入れずINSERTしてエラー |
DEFAULTを設定するか、INSERT時に必ず値を渡す |
| SERIALで思った番号にならない | 失敗したINSERTでもシーケンスは消費される。番号は「一意で増える」だけと割り切る |