Skip to main content

テーブル設計の基礎

この章で得られるスキル:

  • ✅ CREATE TABLE文でテーブルを作成できる
  • ✅ 適切なデータ型を選択できる
  • ✅ NOT NULL制約、UNIQUE制約を設定できる
  • ✅ 主キー(PRIMARY KEY)の役割を説明できる
  • ✅ 外部キー(FOREIGN KEY)の役割を説明できる
  • ✅ CHECK制約でデータの妥当性を保証できる

Step 0: まず体験してみよう

シナリオ:制約がないテーブルに不正データを入れてみる

以下のSQLを実行してみよう。 制約がないテーブルには、 どんなデータでも登録できてしまう ことがわかる。

-- 制約がないテーブル CREATE TABLE employees_bad ( emp_id INTEGER, emp_name VARCHAR(50), salary INTEGER, email VARCHAR(100) ); -- 給与が負の数で登録できてしまう! INSERT INTO employees_bad VALUES (1, '田中太郎', -100000, 'tanaka@example.com'); -- 名前がNULL(空)で登録できてしまう! INSERT INTO employees_bad VALUES (2, NULL, 300000, 'unknown@example.com'); -- 同じメールアドレスで重複登録できてしまう! INSERT INTO employees_bad VALUES (3, '佐藤花子', 400000, 'tanaka@example.com'); -- 同じIDで重複登録できてしまう! INSERT INTO employees_bad VALUES (1, '鈴木一郎', 350000, 'suzuki@example.com'); -- 登録されたデータを確認 SELECT * FROM employees_bad;

問題点

  • 給与が マイナス になっている(現実にはあり得ない)
  • 名前が NULL(空) のレコードがある
  • メールアドレスが 重複 している
  • IDが 重複 しており、どのレコードが誰かわからない
重要

制約がないテーブルは、 不正なデータの入り放題 である。 アプリケーション側のバグで不正なデータが送られてきても、データベースが止めてくれない。 適切な制約を設定することで、データベース自体が データの正しさを守る門番 になる。


Step 1: テーブルの作成(CREATE TABLE)

CREATE TABLE文の基本構文

テーブルを作成するには、 CREATE TABLE 文を使う。

CREATE TABLE テーブル名 (
列名1 データ型,
列名2 データ型,
列名3 データ型
);

最もシンプルなテーブルの例

-- 最もシンプルなテーブル CREATE TABLE fruits ( id INTEGER, name VARCHAR(50), price INTEGER ); -- データを追加 INSERT INTO fruits VALUES (1, 'りんご', 150); INSERT INTO fruits VALUES (2, 'みかん', 80); INSERT INTO fruits VALUES (3, 'バナナ', 120); -- 確認 SELECT * FROM fruits;

命名規則

テーブル名や列名には、以下のルールを守ることが推奨される。

ルール良い例悪い例
英小文字 を使うemployeesEmployees
複数形 をテーブル名に使うdepartmentsdepartment
単語の区切りは アンダースコアhire_datehireDate
予約語 を避けるorder_datedate

Step 2: データ型の選択

主なデータ型

テーブルの各列には、 データ型 を指定する。 データ型は「その列にどんな種類のデータを入れるか」を定義する。

データ型意味使用例
INTEGER整数社員ID、年齢、給与
VARCHAR(n)最大n文字の可変長文字列名前、メールアドレス
TEXT長さ制限なしの文字列備考、説明文
DATE日付(年月日)入社日、生年月日
TIMESTAMP日付と時刻作成日時、更新日時
BOOLEAN真偽値(TRUE / FALSE)有効フラグ、削除フラグ

データ型の選び方

データ推奨するデータ型理由
社員IDINTEGER数値で連番にする
社員名VARCHAR(50)名前は50文字あれば十分
メールアドレスVARCHAR(100)一般的なメールアドレスの長さ
入社日DATE年月日だけで十分
給与INTEGER円単位の整数
備考TEXT長さが不定の自由記述
ポイント

VARCHAR(n)n は最大文字数を表す。 必要以上に大きな値を指定する必要はないが、小さすぎて入力できないことがないように注意しよう。


Step 3: NOT NULL制約

NULLとは

NULL とは、「値がわからない」「値がない」状態を表す特別な値である。 空文字("")やゼロ(0)とは異なる。

意味
0数値のゼロ
""空の文字列
NULL値が存在しない

NOT NULL制約の役割

NOT NULL制約 を付けると、その列にNULLを入れることができなくなる。 必ず値が必要な列に設定する。

-- NOT NULL制約を付けたテーブル CREATE TABLE employees_v2 ( emp_id INTEGER, emp_name VARCHAR(50) NOT NULL, -- 名前は必須 salary INTEGER ); -- 名前ありで登録 → 成功 INSERT INTO employees_v2 VALUES (1, '田中太郎', 350000); -- 名前なし(NULL)で登録 → エラー! INSERT INTO employees_v2 VALUES (2, NULL, 300000);

NOT NULLを付けるべき列の例

  • 社員名(名前のない社員は存在しない)
  • 入社日(入社日がわからない社員はおかしい)
  • メールアドレス(業務連絡に必要)

NULLを許可するケース

  • 備考(書かなくてもよい)
  • 退職日(まだ退職していない社員はNULL)

Step 4: UNIQUE制約

UNIQUE制約の役割

UNIQUE制約 を付けると、その列に 同じ値を2回入れることができなくなる

-- UNIQUE制約を付けたテーブル CREATE TABLE employees_v3 ( emp_id INTEGER, emp_name VARCHAR(50) NOT NULL, email VARCHAR(100) UNIQUE -- メールアドレスは重複禁止 ); -- 1人目の登録 → 成功 INSERT INTO employees_v3 VALUES (1, '田中太郎', 'tanaka@example.com'); -- 違うメールアドレスで登録 → 成功 INSERT INTO employees_v3 VALUES (2, '佐藤花子', 'sato@example.com'); -- 同じメールアドレスで登録 → エラー! INSERT INTO employees_v3 VALUES (3, '鈴木一郎', 'tanaka@example.com');

PRIMARY KEYとの違い

UNIQUEPRIMARY KEY
重複不可不可
NULL許可(1つだけ)不可
テーブルに複数設定可能1つだけ
用途メールアドレスなど行の一意識別

Step 5: PRIMARY KEY(主キー)

主キーの役割

主キー(PRIMARY KEY) は、テーブルの各行を 一意に識別 するための列である。

主キーの特徴:

  • 値の 重複が許されない (UNIQUE)
  • NULLが許されない (NOT NULL)
  • テーブルに 1つだけ 設定できる
-- PRIMARY KEYを設定したテーブル CREATE TABLE departments ( dept_id INTEGER PRIMARY KEY, -- 主キー dept_name VARCHAR(50) NOT NULL UNIQUE, location VARCHAR(50) ); -- 正常にデータを登録 INSERT INTO departments VALUES (1, '営業部', '東京'); INSERT INTO departments VALUES (2, '開発部', '大阪'); -- 同じIDで登録 → エラー!(重複不可) INSERT INTO departments VALUES (1, '人事部', '東京'); -- IDがNULLで登録 → エラー!(NULLは不可) INSERT INTO departments VALUES (NULL, '総務部', '福岡');

自然キーと代理キー

主キーの選び方には2つの考え方がある。

自然キー代理キー
説明データそのものをキーにする連番などの人工的なキーを作る
メールアドレス、学籍番号emp_id、dept_id
メリット意味がわかりやすい変更されない
デメリット変更される可能性がある意味がない数値
実務では代理キーが多い

業務でよく使われるのは 代理キー(連番のID) である。メールアドレスや電話番号は変更される可能性があるが、 IDは一度割り当てたら変更しないため、安定した一意識別が可能になる。


Step 6: FOREIGN KEY(外部キー)

外部キーの役割

外部キー(FOREIGN KEY) は、あるテーブルの列が 別のテーブルの主キーを参照する ことを示す制約である。 これにより、テーブル間の 関連性 を定義し、 データの整合性 を保証する。

-- 部門テーブル(参照される側) CREATE TABLE departments ( dept_id INTEGER PRIMARY KEY, dept_name VARCHAR(50) NOT NULL UNIQUE, location VARCHAR(50) ); -- 社員テーブル(参照する側) CREATE TABLE employees ( emp_id INTEGER PRIMARY KEY, emp_name VARCHAR(50) NOT NULL, dept_id INTEGER REFERENCES departments(dept_id), -- 外部キー salary INTEGER ); -- 部門を先に登録(参照先が必要) INSERT INTO departments VALUES (1, '営業部', '東京'); INSERT INTO departments VALUES (2, '開発部', '大阪'); -- 存在する部門IDで社員を登録 → 成功 INSERT INTO employees VALUES (1, '田中太郎', 1, 350000); INSERT INTO employees VALUES (2, '佐藤花子', 2, 400000); -- 存在しない部門ID(99)で社員を登録 → エラー! INSERT INTO employees VALUES (3, '鈴木一郎', 99, 320000);

参照整合性制約

外部キーによって保証されるルールを 参照整合性制約 と呼ぶ。

ルール説明
参照先が存在すること存在しない部門IDを社員テーブルに登録できない
参照されている行は削除できない社員が所属している部門は削除できない(第3章で学ぶ)
注意

外部キーのあるテーブルにデータを追加する際は、 参照先のテーブルに先にデータを登録 する必要がある。 上の例では、departmentsにデータを入れてからemployeesにデータを入れている。順序を逆にするとエラーになる。


Step 7: CHECK制約

CHECK制約の役割

CHECK制約 は、列の値が 特定の条件を満たすこと をチェックする制約である。 ビジネスルールに基づいたデータの妥当性を保証できる。

-- CHECK制約を設定したテーブル CREATE TABLE employees ( emp_id INTEGER PRIMARY KEY, emp_name VARCHAR(50) NOT NULL, salary INTEGER CHECK (salary > 0), -- 給与は正の数 age INTEGER CHECK (age >= 18 AND age <= 65) -- 年齢は18〜65歳 ); -- 正常なデータ → 成功 INSERT INTO employees VALUES (1, '田中太郎', 350000, 30); -- 給与が負の数 → エラー! INSERT INTO employees VALUES (2, '佐藤花子', -100000, 25); -- 年齢が範囲外 → エラー! INSERT INTO employees VALUES (3, '鈴木一郎', 300000, 10);

CHECK制約の使用例

CHECK制約意味
給与CHECK (salary > 0)給与は正の数でなければならない
年齢CHECK (age >= 18 AND age <= 65)年齢は18歳以上65歳以下
評価CHECK (rating >= 1 AND rating <= 5)評価は1〜5の範囲
在庫数CHECK (stock >= 0)在庫数は0以上

Step 8: 実践課題

課題1:商品テーブルを設計しよう

以下の要件を満たす products(商品)テーブルを設計し、 CREATE TABLE 文を書いてみよう。

要件:

  • 商品ID(整数、主キー)
  • 商品名(最大100文字、必須)
  • 価格(整数、0より大きい)
  • 在庫数(整数、0以上)
  • カテゴリ(最大50文字)
-- ここに商品テーブルのCREATE TABLE文を書いてみよう -- 要件に合った型と制約を設定すること -- テストデータの追加 -- INSERT INTO products VALUES (...); -- 確認 -- SELECT * FROM products;

課題2:制約違反を確認しよう

Step 0のように、作成した商品テーブルに対して 制約に違反するデータ を追加してみよう。 エラーメッセージを確認し、制約が正しく機能していることを確かめよう。

課題3:制約一覧の振り返り

以下の表を完成させてみよう(頭の中で答えを考えてから確認する)。

制約役割
NOT NULL
UNIQUE
PRIMARY KEY
FOREIGN KEY
CHECK

まとめ

この章では、 テーブル設計の基礎 を学んだ。

🎯 達成できたこと

  • ✅ CREATE TABLE文でテーブルを作成できるようになった
  • ✅ 適切なデータ型を選択できるようになった
  • ✅ NOT NULL制約、UNIQUE制約を設定できるようになった
  • ✅ 主キーの役割を説明できるようになった
  • ✅ 外部キーの役割を説明できるようになった
  • ✅ CHECK制約でデータの妥当性を保証できるようになった

📚 学んだ内容

制約役割
NOT NULLNULLを禁止し、必ず値を入れさせる
UNIQUE同じ値の重複を禁止する
PRIMARY KEY各行を一意に識別する(NOT NULL + UNIQUE)
FOREIGN KEY別テーブルの主キーを参照し、データの整合性を保つ
CHECK値が特定の条件を満たすことをチェックする

🚀 次のステップ

次の章では、 データの作成・更新・削除 を学ぶ。 INSERT、UPDATE、DELETE文の使い方と、トランザクションの概念を理解しよう。


💡 よくある質問

Q1: CREATE TABLE文を毎回書く必要があるのか?

A: 実務では、テーブルの作成はプロジェクトの初期に一度だけ行うことが多い。Spring Bootでは schema.sql というファイルにCREATE TABLE文を書いておくと、アプリケーション起動時に自動的にテーブルが作成される。ただし、テーブル設計の内容を理解していることは必須である。

Q2: データ型で VARCHAR と TEXT のどちらを使うべきか?

A: 最大文字数がある程度決まっている場合は VARCHAR(n) を使い、不定長の長いテキスト(備考や本文など)には TEXT を使う。 VARCHAR(n) は最大文字数を超えるとエラーになるため、入力ミスの早期発見にも役立つ。

Q3: 主キーは必ず設定しなければならないのか?

A: 技術的にはPRIMARY KEYなしのテーブルも作成できる。しかし、実務では 必ず主キーを設定する のが原則である。主キーがないと、特定の行を確実に識別する手段がなくなり、更新や削除で意図しないデータを操作してしまうリスクがある。

Q4: 外部キーを設定しないとどうなるのか?

A: 外部キーを設定しなくても、テーブル自体は作成できる。しかし、存在しない部門IDで社員を登録できてしまうなど、データの整合性が保てなくなる。外部キー制約は「データベースが自動的にチェックしてくれる安全装置」のようなものである。

Q5: CHECK制約はどこまで複雑な条件を書けるのか?

A: PostgreSQLでは、かなり複雑な条件も書ける(例:CHECK (start_date < end_date))。ただし、あまり複雑なビジネスルールはアプリケーション側でチェックし、データベースの制約はシンプルに保つのが一般的である。


確認問題

SQL記述の約束
  • テーブル名・カラム名は 小文字 で入力する(例: employees, dept_id
  • SQLキーワードは 大文字 で入力する(例: SELECT, FROM, WHERE

正しい例: SELECT emp_name FROM employees WHERE dept_id = 1;

以下のCREATE TABLE文の空欄を埋めて、departments テーブルを作成せよ。

TABLE departments ( dept_id INTEGER
KEY, dept_name VARCHAR(50)
NULL );

解答例
CREATE TABLE departments (dept_id INTEGER PRIMARY KEY, dept_name VARCHAR(50) NOT NULL);
解説

CREATE TABLE でテーブルを作成する。列定義には列名、データ型、制約を記述する。

基本構文:

CREATE TABLE テーブル名 (
    列名1 データ型 制約,
    列名2 データ型 制約,
    ...
);

主な制約

  • PRIMARY KEY: 主キー(各行を一意に識別する。NOT NULL かつ UNIQUE が自動保証)
  • NOT NULL: NULL禁止(必須項目に設定する)
  • UNIQUE: 重複禁止
  • REFERENCES テーブル名(列名): 外部キー
  • CHECK (条件): 条件チェック

社員の名前を格納する列のデータ型として最も適切なものを選べ。

正解

B. VARCHAR(50)(最大50文字の文字列)

解説

文字列を格納するには VARCHAR(n) を使用する。n は格納できる最大文字数である。

主なデータ型

  • INTEGER: 整数(社員ID、年齢など)
  • VARCHAR(n): 最大n文字の可変長文字列(名前、メールアドレスなど)
  • CHAR(n): 固定長n文字の文字列(郵便番号など)
  • DATE: 日付(2024-04-01 の形式)
  • DECIMAL(p, s) / NUMERIC(p, s): 小数を含む数値(金額など)
  • BOOLEAN: 真偽値(TRUE / FALSE)

名前には VARCHAR(50) が適切である。

NOT NULL 制約を設定した列の説明として正しいものを選べ。

正解

B. 値を省略することを禁止する

解説

NOT NULL 制約はその列にNULL値(未入力状態)を禁止する制約である。 必須項目(名前、IDなど)に設定することでデータの欠落を防げる。

各制約の役割

  • NOT NULL: NULL禁止(値の省略を禁止する)
  • UNIQUE: 重複禁止(同じ値の重複を禁止する)
  • データ型(例: INTEGER): 格納できる値の種類を定義する
  • REFERENCES テーブル名(列名): 外部キー(他テーブルの行を参照する)

UNIQUE 制約の説明として正しいものを選べ。

正解

C. 列の値が重複しないことを保証する

解説

UNIQUE 制約はその列に重複した値を入れることを禁止する制約である。 メールアドレスや社員番号など、全行で値が一意である必要がある列に設定する。

各制約との比較

  • NOT NULL: NULLの禁止(値の省略を禁止する)
  • UNIQUE: 重複の禁止(同じ値の登録を禁止する)
  • REFERENCES: 外部キー(他テーブルの行を参照する)
  • CHECK (条件): 値の範囲や形式を制限する

サンプルDBでは employees.emailUNIQUE 制約が設定されており、 同じメールアドレスを持つ社員が複数登録されることを防いでいる。

主キー(PRIMARY KEY)の説明として正しいものを選べ。

正解

A. テーブル内の各行を一意に識別するためのキー(NOT NULL かつ UNIQUE が保証される)

解説

主キー(PRIMARY KEY)はテーブル内の各行を一意に識別する列(または列の組み合わせ)である。

主キーの特性

  • NOT NULL: NULLは許可されない(各行に必ず値が必要)
  • UNIQUE: 重複は許可されない(同じ値は1行のみ)
  • 1テーブルに1つだけ設定できる

各キーの役割

  • 主キー(PRIMARY KEY): 自テーブルの行を一意に識別する
  • 外部キー(FOREIGN KEY / REFERENCES): 他のテーブルの主キーを参照する
  • UNIQUE制約: 重複を禁止するが主キーではない(NULLを許可する点が主キーと異なる)

以下の説明のうち、 外部キー(FOREIGN KEY) の役割として正しいものを選べ。

外部キーは テーブル間の関連 を定義するための制約である。 例えば「社員テーブルの dept_id が、部門テーブルの dept_id を参照する」というように使う。 参照先に存在しない値を登録しようとするとどうなるか、思い出してみよう。

正解

B. 別のテーブルの主キーを参照し、存在しない値の登録を防ぐ制約

解説

外部キー(FOREIGN KEY) は、あるテーブルの列が別のテーブルの主キーを参照することを示す制約である。

外部キーの役割

  • テーブル間の 関連性(リレーション) を定義する
  • 参照先に存在しない値の登録を 自動的に防ぐ(参照整合性制約)

他の選択肢について

  • A(各行を一意に識別)→ これは 主キー(PRIMARY KEY) の役割
  • C(値の重複を防ぐ)→ これは UNIQUE制約 の役割
  • D(NULLを防ぐ)→ これは NOT NULL制約 の役割

外部キーは「データベースが自動的にチェックしてくれる安全装置」であり、存在しない部門IDで社員を登録するようなミスを防いでくれる。

SQL記述の約束
  • テーブル名・カラム名は 小文字 で入力する(例: employees, dept_id
  • SQLキーワードは 大文字 で入力する(例: SELECT, FROM, WHERE

正しい例: SELECT emp_name FROM employees WHERE dept_id = 1;

salary が0より大きい値のみ許可する CHECK 制約を含む列定義の空欄を埋めよ。

salary INTEGER
(salary
0)

解答例
salary INTEGER CHECK (salary > 0)
解説

CHECK 制約は条件式を指定して、条件を満たさない値の挿入・更新を禁止する制約である。

基本構文:

列名 データ型 CHECK (条件式)

使用例

  • salary INTEGER CHECK (salary > 0): 給与は0より大きい値のみ許可
  • age INTEGER CHECK (age >= 0 AND age <= 150): 年齢は0〜150の範囲のみ許可
  • status VARCHAR(20) CHECK (status IN ('active', 'inactive')): 特定の値のみ許可

条件を満たさない値を INSERT または UPDATE しようとするとエラーが発生する。

SQL記述の約束
  • テーブル名・カラム名は 小文字 で入力する(例: employees, dept_id
  • SQLキーワードは 大文字 で入力する(例: SELECT, FROM, WHERE

正しい例: SELECT emp_name FROM employees WHERE dept_id = 1;

以下の要件から projects テーブルの CREATE TABLE 文を完成させよ。

  • project_id: 整数・主キー
  • project_name: 最大100文字の文字列・NULL禁止
TABLE projects ( project_id INTEGER
KEY, project_name
(100) NOT NULL );

解答例
CREATE TABLE projects (project_id INTEGER PRIMARY KEY, project_name VARCHAR(100) NOT NULL);
解説

要件を読んでCREATE TABLE文に変換する手順:

  1. テーブル名を確認する
  2. 各列のデータ型を決定する(整数 → INTEGER、文字列 → VARCHAR(n)、日付 → DATE
  3. 各列の制約を決定する(主キー → PRIMARY KEY、NULL禁止 → NOT NULL、外部キー → REFERENCES

要件の読み方

  • 「主キー」または「ID」→ PRIMARY KEY
  • 「必須」または「NULL禁止」→ NOT NULL
  • 「最大〇文字の文字列」→ VARCHAR(〇)
  • 「整数」または「〜番号」→ INTEGER