SQLアンチパターン読書会 :番外編「論理削除の四方山話」

論理削除

論理削除とはデータベースのカラムにdelete_flagを追加して、そのカラムが1のとき削除しているとみなすこと。DELETE文は使われず、一度作ったデータは永遠に残る。

目的:DELETE文を使わずに削除されているように扱えるようにする

プログラマの不安

何かあったときにデータさえ残っていれば復元できる。復元するためにはdelete_flagを0にすればいい。そう思い込んでしまうときがある
実際には関連を考えつつ戻す必要がある。テーブルの1カラムのデータを戻すにしても実際には複数テーブルを更新する必要がある。
性能上の問題も発生する。更新のたびにデータが増える。削除すれば十分に発揮される性能でも十分に発揮されない可能性がある。
参照制約にも問題が発生する。データが複数存在するの一意に決まらない。キーレスエントリーになり開発するのも困難になる。


削除したカラムに何かを行いたい

エンドユーザーが通常使う範囲で削除したカラムに対して何かの操作をしたい場合がある。例えば、検索したり、復元したりということだ。これらは当然物理削除した場合できなので、delete_flagを使いたくなる。
要件としてそれがあるのなら論理削除ではなく、「顧客の言う削除」という状態である。これはDELETE文の意味でもないし、もちろんdelete_flagのことではない。


日次パッチで集計する

1日に一度集計のためにデータを残しておいて、集計が終わり次第削除する。一時的に物理削除せずに置いておく必要がある。
それが要件ならそれ用のテーブルを用意すべきことである。

アンチパターンを使っていい場合

  • ないという結論に至った
  • delete_flagが使いたくなるような場合は大抵要件定義不足である。
  • 論理削除しても復元は現実的ではない。戻せることが要件にあるのならそれを考慮した設計にする

解決法

物理削除するべきところはする

不安があるならバックアップを取ればいいじゃない。消して問題があるのならバグか要件が足りていない。

状態として捉える

顧客が本当に欲しいものは「ゴミ箱」だったんだ。
PCだと削除しても実際には削除されず一旦ゴミ箱に入る。ユーザーはゴミ箱の中身に対していくらでも操作ができる。Redmineのチケットの用に複数の状態を持っている場合もある。
物理設計上仕方なくdelete_flagを入れるわけではなく、要件として論理設計に昇格させる。そしてちゃんとした名前を持ったカラムによって存在させる。


履歴管理する

前に戻す必要があり、過去のデータにも常にアクセスする必要がある場合、履歴管理を行う。一度作った列は管理用のカラム以外を更新せず、変更するたびに新しい列を追加する。


削除されたものを別のテーブルに移す

集計のためや性能上の理由なら別のテーブルに移す。削除は関連ごと削除する必要があるので、元のDBと同じ程度のものが必要になる。

まとめ

  • 論理削除は使うべきではない
  • 使いたくなるような時は要件が不十分
  • 戻せる実装はいくつかある

余談

論理削除の大抵のライブラリ実装は日付を入れるそうです。