« 拡大・縮小する前に美しいER図を | トップページ | 適用分野と実装手段の組み合わせ戦略 »

2016.03.31

「論理削除」ではなく「無効化」を

 以前の記事で、テーブルに「スタンプ属性」をいちいち載せるのは惰性的習慣ではないかと書いたが、似たものに「論理削除フラグ」がある。各テーブルにいちいちこれが置かれているDBを見ることがあるが、それもまた惰性の結末ではないか。スタンプ属性だろうが論理削除フラグだろうが、必要ならば置くべきだし、必要でないなら置くべきではない。ただし、多くのケースで必要なのは「論理削除」ではなく「無効化」であることは知っておいたほうがいい。

 削除のややこしさは、他テーブルとの関係にある。まず、通常の削除(物理削除)について見よう。テーブルAと参照関係にあるテーブルBがあるとする(図1)。ここで、テーブルA上のレコード①が、テーブルB上のレコード③と④によって結合(参照)されているとすれば、①を削除することは許されない。もし削除してしまえば、存在しないAレコードを結合するBレコードの存在を許してしまう。典型的な更新時異状である。

▼図1.通常の削除にともなう制約
20160325a

 いっぽう、テーブルAのレコード②は、テーブルBのどのレコードからも結合されていないので、削除は許可される。もちろんレコード①も未来永劫に削除できないわけではない。レコード③と④が削除されたなら、①を結合するレコードがなくなるので、そのときには削除できるようになる。

 このシンプルな枠組みで済むことは多いが、済まないこともある。たとえば、結合データが存在するゆえにあるレコードの削除が許されない状況でも、それを「無効化」したいケースがある。そして、無効化されたレコードを結合するデータについては、照会のみを許し、更新系操作(追加・変更・削除)を制限するのである。

▼図2.無効化にともなう制約
20160325b

 具体的には、図2のテーブルAのレコード①が無効化(有効フラグがoff)されている状況で、レコード③と④を照会することは許されるが、それらを変更することも削除することも許されない。また、①を結合する新たなレコードを追加することも許されない。つまりレコードを無効化するということは、「そのレコードを結合する関連データの存在は許すが、それらについていかなる異動も許さない」ことの宣言である。ただしこの宣言は簡単に取り消せる。③や④の更新操作が必要なことがわかったなら、①の有効フラグを一時的にonに戻したうえで必要な操作を行えばよい。

 なお、無効化されたレコードを結合するデータ(③④)について、新規追加や更新だけでなく削除操作まで禁止されることが奇妙に思われるかもしれない。上図の③や④を削除できないとすれば、無効レコードである①までが未来永劫に削除できないような気がする。

 その心配がいらない理由を説明しよう。まず、テーブルAが「得意先」だとして、テーブルBが「出荷」だとする。通常の業務システムであれば、出荷には受払実績や売上実績が関連しているはずだ。無効化されている得意先に関わる出荷データを削除(*1)した場合、「赤伝」に相当する受払実績や売上実績が追加される。無効な得意先向けに、たとえ赤伝であろうと受払実績や売上実績が追加されるのはおかしい。つまり、図2のように単純なモデルではわかりにくいが、一般的には無効化されたレコードを結合するデータについては、追加や変更だけでなく削除までも制約されなければいけないのである。

 そして、無効な得意先向けの出荷データ(③④)の削除が禁止されるとはいえ、それらがいつまでも存在するわけではない。遅かれ早かれ、パージ(切り離し)専用のバッチ処理によって削除される。このとき、バッチ処理のロジックによって広域のデータ整合性が担保されるため、「無効な得意先にもとづくものであるゆえに削除不可」の制約は適用されない。つまり、対話型処理とバッチ処理とで、適用される業務ルールは異なるということだ(本ブログでの記事「テーブル操作と業務ロジックの連動制御」参照)。データ処理の「スコープ」が異なるという意味では、あたりまえの話ではある。

 では、無効レコードに関連するデータがすべてパージされたとして、当の無効レコードはいつ削除されるのだろう。その時点では保守用フォーム上で削除することが許されるので、1件ずつ開いて処理してもよいのだが、専用のバッチ処理を用意して一括削除するほうがスマートだ。たいていのパージ処理は、出荷や受払実績といったトランザクションデータを一括削除するためのものだが、ときには「無効化されたマスター」を一括削除するために用意される。これを適宜に実行することで、無効レコードの無駄な増加も避けられる。また、有効期間を伴うテーブルであれば、そのバッチ処理の中で発効日や失効日の到来に合わせて有効フラグを自動更新したり、無効化された後で一定期間後に一括削除するといった応用も可能だ。

 以上が「無効化」のあらましであるが、このしくみを理解してしまえば、「論理削除」がなにやら中途半端なものに思えてくる。「論理削除フラグ」を置いて、レコードを削除する際に、物理的に削除する代わりに、そのフラグをonに更新するスタイルである。こうすると、そのレコードは物理的には存在するが、論理的には存在しないとみなされる。

▼図3.論理削除がもたらす「論理的な更新時異状」の例
20160325d

 論理削除とはいえ、データ整合性の管理上は物理削除と変わらない点に注意してほしい。レコードの論理削除が許可されるためには、図1と同様に、それを結合するデータが存在してはいけない。もし図3のように、③や④が存在している状態で①の論理削除(論理削除フラグをonに更新)を許してしまえば、「論理的な更新時異状」とでも言うべき奇妙な状況になる。

 ところがレコードが物理的には残っているという「安心感」のゆえか、そのあたりの制約設計が甘くなりがちで、図3のようなデータ状況がふつうに生じていたりする。そうでなくても、テーブルAを結合する処理の中でいちいち「論理削除フラグ=off」のWhere句を組み込むのが面倒だ。「無効化」において、更新系アプリだけに制約を組み込めば済むのとは対照的である。

 にもかかわらず論理削除が多用されるのはなぜか。理由を尋ねても、設計者本人もよくわかっていなかったりする。「ユーザが間違って削除した場合の保険」と説明されることもあるが、バックアップからレコード単位で復帰するための機構を用意しておけば(*2)、本番環境のアプリを論理削除のために複雑化せずに済む。もしかしたら本当にやりたいことは「無効化」なのに、論理削除フラグを組み込んで実現したつもりになっている、なんて実態もあるかもしれない。

 いずれにせよ本当に必要であれば、「論理削除フラグ」なり「有効フラグ」なりを置いて、必要な制約を盛り込めばよいだけの話ではある。「とりあえずの論理削除フラグ」や「そもそも削除なんて不要」といった思考停止に陥ることなく、個別に緻密に考えること。それがシステム設計という因果な仕事の基本だ。


*1.この場合、出荷データはむしろ「論理削除的」な扱いがされたほうがいい。具体的には、日付項目と連動した「指示書発行済」、「売上計上済」等と並ぶ「取消済」のステータスに更新したほうがいい。なぜなら、受払履歴や売上履歴の赤伝が参照するはずの元取引データが存在しないのでは困るからだ。いずれにせよ、無効レコードを結合するデータだからといって安易に削除できるようであってはいけない。
*2.そんな機構も要らないかもしれない。そもそも、緻密な削除制約が組み込まれているシステムでユーザが「間違って削除した」としても、それを結合する関連データが存在しないゆえにこそ削除が許可されたはずだ(つまり、間違って削除できるケースは案外少ない)。それならゆったり構えて、バックアップを眺めつつ、主キーを旧値のままとするなり新規値とするなりして、手作業で新規データとして登録し直せばいい。

|

« 拡大・縮小する前に美しいER図を | トップページ | 適用分野と実装手段の組み合わせ戦略 »

コメント

コメントを書く



(ウェブ上には掲載しません)




トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/87674/63420432

この記事へのトラックバック一覧です: 「論理削除」ではなく「無効化」を:

« 拡大・縮小する前に美しいER図を | トップページ | 適用分野と実装手段の組み合わせ戦略 »