命名規則のベストプラクティス
このレッスンで分かること
- SQL コミュニティの標準は
snake_case+ 複数形テーブル名 (users,orders)- 外部キーは
<参照テーブル単数形>_id(user_id)、ブール値はis_プレフィックス- インデックスは
idx_*、制約はfk_*uq_*chk_*のプレフィックスで種別を明示
命名規則のベストプラクティス とは
データベース設計における命名規則の重要性と、SQLで推奨されるベストプラクティスを実践的に解説します。
なぜ重要か
テーブル名・カラム名・インデックス名は、一度本番に出ると改名のコストが高く、ずっとコードと運用に付きまといます。命名がチームで揃っていないと、user_id と userId と uid が同じテーブル内に混在し、JOIN のたびにドキュメントを読み直すような事態になります。命名規則 は 書き手のお気持ち ではなく、読み手とクエリプランナへの契約 です。
ここでは、現場で広く使われている命名規則と、なぜそれが推奨されるのかを整理します。
命名規則の典型パターン
SQL の世界で最も普及しているのは snake_case + 複数形のテーブル名スタイルです。
この図のように、テーブル名は複数形 (users)、主キーは id、外部キーは <参照テーブルの単数形>_id (role_id) という単純なルールを守るだけで、クエリの読みやすさが劇的に上がります。
| 種類 | 命名ルール | 例 |
|---|---|---|
| テーブル | snake_case + 複数形 | users order_items |
| 主キー | id 単独 | id |
| 外部キー | <参照単数形>_id | user_id role_id |
| ブール値 | is_ / has_ / can_ | is_active has_avatar |
| タイムスタンプ | <動詞ed>_at | created_at registered_at |
| 論理削除 | deleted_at | deleted_at (NULL は未削除) |
| 列挙型 | <対象>_type / _status | customer_type order_status |
代表例 命名規則違反のリファクタ
命名がバラついているテーブルを見てみましょう。
SQL クエリ
CREATE TABLE Customer (
CustomerID INT PRIMARY KEY,
customerName VARCHAR(100),
RegDate TIMESTAMP,
isActiveFlg BOOLEAN,
CustomerType VARCHAR(20)
);問題は次のとおりです。
- テーブル名が
PascalCaseで単数形 - カラム名が
PascalCaseとcamelCaseの混在 FlgやRegDateという省略形がチーム外に伝わらない- ブール値かどうかが命名から読めない
統一すると次のようになります。
SQL クエリ
CREATE TABLE customers (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
name VARCHAR(100) NOT NULL,
registered_at TIMESTAMP NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
customer_type VARCHAR(20) NOT NULL
);is_active のように ブール値は is_ has_ can_ で始める と、SELECT 結果を見たときに型が一目で分かります。日時は <動詞 + ed>_at (registered_at, created_at) とすると、それが「いつ起きたか」を表すことが明確になります。
命名は「自分の好み」ではなく、その言語コミュニティ全体の慣習に合わせるのが正解です。Rails / Django / Laravel など主要な ORM がすべて snake_case + 複数形を採用しているため、これに合わせるとライブラリと衝突しません。
インデックスと制約の命名
インデックス名と制約名にも規則があります。プレフィックスで種類が分かるようにします。
- インデックス
idx_<table>_<columns>(例idx_orders_user_id_ordered_at) - 一意制約
uq_<table>_<columns>(例uq_users_email) - 外部キー
fk_<table>_<ref_table>(例fk_orders_users) - チェック制約
chk_<table>_<rule>(例chk_products_price_positive)
これらの名前は SHOW CREATE TABLE や information_schema でそのまま現れるため、トラブル時のログ追跡が早くなります。
注意 名前の長さ制限に注意しましょう。MySQL の識別子は最大 64 文字ですが、Oracle は 30 文字 (11g) / 128 文字 (12c 以降) と分かれています。クロス DB を想定するなら 30 文字以内に収めるのが無難です。
「コードは読まれる時間の方が書かれる時間より大きくに長い」というのは Robert C. Martin の有名な言葉ですが、これはスキーマにもそのまま当てはまります。命名で 5 秒迷うくらいなら、迷う必要のない規則を最初に決めて全員で守るほうが、長期的に得です。
代表例 命名から外部キーを推測する
命名規則を揃えておくと、ER 図がなくても次のような JOIN が直感的に書けます。
SQL クエリ
SELECT
o.id AS order_id,
u.name AS user_name,
p.name AS product_name
FROM orders AS o
JOIN users AS u ON u.id = o.user_id
JOIN order_items AS oi ON oi.order_id = o.id
JOIN products AS p ON p.id = oi.product_id;orders.user_id が users.id を、order_items.order_id が orders.id を、order_items.product_id が products.id を指しているのが命名だけで分かります。これは命名規則がきちんと揃っているチームの特権です。
ここまでの要点
snake_case + 複数形を SQL コミュニティ標準として採用。FK は <参照単数形>_id、ブール値は is_*、日時は _at。インデックス・制約は種別プレフィックス (idx_ fk_ uq_ chk_) で見分け。
まとめ
- snake_case + 複数形のテーブル名が SQL コミュニティの標準
- 外部キーは
<参照テーブル単数形>_idで統一 - ブール値は
is_/has_/can_プレフィックス、日時は_atサフィックス - インデックス・制約には種類が分かるプレフィックスをつける
- 命名規則は読み手とプランナへの契約。一度決めたら厳守する
演習
バラついた命名のテーブル定義の一部を information_schema 風のメタデータで保持しています。命名規則違反を検出してください。
次のレッスン
次は インデックスの仕組み です。SQLでのデータ検索を高速化するインデックスの仕組みを、SQLでの操作を通して実践的に学びます。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- 命名規則 の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. 命名規則 とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
テーブル構造
schema.sql
CREATE TABLE column_metadata (
id INT PRIMARY KEY,
table_name VARCHAR(50) NOT NULL,
column_name VARCHAR(50) NOT NULL,
data_type VARCHAR(20) NOT NULL
);
INSERT INTO column_metadata VALUES
(1, 'users', 'id', 'BIGINT'),
(2, 'users', 'email', 'VARCHAR'),
(3, 'users', 'createdAt', 'DATETIME'),
(4, 'orders', 'id', 'BIGINT'),
(5, 'orders', 'UserID', 'BIGINT'),
(6, 'orders', 'total_amount', 'DECIMAL'),
(7, 'products', 'ProductName', 'VARCHAR'),
(8, 'products', 'price', 'DECIMAL'),
(9, 'reviews', 'id', 'BIGINT'),
(10, 'reviews', 'rating', 'INT');期待される出力
| table_name | column_name |
|---|---|
| users | createdAt |
| orders | UserID |
| products | ProductName |