命名規則の Tips — テーブル・カラム名で後悔しないための約束事
snake_case・{table}_id 主キー・is_* / *_at など、後から楽になる命名規約。
基本は snake_case — 識別子クォートから逃げる
テーブル名・カラム名は すべて小文字 + アンダースコア区切り(snake_case) を基本とします。キャメルケース(CustomerName)やパスカルケース(customerName)を避けるのは、RDBMS 間での大文字小文字の扱いが揃っていないためです。
- PostgreSQL: 引用符なしで書かれた識別子は自動で小文字化される →
CustomerNameと書いてもcustomernameとして保存 - Oracle: 同上、自動で大文字化される →
CUSTOMERNAMEとして保存 - MySQL: OS 依存で、Linux ではテーブル名が大文字小文字区別される / Windows では区別されない
- SQL Server: 照合順序(collation)次第で挙動が変わる
大文字を使いたいときは "CustomerName" のように ダブルクォートで囲み続ける 必要があり、アプリ・運用クエリ・ツールすべてに波及する大きな負債になります。最初から小文字だけで書いておけば、この問題は発生しません。
合わせて決めておきたいルール:
- 数字始まりの識別子を避ける(
2024_salesは多くの RDBMS で NG) - ハイフンを使わない(
order-itemsは引用符必須) - 予約語(
user,order,group,typeなど)は避けてusers/orders/groups/type_codeにする
マスタ / トランザクションに prefix — mst_ / trn_
テーブルを マスタ系(mst_) と トランザクション系(trn_) で prefix を分けておくと、規模が大きくなった時に一覧性・検索性が劇的に上がります。
| Prefix | 性質 | 例 |
|---|---|---|
mst_ | マスタ:変化が遅い参照データ。件数は少なく、FK の参照先になる | mst_customers / mst_products / mst_departments |
trn_ | トランザクション:業務で日々積まれるデータ。FK で mst_ を参照する | trn_orders / trn_order_items / trn_shipments |
log_ / hst_ | ログ・履歴:削除せず積むだけ。監査用途 | log_login / hst_price_changes |
rel_ / map_ | 多対多の交差テーブル(任意) | rel_user_roles |
メリット:
- ツールのテーブル一覧が役割でグルーピングされる(アルファベット順で並べるだけで整理される)
- 運用 SQL で
LIKE 'trn_%'のようにマスタを除いた集計や、バックアップ範囲の指定が楽 - 新人が「これは参照して良いマスタか、それとも業務データか」を名前だけで判別できる
注意点・バリエーション:
- prefix を 途中から導入すると移行が重いので、プロジェクト開始時に決めること。既存システムに後付けするのは現実的でないことが多い
- SaaS・アプリ系では prefix を使わず素直に
customers/ordersとする文化もある(Rails/Laravel 系の標準はこちら) - 業務系・基幹系・日本の SIer 文化では
M_/T_のように大文字 prefix も多いが、前項の通り snake_case に揃えるほうが無難
-- マスタ
CREATE TABLE mst_customers (
customer_id BIGINT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
tel VARCHAR(20)
);
-- トランザクション
CREATE TABLE trn_orders (
order_id BIGINT PRIMARY KEY,
customer_id BIGINT NOT NULL REFERENCES mst_customers(customer_id),
ordered_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- ログ
CREATE TABLE log_login (
log_id BIGSERIAL PRIMARY KEY,
customer_id BIGINT NOT NULL,
logged_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);主キーは id ではなく {table}_id
多くのフレームワーク(Rails, Django 等)は慣例で主キーを id にしますが、「テーブル名 + _id」で主キーを命名する ルールにしておくと、JOIN・デバッグ・運用 SQL で一気に楽になります。
例: customers.id ではなく customers.customer_id、orders.id ではなく orders.order_id。
メリット:
- JOIN 条件がシンプル:
ON orders.customer_id = customers.customer_id、あるいはUSING (customer_id)がそのまま使える - FK 側と PK 側のカラム名が一致するので、どちらを参照しているか混乱しない
- 集計 SQL で
SELECT customer_id, COUNT(*)のように書いたとき、どのテーブルの id か一目でわかる SELECT id FROM orders JOIN customers ON ...のような曖昧な書き方がそもそもできず、エラーで弾ける
デメリット・反論:
- ORM(Active Record 系)は
id固定がデフォルトで、primary_key :customer_idのような設定が必要になる場面がある - カラム名がわずかに冗長になる(
customers.customer_id)
それでも、データ量が増え・結合が増えるほど「どの id か」が明確な方が強いため、基幹系・業務系ではほぼ一貫して {table}_id が採用されます。
-- {table}_id 方式なら USING が自然に使える
SELECT c.name, o.ordered_at
FROM trn_orders o
JOIN mst_customers c USING (customer_id);
-- id 固定方式だとこう書くことになる
SELECT c.name, o.ordered_at
FROM orders o
JOIN customers c ON o.customer_id = c.id;外部キー列は「参照先の主キー名」と同じにする
前項と併せて、FK 列は参照先の PK と同名にする のを原則にします。mst_customers.customer_id を参照する FK は、どのテーブルでも customer_id。
例外は同じテーブルを複数回参照するときです。その場合は役割を表す prefix を付けて区別します。
- 荷物の発送元・発送先が両方
mst_addressesを参照:from_address_id/to_address_id - 承認フローで承認者と申請者が両方
mst_usersを参照:applicant_user_id/approver_user_id - 作成者・更新者:
created_by_user_id/updated_by_user_id(単にcreated_byでも可)
こうしておくと、カラム名を見ただけで参照先が一意に決まるため、ER 図なしでもスキーマの読解が進みます。
カラムの接頭辞・接尾辞で型・意味を表す
カラム名に意味を表す接頭辞・接尾辞を一貫して付けると、SQL を書くときに型や扱い方を名前だけで想起できます。
| 規則 | 意味 | 例 |
|---|---|---|
is_* / has_* | 真偽値(BOOLEAN)。flag 単独は避ける | is_active / is_deleted / has_paid |
*_at | 時刻(TIMESTAMP / TIMESTAMPTZ) | created_at / shipped_at / expires_at |
*_on / *_date | 日付のみ(DATE) | birth_on / invoice_date |
*_count / *_total | 件数・集計済み数値 | order_count / amount_total |
*_id | 他テーブルへの参照(FK) / 主キー | customer_id / order_id |
*_code | 業務で定まる短いコード値。区分値のキー | status_code / currency_code |
*_name | 人が読む名称 | product_name / department_name |
*_type / *_kind | 区分・種別 | payment_type / item_kind |
*_url / *_path | URL / ファイルパス | avatar_url / upload_path |
アンチパターン:
flag1,flag2のような番号付きの意味不明なフラグ列(何の真偽かわからない)date単独(予約語かつ日付と時刻のどちらか曖昧)name,typeのようなテーブルをまたぐと衝突する汎用名(customer_name/product_nameのように具体的に)- ビジネス用語を略しすぎた
cst_nm,prd_cd(古い基幹系に残る文化。可読性が大幅に下がる)
単数形 vs 複数形 — チームで 1 つに決める
テーブル名を 単数形(customer, order)にするか 複数形(customers, orders)にするかは、長年の宗教論争でどちらにも合理性があるため、チームで 1 つに決めて全テーブルで統一することだけが重要です。
- 複数形派(Rails, Laravel, Web 系多数): 「テーブルは行の集合」という英文法的直感。
SELECT * FROM customersは「複数の顧客を取る」で自然 - 単数形派(Hibernate, .NET, 業務系多数): ドメインモデルのクラス名と 1:1 対応しやすく、予約語衝突しにくい(
userと違ってusersは予約されない、という皮肉な理由で複数形派が多数)
前述の mst_ / trn_ prefix 文化とは合わせて考えます。日本の業務系では mst_customer(単数)が多く、Web 系では customers(複数)が多い傾向です。混在だけが避けるべき状態で、どちらでも成立します。
識別子の長さ制限に注意
RDBMS ごとに識別子(テーブル名・カラム名)の最大長に差があります。とくに Oracle の 30 文字制限 に引っかかると、自動生成されるインデックス名・FK 名(mst_customers_customer_id_fkey のような長い名前)が切れて衝突するトラブルが起きます。
| RDBMS | 最大文字数 | 備考 |
|---|---|---|
| Oracle(〜12c) | 30 | インデックス名・FK 名を含めて注意 |
| Oracle(12.2+) | 128 | 12.2 以降で緩和 |
| PostgreSQL | 63 | NAMEDATALEN で拡張可能(再ビルド必要) |
| MySQL | 64 | テーブル・カラム共通 |
| SQL Server | 128 | ほぼ困らない |
| SQLite | 制限なし | 実質数千文字まで OK |
Oracle サポートが視野にあるなら、テーブル名は 20 文字程度まで に収めておくと、FK・インデックス名が 30 文字内に収まって安全です。
まとめ — 一枚の規約シートに書いて共有
命名規則は一貫していれば詳細はあまり重要ではないため、最初にチームで決めて、1 枚の規約シートに書いて共有するのが最大の効果を生みます。
ミニマムで決めておくこと:
- ケース: snake_case(小文字固定)
- テーブル prefix:
mst_/trn_/log_を使うか、使わないか - テーブルの単複: 単数形か複数形か
- 主キー:
idか{table}_idか - 時刻列:
*_at(TIMESTAMP)、*_on(DATE) - 真偽値:
is_*/has_*必須 - FK 列名: 参照先 PK と同名、例外のときの prefix ルール
- 予約語回避リスト(
user,order,group,typeなど)
これらを最初の 1 週間で決めておけば、後から来るメンバー全員が迷わず同じスキーマを書けるようになります。途中から変えるのはほぼ不可能なので、プロジェクト開始直後が最良のタイミングです。
