2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

私がDB設計でサロゲートキーを選ぶ理由

2
Last updated at Posted at 2026-02-18

はじめに

DB設計で主キーをどう決めるかは、よく議論になるテーマです。大きく分けると2つの方針があります。

  • ナチュラルキー派:ビジネス上の識別子をそのまま主キーにする
  • サロゲートキー派:全テーブルに Id 列を置き、ビジネス上の一意性はユニーク制約で表現する

私はサロゲートキー派です。理由は2つあります。

  1. ビジネスルールの変更でキー構成が変わったときの影響範囲を小さくしたい
  2. ORMとの相性がよい(特にナチュラルキーが複合キーになる場合)

この記事では、なぜ私がサロゲートキーを選ぶのか、具体例を交えて整理します。

ナチュラルキーとサロゲートキーの違い

注文(Orders)と注文明細(OrderItems)を例に、SQL Serverの構文で見てみます。

ナチュラルキー方式

OrderItemsの主キーを (OrderId, LineNumber) とする設計です。

CREATE TABLE OrderItems (
    OrderId INT NOT NULL,
    LineNumber INT NOT NULL,
    ProductId INT NOT NULL,
    Quantity INT NOT NULL,
    PRIMARY KEY (OrderId, LineNumber),
    FOREIGN KEY (OrderId) REFERENCES Orders(Id)
);

ビジネス上の一意性がそのまま主キーになるので、テーブル定義を見ただけで「何がこのレコードを一意に識別するか」がわかります。

サロゲートキー方式

全テーブルに Id 列を追加し、ビジネス上の一意性はユニーク制約で表現します。

CREATE TABLE OrderItems (
    Id INT IDENTITY PRIMARY KEY,
    OrderId INT NOT NULL,
    LineNumber INT NOT NULL,
    ProductId INT NOT NULL,
    Quantity INT NOT NULL,
    CONSTRAINT UQ_OrderItems_OrderId_LineNumber
        UNIQUE (OrderId, LineNumber),
    CONSTRAINT FK_OrderItems_Orders
        FOREIGN KEY (OrderId) REFERENCES Orders(Id)
);

キー構成の変更への強さ

業務システムを長く運用していると、ビジネスルールの変更は避けられません。ナチュラルキーを主キーにしていると、ビジネスルールの変更が主キーの変更に直結します。

たとえば「OrderIdとLineNumberで一意だった明細に、倉庫コード(WarehouseCode)が加わって3列で一意になった」というケースを考えます。

ナチュラルキー方式の場合

主キーの構成変更は破壊的です。

  1. OrderItemsの主キーを (OrderId, LineNumber)(OrderId, LineNumber, WarehouseCode) に変更
  2. OrderItemsを外部キーで参照しているすべてのテーブルに WarehouseCode 列を追加
  3. 関連するインデックス、外部キー制約をすべて再作成
  4. アプリケーション側のFindやJOINも全箇所修正

影響が連鎖的に広がります。

サロゲートキー方式の場合

  1. OrderItemsのユニーク制約を変更
-- 旧
CONSTRAINT UQ_OrderItems_OrderId_LineNumber
    UNIQUE (OrderId, LineNumber)

-- 新
CONSTRAINT UQ_OrderItems_OrderId_LineNumber_WarehouseCode
    UNIQUE (OrderId, LineNumber, WarehouseCode)

これだけです。Id で参照している関連テーブルには一切手を入れる必要がありません。

ORMとの相性

多くのORMは、単一の整数型・UUID型の主キーを前提としたAPIを提供しています。ナチュラルキーが単一列であればまだ問題は少ないですが、複合キーになると途端に扱いが煩雑になります。

私が普段使っているC# + EF Coreでも、この傾向は顕著です。

Findメソッドが不便

サロゲートキーなら直感的に書けます。

var item = await context.OrderItems.FindAsync(orderItemId);

複合キーだとこうなります。

var item = await context.OrderItems.FindAsync(orderId, lineNumber);

FindAsync の引数は params object[] なので、型も個数もコンパイル時にはチェックされません。キーの定義は OnModelCreating にあり、呼び出し側とは離れた場所にあるため、引数の過不足や順序の間違いがあっても実行時まで気づけません。

モデル定義が冗長になる

複合キーはFluent APIでの設定が必須です。

modelBuilder.Entity<OrderItem>()
    .HasKey(x => new { x.OrderId, x.LineNumber });

サロゲートキーなら [Key] 属性をつけるだけ、あるいは Id という命名規則に従えば設定すら不要です。テーブル数が増えるほど、この差が積み重なります。

リレーションの設定も複雑になる

複合キーを外部キーとして参照する側のテーブルでは、複数列をセットで指定する必要があります。関連テーブルが増えるたびに列が増え、Fluent APIの設定も膨れていきます。

サロゲートキーへのよくある懸念

ユニーク制約だとわかりづらくないか

サロゲートキー方式に対してよく挙がる懸念です。ナチュラルキーなら「このテーブルはこの列の組み合わせで一意」というのが主キー定義から一目瞭然ですが、ユニーク制約だと見落とされるのではないか、と。

もっともな懸念です。ただ、以下の運用でカバーできています。

制約に意味のある名前をつける

CONSTRAINT UQ_OrderItems_OrderId_LineNumber UNIQUE (OrderId, LineNumber)

UQ_{テーブル名}_{カラム名} という命名規則を統一しておけば、DDLを見たときにビジネスキーがすぐわかります。

テーブル定義書に明記する

「ビジネスキーは OrderId + LineNumber の組み合わせ」と定義書やカラムコメントに書いておきます。

レビューでチェックする

設計規約として「サロゲートキーを置いたテーブルには必ずビジネスキーのユニーク制約をつける」を明文化し、レビュー観点に入れています。ユニーク制約の付け忘れは、ビジネス上ありえない重複データの登録を許してしまうので、ここは手を抜けないポイントです。

正規化に反しないか

「Id列を追加するのは正規化のルールに反するのでは」と聞かれることがありますが、反しません。

正規化が求めるのは「候補キーが存在し、非キー属性がキーに完全に関数従属していること」です。サロゲートキーを追加しても、ビジネスキーにユニーク制約をつけていれば候補キーは消えていないので、正規化の条件はそのまま満たされます。

Id(OrderId, LineNumber) もどちらも候補キーであり、Id を主キーに選んでいるだけ。候補キーが2つあるテーブルというだけの話です。

まとめ

観点 ナチュラルキー サロゲートキー + ユニーク制約
ビジネス上の意味の明示性 ◎ 主キーがそのまま意味を持つ ○ 命名・規約でカバーが必要
キー構成変更時の影響範囲 △ 関連テーブルに波及 ◎ ユニーク制約の修正だけ
ORMでの扱いやすさ △ 複合キーになると煩雑 ◎ 規約ベースでシンプル
正規化との整合性 ◎(ユニーク制約があれば問題なし)

ナチュラルキーの「意味的なわかりやすさ」は確かに魅力です。ただ、変更への強さとORMとの相性を天秤にかけると、私はサロゲートキーを選びます。

ユニーク制約のわかりづらさは命名規約・ドキュメント・レビューで十分カバーできます。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?