sqlアンチパターン 幻の第26章「とりあえず削除フラグ」

45
和田 卓人 (@t_wada) Aug 31, 2015 @論理削除 Casual Talks SQLアンチパターン 幻の26章 「とりあえず削除フラグ」 #ronsakucasual

Upload: takuto-wada

Post on 07-Jan-2017

60.873 views

Category:

Technology


2 download

TRANSCRIPT

Page 1: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

和田 卓人 (@t_wada) Aug 31, 2015

@論理削除 Casual Talks

SQLアンチパターン 幻の26章 「とりあえず削除フラグ」

#ronsakucasual

Page 2: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

和田 卓人 id: t-wada @t_wada github: twada

Page 3: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

スタンド名は「ワイルド・サバンナ」

Page 4: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

おかげさまで高評価を頂いております

Page 5: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

第26章?

本書に入れてみたかった章の話をします

(『SQLアンチパターン』は25章まで)

Page 6: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

愚者は経験に学び、賢者は歴史に学ぶ。 ─オットー・フォン・ビスマルク

テーマについて

Page 7: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

諸君は自らの経験からいくらか学ぶことができるという、全く愚かな考えであろうが、 余はむしろ他人の失敗を学ぶことで、自分の失敗を回避することを好む。

─オットー・フォン・ビスマルク

Nur ein Idiot glaubt, aus den eigenen Erfahrungen zu lernen. Ich ziehe es vor, aus den Erfahrungen anderer zu lernen, um von vorneherein eigene Fehler zu vermeiden.

Page 8: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

アンチパターンとは 単なる べからず集 あるある集

では無い

Page 9: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

0. 名前 1. 目的 2. アンチパターン 3. アンチパターンの見つけ方 4. アンチパターンを用いても良い場合 5. 解決策

アンチパターンの構成

Page 10: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

アンチパターン名

とりあえず 削除フラグ

Page 11: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

0. 名前 1. 目的 2. アンチパターン 3. アンチパターンの見つけ方 4. アンチパターンを用いても良い場合 5. 解決策

アンチパターン: とりあえず削除フラグ

Page 12: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

目的: データを消さずに、無いことにしたい

エンドユーザから見るとデータが無いことにしたいけど、実際のデータは消したくない

「削除した」データを検索したい

データを消さずにログとして簡単に残したい

誤った操作をなかったことにしたい、すぐに元に戻したい

Page 13: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

0. 名前 1. 目的 2. アンチパターン 3. アンチパターンの見つけ方 4. アンチパターンを用いても良い場合 5. 解決策

アンチパターン: とりあえず削除フラグ

Page 14: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

CREATE TABLE Bugs ( id SERIAL PRIMARY KEY, bug_code VARCHAR(20) NOT NULL, date_reported DATE NOT NULL, summary VARCHAR(80), description VARCHAR(1000), is_deleted TINYINT(1) DEFAULT '0', ... );

1/true の場合削除されていると見なす

アンチパターン: 削除フラグの導入

Page 15: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

アンチパターンのメリット(?)

簡単に元に戻せる気がする なんとなく安心

UPDATE Bugs SET is_deleted = 0 WHERE bug_code = ‘hoge' AND is_deleted = 1

Page 16: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

アンチパターンとは何でしょうか。それは、問題の解決を意図しながらも、しばしば他の問題を生じさせてしまうような技法を指します。

─ Bill Karwin

よかれと思って裏目に出てしまうもの

Page 17: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

アンチパターンにより起こること

常に WHERE 句が必要 コードが削除フラグだらけ 認識の齟齬を生みやすい

SELECT bug_code, date_reported, summary FROM Bugs WHERE is_deleted = 0

Page 18: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

class Bug < ActiveRecord::Base default_scope ->{ where( is_deleted: false ) } …

アンチパターンにより起こること

よかれと思ってコードレベルでデフォルトを変えたらバグがゴロゴロ

Page 19: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

SELECT bug_code, date_reported, summary FROM Bugs WHERE is_deleted = 0 ORDER BY id DESC LIMIT 1

アンチパターンにより起こること

データ不整合と 場当たり的クエリの巣窟

Page 20: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

アンチパターンにより起こること

削除フラグの立ったデータがテーブルに隠れている

https://www.flickr.com/photos/usoceangov/8290528771

Page 21: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

あるエンティティ定義に、論理削除有無を設定する属性が定義されている時点で、開発者は『ああ、この表のデータって削除していいんだ』という暗黙の了解に思考を縛られる

─ @dekasasaki

泥箱的なメモ 論理削除が奪うもの http://dekasasaki.tumblr.com/post/69487259373/論理削除が奪うもの

アンチパターンにより起こること

Page 22: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

0. 名前 1. 目的 2. アンチパターン 3. アンチパターンの見つけ方 4. アンチパターンを用いても良い場合 5. 解決策

アンチパターン: とりあえず削除フラグ

Page 23: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

直面している問題の種類や、メンバー間の会話での何気ない言葉が、そこにアンチパターンがあるかもしれないことに気づくヒントになります。

─ Bill Karwin

Page 24: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

アンチパターンの見つけ方

Q: この is_deleted 列はどういう目的で必要なのですか? A: データ上は無い事にしたいけど、実際のデータは消したくないからです (http://qiita.com/Jxck_/items/156d0a231c6968f2a474 より)

Q: なぜこのテーブルにも削除フラグが付いているのですか? A: プロジェクトのルールで、全てのテーブルに削除フラグを定義することになっているんです

Q: この is_deleted2 というカラムは何者ですか? A: ああ、それは管理用フラグです。管理者が非公開設定にしたときに true になります。 is_deleted との組み合わせで表示を制御します

Page 25: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

0. 名前 1. 目的 2. アンチパターン 3. アンチパターンの見つけ方 4. アンチパターンを用いても良い場合 5. 解決策

アンチパターン: とりあえず削除フラグ

Page 26: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

アンチパターンを用いても良い場合

論理削除(UPDATE)は物理削除(DELETE)よりも大概の場合速い

物理削除(DELETE)と天秤に掛ける際に、高トラフィックのサイトで UPDATE ベースの解を採用することはある

……ただし、できればフラグ以外の実現方法で

Page 27: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

0. 名前 1. 目的 2. アンチパターン 3. アンチパターンの見つけ方 4. アンチパターンを用いても良い場合 5. 解決策

アンチパターン: とりあえず削除フラグ

Page 28: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

私の経験上は、ユーザーから「論理削除」という言葉を聞いたことがありません。 次のような要件は、聞いたことがあります

•社員が退職(転属)する •(売掛金の回収を諦めて)売上を打ち消す •「お知らせメッセージ」を公開日がくるまで非表示にする •既読メッセージを表示しない •保存期間が過ぎたアンケート結果をオペレーターが見れなくする

─ @ledsun

論理削除フラグという名の死亡フラグ - @ledsun blog http://ledsun.hatenablog.com/entry/2015/03/27/015203

解決策への糸口

Page 29: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

解決策1: (問題解決になっていないが)せめて削除日にしてみる

CREATE TABLE Bugs ( id SERIAL PRIMARY KEY, bug_code VARCHAR(20) NOT NULL, date_reported DATE NOT NULL, summary VARCHAR(80), description VARCHAR(1000), deleted_at DATETIME, ... );

Rails の論理削除プラグインの多くがこの設計 (機械的な WHERE 句は減らないが、プラグインに書かせる) しかしカラムに NULL が入るとインデックスを使えないデメリットがある

Page 30: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

解決策1: (問題解決になっていないが)せめて削除日にしてみる

CREATE TABLE Bugs ( id SERIAL PRIMARY KEY, bug_code VARCHAR(20) NOT NULL, date_reported DATE NOT NULL, summary VARCHAR(80), description VARCHAR(1000), deleted_at DATETIME, ... );

Rails の論理削除プラグインの多くがこの設計 (機械的な WHERE 句は減らないが、プラグインに書かせる) しかしカラムに NULL が入るとインデックスを使えないデメリットがある

問題解決に なっていない

Page 31: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

解決策1: もうちょっとマシに

CREATE TABLE Bugs ( id SERIAL PRIMARY KEY, bug_code VARCHAR(20) NOT NULL, date_reported DATE NOT NULL, summary VARCHAR(80), description VARCHAR(1000), closed_at DATETIME NOT NULL DEFAULT ‘9999-12-31 23:59:59’, ... );

ドメインの言葉(closed_at)を使いつつ、加えてカラムに NOT NULL 制約を付ける(未来日のマジックナンバー)

Page 32: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

解決策1: もうちょっとマシに

CREATE TABLE Bugs ( id SERIAL PRIMARY KEY, bug_code VARCHAR(20) NOT NULL, date_reported DATE NOT NULL, summary VARCHAR(80), description VARCHAR(1000), closed_at DATETIME NOT NULL DEFAULT ‘9999-12-31 23:59:59’, ... );

ドメインの言葉(closed_at)を使いつつ、加えてカラムに NOT NULL 制約を付ける(未来日のマジックナンバー)

まだ問題解決に なっていない

Page 33: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

“We won’t support soft-delete at all.

If you want to implement a soft-delete alike behaviour its probably a good idea to look into the State pattern instead.”

Doctrine 2 “Behaviours” in a Nutshell

http://www.doctrine-project.org/2010/02/17/doctrine2-behaviours-nutshell.html

解決策2: それはフラグではなく状態である

Page 34: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

CREATE TABLE Bugs ( id SERIAL PRIMARY KEY, bug_code VARCHAR(20) NOT NULL, date_reported DATE NOT NULL, summary VARCHAR(80), description VARCHAR(1000), status VARCHAR(20) NOT NULL DEFAULT ‘NEW', ... FOREIGN KEY (status) REFERENCES BugStatus(status) );

“IsDeletedフラグを使う代わりに、Dahan氏はデータの状態を表すフィールドを保持することを提案している。 例えば、有効、中止、キャンセル、廃止予定のような状態だ” http://www.infoq.com/jp/news/2009/09/Do-Not-Delete-

Data

Rails のプラグインでは AASM が便利

解決策2: それはフラグではなく状態である

Page 35: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

解決策3: 履歴テーブルに移す

CREATE TABLE BugHistories ( bug_id INTEGER PRIMARY KEY, bug_code VARCHAR(20) NOT NULL, date_reported DATE NOT NULL, summary VARCHAR(80), description VARCHAR(1000), archived_at DATETIME, ... );

二つのテーブルの間の整合性はトリガー等で保つ (詳しくは『理論から学ぶデータベース実践入門』を)

Page 36: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

解決策4: そもそも削除も更新もしない

Page 37: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

アプリケーションは現実を何かの業務等々の観点で抽象化したものであり、それが扱うデータは事実に忠実にモデル化されたのなら残り続けているはずなのです。現実から事実を消し去ることは不可能。

─ @dekasasaki

泥箱的なメモ 論理削除が奪うもの http://dekasasaki.tumblr.com/post/69487259373/論理削除が奪うもの

解決策4: そもそも削除も更新もしない

Page 38: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

T字形ER手法というのをベースにしたテーブル設計をしていて、そこでかなり鍛えられたわけですが、その時にはだいたいこのような原則を叩きこまれました。

•テーブルに状態を持たせない •究極には機械が認識するキーと、人間にとって意味のあるデータだけのエンティティだけですべての業務のデータを構成できる •日付を持つデータはイベント(これもひとつのエンティティ) •NULLのデータは絶対に持ってはならない •テーブルはでかく作るな、小さく作れ •テーブル同士の関連は直接持つな、関連を表すテーブルを作れ •1:1の関連になったとしても、イベントとそれに付随するデータは分離しろ •データが増える?金と物理で殴れ(ディスク増強しろ)

─ @mike_neck論理削除が云々について - mike-neckのブログ http://mike-neck.hatenadiary.com/entry/2015/03/24/231422

解決策4: そもそも削除も更新もしない

Page 39: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

解決策4: そもそも削除も更新もしない

https://twitter.com/takezoen/status/580147622427537408

Page 40: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

解決策4: そもそも削除も更新もしない

http://www.datomic.com/

Page 41: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

解決策5: オペミスを防ぐには

「誤った操作をなかったことにしたい、すぐに元に戻したい」の解が無い

これは難しい課題

• 間違えにくい UI を作ったり • 「確認画面」を用意したり

Page 42: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

解決策5: 遅延レプリケーションはどうか?

http://dev.mysql.com/doc/refman/5.6/ja/replication-delayed.html

Page 43: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

• 「とりあえず」が思考停止 • 全てのテーブルに削除フラグはおかしい

• 「削除」は設計不足を示す • お客様は本当に「削除」と言っているか?

• 「フラグ」以外もある • 状態遷移で考えるほうがマシ • 更新/削除をしない世界もある

• それでもよく考えた末の削除フラグなら OK.

まとめ: とりあえず削除フラグ

Page 44: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

まとめ: 二つの世界

Web システムにおける RDBMS はトランザクショナルなキャッシュとしての側面と永続的データストアとしての両面を持っている

企業システムにおける RDBMS は企業活動において発生した事実を余さず記録するトランザクショナルで永続的なデータストアとしての側面が強い

Page 45: SQLアンチパターン 幻の第26章「とりあえず削除フラグ」

ご清聴ありがとうございました