📑

データ設計Tips:安易に論理削除フラグは作るな。

に公開

1. はじめに

多くのプロジェクトでは、「とりあえず削除フラグを付けておく」という標準ルールが当たり前のように使われています。
しかし、削除フラグは便利どころか、設計の不整合・実装の複雑化・バグ増加・データ品質低下を引き起こす原因になります。

ここでは、以下の図に示すような「商品の履歴管理」を例に、削除フラグの問題点と代替手法を体系的に説明します。

上図左側のような「削除フラグつき商品テーブル」よりも、右側のように
商品(現役データ)と商品履歴(過去データ)を分離する設計
の方が、管理性・整合性・保守性に優れています。


2. 削除フラグを付けるときのよくある“理由”

削除フラグを標準で付与する理由として、以下のような理由が考えられます。

  1. 削除したデータを復活させたい
  2. 削除したデータを参照して新規登録したい
  3. データ履歴を残したい
  4. ディスクI/Oや断片化を防ぎたい(性能問題)
    しかしこれらは、削除フラグ以外の手段で全て解決できます。
    むしろ削除フラグを使うことで新たな問題が発生します。

3. 削除フラグが“標準化”されることの危険性

削除フラグは一見シンプルですが、実際にはアプリケーション全体に複雑さを持ち込みます。
とりわけ危険なのは、「削除フラグの扱いルールが統一されていない」ことです。

■ 例:キャンセルした注文を復活させたい場合

「削除フラグを見て、復活させるかどうか判断する」という仕様にすると、

  • 登録処理
  • 更新処理
  • 検索処理
  • 集計処理
  • 親子テーブル間の整合性(例:注文 ↔ 注文明細)

これらすべてで「削除フラグをどう扱うか」を記述しなければなりません。

▼ 削除フラグの扱いが統一されない例

情報 仕様の曖昧さ
商品テーブル 削除フラグを見て削除扱い
注文明細テーブル 削除フラグを見ない?見てもいい?どちらが正しい?

ルールが曖昧だと、商品を削除すると注文明細が表示されなくなるなど不具合が発生します。


4. 削除フラグが引き起こす問題点

4.1 仕様ミス・テスト漏れを誘発する

削除フラグがあるだけで、多くの処理に条件分岐が追加されます。

SELECT * FROM orders WHERE deleted_flg = false;

この条件を書き忘れると、
削除された注文が“生きている”データとして扱われるバグが簡単に発生します。

JOIN が絡むとさらに致命的です。例えば、注文と注文明細のような親子テーブルの両方に削除フラグがある場合、親子の削除フラグが異なっている場合の表示ルールや、削除した注文を復活したい場合などはどのように処理するか処理条件が複雑になります。


4.2 削除フラグの意味が曖昧になる

削除フラグは、見かけ上は単純なBooleanですが、実際には次のどれを意味するのか曖昧になりがちです。

  • 非表示
  • キャンセル
  • 無効化
  • 一時停止
  • 復活可能状態
  • 削除済み履歴
    この曖昧さがロジックを複雑化させます。

4.3 履歴管理としては不十分

削除フラグを立てても、以下は記録できません。

  • 誰が削除したか
  • いつ削除したか
  • なぜ削除したか
  • どの業務フローから削除されたか

削除フラグは「履歴管理としては不合格」です。


4.4 性能改善の理由は根拠が弱い

「DELETEするとディスクI/Oが増えるから削除フラグが良い」という意見もありますが、近年のDBMSは以下の仕組みを持っています。

  • キャッシュ
  • VACUUM(PostgreSQL)
  • セグメント圧縮
  • 自動最適化

物理削除による性能問題は基本的に気にしなくてOKです。
論理設計で物理最適化を気にするのは間違いです。


5. 削除フラグの代替手段(こちらが正解)

原則として削除フラグは不要です。
目的に応じて、次の手段を使う方がはるかに明確で安全です。


5.1 復活させたい → 状態を持つ(ステータス管理)

例:商品管理区分

・販売前
・販売中
・販売終了

例:注文状況区分

・仮受注
・注文確定
・キャンセル

削除ではなく、状態の変化として管理すべきです。


5.2 過去データを参照したい → 履歴テーブルを使う

削除フラグではなく、履歴テーブルを使います。
商品を例にすると、以下の構造が適切です。

▼ 商品テーブル(現役)

商品コード
商品分類
商品名
単価

▼ 商品履歴テーブル(過去のバージョン)

商品ID
商品コード(FK)
適用開始日
商品分類
商品名
単価

データ構造が明確になり、以下が得られます。

  • 過去データをいつでも参照可能
  • 削除フラグ不要
  • 現役データと履歴データが混ざらない
  • 性能と整合性の両立

5.3 削除したデータを“残したいだけ” → アーカイブテーブルへ退避

「削除するがデータは保持したい」ケースでは、
アーカイブテーブルが有効です。


5.4 性能を気にする → DBMSに任せる

現代のDBMSは物理削除を前提とした設計になっています。
ユーザーが削除フラグで最適化する必要はありません。


6. “削除フラグは百害あって一利なし”を理解するための実例

● 削除フラグつき注文テーブルで発生したバグ

バグ内容 原因
商品を削除したら注文明細等が見えなくなった 商品マスタに削除フラグ条件を付けている
キャンセルした注文が集計に含まれた WHERE句で削除フラグ条件を書き忘れ
注文明細が復活しない 親テーブルだけ削除フラグを見ていた
注文の再登録ができない 削除フラグつきレコードがユニーク制約をブロック

削除フラグは“忘れられる可能性のあるロジック”です。
忘れられた瞬間、破綻します。


7. まとめ:削除フラグは最後の手段どころか“基本使わない”

削除フラグを標準にするのはアンチパターンです。

目的 正しい手段
復活したい ステータス管理
過去を参照したい 履歴テーブル
削除したくない アーカイブ
性能が心配 DBMSに任せる

8. 最後に

削除フラグは
「安易に設計を済ませようとした結果の妥協」
になりがちです。

本当に必要か?
代替手段はないか?
整合性は保てるか?
将来の運用は大丈夫か?
を考えて、問題がないと思われる場合のみ利用すべきです。

Discussion