LoginSignup
0
2

More than 5 years have passed since last update.

論理削除 is evil

Posted at

何が問題か

「削除」じゃないじゃん!!

おわり。

問題を生んだ思考。

  • 「削除」じゃないものを「削除」と開き直る態度。
    • あるいは言語感覚の欠落。
  • 自己矛盾に気づかないレベルの思考停止。
  • もとに戻したいときに復活できるから。という諦めの悪さ。
    • そもそも「もとに戻したい」可能性があるなら削除させてはいけない。

「削除」を騙ることの弊害と代償

詳しくは、こちらを参照。
https://www.slideshare.net/t_wada/ronsakucasual
https://www.slideshare.net/SoudaiSone/postgre-sql-54919575

「削除」したはずデータが残っているという現実との矛盾の辻褄合わせが連鎖的に膨れ上がって、制御不可能になるということです。

論理削除 is evil というか、 absolutely evil。

どうしても論理削除をしたいという、諦めの悪い人のために

ゴミ箱テーブルを用意して、delete トリガで自動バックアップをとりましょう。

これ自体は別に目新しいアイデアではありませんが、

  • テーブルごとに同じカラムを持つゴミ箱テーブルや、トリガを用意する必要がある。
  • 元テーブルのカラムが増減するたびに、ゴミ箱テーブルや、トリガもメンテナンスする必要がある。

という問題があります。

ですが、PostgreSQL (9.3以降)であれば各レコードをjson化する row_to_json() 関数でどんなカラム構造のデータでも1つのjsonbデータに変換することができるので、カラム構造の違うテーブルのレコードを同一のテーブルに収めることが可能になります。

定義方法

create table dustbin (
    table_name text not null,
    id integer not null,
    data jsonb not null default '{}',
    deleted_at timestamp without time zone not null default current_timestamp,

    primary key (table_name, id)
)

create function throw_in_dustbin() returns trigger as $$
    begin
    insert into dustbin (table_name, id, data) values (tg_table_name, old.id, row_to_json(old));
    return old;
    end;
$$ language plpgsql

あとは、論理削除をしたいテーブル個別に以下のdelete トリガを設定するだけ。

create trigger delete_<table_name> before delete on <table_name>
  for each row execute procedure throw_in_dustbin()

この方法であれば、元テーブルのカラムが増減しても dustbin テーブルや throw_in_dustbin 関数の内容を変更する必要がありません。

え? データが jsonb だと検索が遅くて大変?
この dustbin テーブルを検索するのは1年に1回あるか無いのだから気にしない。(何度も検索するようであれば、delete操作が間違っている証拠。)

使用例

# -- 対象テーブル
# select * from addresses;
 id | name |           info
----+------+--------------------------
  1 | hoge | {"zip": "1234567"}
  2 | fuga | {"tel": "090-1234-5678"}

# -- トリガ追加
# create trigger delete_addresses before delete on addresses
  for each row execute procedure throw_in_dustbin()

# -- hogeを削除
# delete from addresses where name = 'hoge'

# -- address からはキレイに削除されている
# select * from addresses;
 id | name |           info
----+------+--------------------------
  2 | fuga | {"tel": "090-1234-5678"}

# -- 削除されたデータは dustbin に保存されている。
# select * from dustbin;
 table_name | id |                         data                          |         deleted_at
------------+----+-------------------------------------------------------+----------------------------
 addresses  |  1 | {"id": 1, "info": {"zip": "1234567"}, "name": "hoge"} | 2017-12-13 12:17:15.511607

注意事項

  • delete文が遅くなりますので、本当にバックアップが必要なテーブルのみに適用しましょう。
  • dustbin テーブルにはデータさ蓄積されていく一方です。
    • 大量・長期保存する必要があるならちゃんとパーティションを切りましょう。
    • これは読者への宿題とします。
0
2
1

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
0
2