📖 テーマ設定
🔊 音声設定
1.2
1.0
1.0
▶️ 再生コントロール
🎵 BGM設定
0.3
🔔 効果音設定
0.3

テーブル設計と制約(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_caseemployees, order_items
  • 列名も snake_casefirst_name, created_at
  • 主キーは {テーブル単数}_idemployee_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 を使うと何が嬉しいか、例を挙げられますか?

補足資料

  • 発展課題: 自分の身の回り(部活、サークル、家計簿など)から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でもシーケンスは消費される。番号は「一意で増える」だけと割り切る
読み上げを開始します...

AIに質問する