Ognacの雑感

木漏れ日々

目次

Blog 利用状況

書庫

ギャラリ

Update か Delete/Insertか

もちろん、優劣や速度の面で議論するのは的外れです。
Case by Caseです。と結論つけると、何も主張していないことになりますね。
私の基準は、該当レコードがリーフ行(自分行を親とする子データ)である時は、Updateにします。
自分の行が親(鏡行)になっていて、明細が付く時は、Delete/Insertで作るようにしています。
理由:
   修正前と修正後で明細行の行数が増減するとき、 Delete/Insertでないと、処理が煩雑になる
例)
 修正前
           明細NO   品名     個数
    明細1:  01      aaa       10
    明細2:  02      bbb       20
    明細3:  03      ccc       30

この伝票で、 明細2が抹消になり,  明細3の個数が 22 に変わり, 1行追加(  ddd 40個) があった。
              明細1を最後に持って行きたい。
業務仕様で、明細NOは1基準の通番にしないといけない。
   (このシーンは誇張ではなく、現場では起こりうる要求だったりします。)
この場合、明細2を消して 、明細3を変更して、明細4を追加し、明細NOを振り直す。 よりも、一旦、明細を削除して、新規Insertした方がシンプルになります。

 修正後
           明細NO   品名     個数
    明細3:  01      ccc       22
    明細4:  02      ddd       40
    明細1:  03      aaa       10

業務面から見ると、どちらでも結果は同じなので、安定して作れるほうが良いと思います。
連番の付け方が問題になることがあります。
  SQL ServerのIdentity項目やOracleのシーケンスは便利なんですが、トランザクションとの相性が悪く、ロールバックが起こると欠番が生じます。
いずれにしても、表示連番は、自力で管理する必要があります。
昔は、Delete/Insertで生じるData領域の抜け穴の処理問題がおおきく、「駄目だ/嫌だ」と主張する人もいましたね。
この主張は、当時から納得できませんでした。トリガーをみれば、
 SQL Serverの更新トリガーでは変更前後は Delete/Insertに格納されていますし、 Oracleでも new/oldに格納されています。
 RDBの内部実装上は、delete/insert的な仕組みで処理されているのでしょうね。
となると、アプリでどちらが良いかの議論は陰が薄れると思うのです。(下段補足)

今日のように、大容量HDDの時代では、定期的に全体のコンプレスすることもあり、個別の穴を意識することも少なくなりました。
(*) 少し後ろめたさは感じるのは、Byte単位で短いプログラムを作ったり、データ無効化による領域穴が、リソース的に大きな問題になっていた時代のトラウマでしょうか。
    メモリアロケーションを自前でしてた当時、サイズとアロック順を意識しましたが、GCに依存する環境では、穴を意識することはないですね。
    それと同様に、RDBでも、穴を意識しない時代になったのでしょうか。

(*)上記の例は 比較するために想定した例です。
  実務で上記の更新操作は疑問です。伝票は、更新/削除するのは駄目で、赤黒処理するのが基本です。その面では、常に Insertになります。

更新でも削除でも、変更履歴を残すのは基本なので、変更履歴を追跡して、過去に戻って復元可能だからOKだとの意見もありますが、復元する仕組みは面倒そうですね。

「実装のしやすさで判断すれば良い」というのが、私の思いです。
鏡と明細の二次元ならこれで良いのですが、3次元以上の階層構造のテーブルになると、何が適切なのか判りません。
そのときは、Classのシリアライズ機能に適応して欲しいと願うのです。(Sql ServerのXML項目で可能か、試行してみて、挫折したのは内緒です。)
(PS). RDB内部で Delete/Insertしているという意味ではなく、 変更前後のデータほ保持しているのと、 rolleback/Comit用に多くの領域を確保しているので、行単位のdelete/Insert とupdateの領域効率を厳密に追求するのはどうなのなかぁ..という意味です。

投稿日時 : 2009年6月19日 0:22

Feedback

# re: Update か Delete/Insertか 2009/06/19 2:54 choir

Delete/Insertはあまり使わないなぁ。
怖いから。

何故その方が良いか自分/他人にきっちり説明できるとき以外はUpdate使ってます。


Delete/Insertでデータ飛ばされた経験が幾度もあるもので..._●■=
Insert用のアドホッククエリを生成してるメソッド。
それを使いまわすためにDelete発行してるケースに良く遭遇してます。

# re: Update か Delete/Insertか 2009/06/19 10:19 れい

> Case by Caseです。と結論つけると、何も主張していないことになりますね。

そうそう!
Case By Caseなのは当然で、そんな主張は意味ありません。
抜けがあったとしても、間違いであったとしても、「知りうる範囲では」とか、「この場合はコレ!」といった基準を言うべき。

> 更新でも削除でも、変更履歴を残すのは基本なので、変更履歴を追跡して、過去に戻って復元可能だからOKだとの意見もありますが、復元する仕組みは面倒そうですね。

この「変更履歴」もそうですが、RDBはデータの時間変化が扱いづらいと思っています。
時間変化まで考慮してDB設計をするとRDBだと煩雑すぎて、本質が見えなくなってしまいます。
そのあたり、だれか教えてくれないかなぁ:D

# re: Update か Delete/Insertか 2009/06/19 11:18 DELI

大規模なシステムでDeleteを認めていないやつがありました。
全てのレコードに有効かどうかのフラグを持っておき、Updateはそのフィールドの変更しかしない。
レコードの値を更新したい場合は、既存レコードを無効にした上で、常に新しい値のレコードをInsertする。
これで問題が起こったときも全ての変更履歴が残っているので、復元や原因調査などに使えるという寸法
です。
というのは建前で、プログラマのミスでdeleteしてしまったら色々と困るので、馬鹿よけの設計というのが
実際の理由。

ただ、テーブルサイズが馬鹿でかくなるので、バックアップを取った上で無効レコードの一括削除などを時々
行なう工夫が必要だったりしますが。

# re: Update か Delete/Insertか 2009/06/19 11:35 みきぬ

変化球。

・DB に PostgreSQL を選べば悩まなくてすむよ!

・最初に全レコード Insert しちゃえば、以後は Update だけですむよ!(明細の上限が決まっていて、かつそれほど多くないケースで有効)

# re: Update か Delete/Insertか 2009/06/19 12:12 trapemiya

SQL Server 2008のManagement Studioからあるテーブルに主キーを付けようとしたら、レコードが一度削除されるような操作はデフォルトで禁止されているとか表示されたことがありました。確か。うろ覚えですみません。
なので主キーを付ける時はこんなことやってるのかぁと思った記憶があります。あまり関係ない話題ですみません。

ところで確かにリーフならDelete/Insertでも良いと思いますが、たぶん私はDALにDataTableを持たせて、バインドはDTOとになるでしょうから、Updateが基本になると思うなぁ。履歴もDataRowのRowStateで抜けそうだし・・・。
でも、このパターンで実装したことがないのでもっと良く考えてみないとわかりません・・・


# re: Update か Delete/Insertか 2009/06/19 12:34 やじゅ

少ない明細ならいいけど、100明細あって1行だけ変更
した場合、Delete/Insertで、100行登録するよりは
DataTableのRowStateなど参照して、変更箇所のみと
した方がいい気がする。

# re: Update か Delete/Insertか 2009/06/19 19:44 Ognac

>怖いから。
怖いですか? テスト不良のまま消してしまう開発者対策との話もありますが、妥協的解決な気がします。

>時間変化まで考慮してDB設計をするとRDBだと煩雑すぎて、本質が見えなくなってしまいます。
一時期
   更新日時、テーブル名、項目名、変更前値、変更後値
 という項目を持つ、変更項目管理表を作ったことがありますが、保険的な面が強く、活用した形跡は少なかったですね。
 このテーブルから、過去の任意の時刻のテーブルを復元するのは結構手間でした。

>というのは建前で、プログラマのミスでdeleteしてしまったら色々と困るので、馬鹿よけの設計というのが
削除フラグの使い方が違うような気がしますね。該当キーの行の抹消に用いるのが良いのでしょうね。

>最初に全レコード Insert しちゃえば、以後は Update だけですむよ!(明細の上限が決まっていて、かつそれほど多くないケースで有効)
確かに一理ありますね。全パターンを事前登録して、有効無効識別するのも手ですね。

>主キーを付ける時はこんなことやってるのかぁと思った記憶があります。
そんな所からも、私は永久連番サロゲート派です。

Adapter.Updateを使ってないので、なんとも評価できませんが、自動化するなら、Updateになるように思います。
Updateが基本になると思うなぁ。履歴もDataRowのRowStateで抜けそうだし・・・。

>100行登録するよりは DataTableのRowStateなど参照して、変更箇所のみと
境界線の設定は、難しいです。境界線を引いて、使い分けする手間と、どちらかに一本化したときの冗長度とのトレードオフなんでしょが、私は一本化派です。

# re: Update か Delete/Insertか 2009/06/19 23:13 Pasie.

 出遅れた…
 話題に取り上げていただきありがとうございます。m_o_m

 とりあえず、OracleのMergeは、例えば表Aに表Bをまるごと突っ込む場合に、表A側にレコードかあったりなかったりする場合に困るので、insert句とupdate句を書きましょう、という話だろうから、今回の様な1レコードに対する処理と言うところでは、どうしても冗長な記載に見えてしまうってことで、まずパスで。-_-;
 常にdelete/insertってのは、sqliteだと、deleteは行削除フラグが立つだけなので、delete/insertを繰り返すとDBファイルが限りなく肥大化します。ときにはvacuum、というのをプログラムでするのも良いですが、本当に?って感じもするので、基本updateにしたいところ。PostgreSQLはどうなんでしょうね?
 本命のoracleとかsqlserverとかだと、そういうことはないのだろうけど、しかし気持ち悪さが多少ある(私は-_-;)わけなので、できればupdateを基本に…といいたいところですが、レコードがあるかどうかを判断してinsertかupdateを選択するというのはそれはそれで嫌な感じがするわけです。そうするとどうしたってdelete/insertかなあとか。そして結局ストアドに逃げるわけですが…
 てことで、現時点ではdelete/insert支持なのかな。しかし、レコードの全フィールド総リプレースまたは追加って普通にある操作だと思うんですが、なんでSQLはそれに対応していないんですかね。そのあたりで理論的ななにかを知っている人いませんか?

# re: Update か Delete/Insertか 2009/06/20 0:40 Ognac

>そのあたりで理論的ななにかを知っている人いませんか?
update文は単に上書きだけなんでしょうか。記事にも書いたのですか、トリガーで新旧行を保持して、且つロールバック領域も取られるので、......
各RDBによって、解消の仕組みが異なるのでチューニング技術が、要求されるのでしょうね。
チューニング技術もバージョンによって差違があるみたいで、陳腐化してしまう..と愚痴られたことがあります。
 Oracleの REDOの仕組みがイマイチ理解できていない私がいます。

# re: Update か Delete/Insertか 2009/06/20 1:08 Pasie.

 Oracle現在勉強中であります。よくわかりません。だれか教えてください(汗
 REDOってあれですよ。commitした時に書かれるのはバッファキャッシュであって物理ディスクじゃないから、commit直後に落ちるとデータが失われます。なのでcommit時にredoログに書き出して、復旧に備える、ということのようです。多分。自信ありません-_-;
 ぐぐったらsqlserverではトランザクションログファイル と呼ぶファイルがredoと同じ物のようです。(多分)

>各RDBによって…仕組みが異なるので…
 なかなかわかってもらえないんですよね。どれも一緒だろ攻撃をうけやすくて困っております。>_<;

# re: Update か Delete/Insertか 2009/06/20 1:22 Ognac

>commit直後に落ちるとデータが失われます。
このへんが、納得しにくい仕組みなんですよね。
DB2は違う仕組みだと聞いてますし。
実際に使いこなさないと見えない部分があるわけで、
教科書の知識がどこまで通用するかも不透明でブツブツ....

# re: Update か Delete/Insertか 2009/06/20 10:44 trapemiya

>commit直後に落ちるとデータが失われます。

え~、本当にそうなんですか? いや、私も詳しくないんで感覚的に言っているだけですが(^^;
SQL Serverの場合は、まずトランザクションログに書き込みます。それからデータベースに書き込みますので、データベースに書き込み中に電源が落ちても、電源投入後はトランザクションログからデータベースに正しく書き込まれます。
commit直後というのがトランザクションログがまだ全て書かれていない状態なら確かにデータベースには反映されませんが、commit直後というのがアプリケーションにcommit完了が返ってきた時点であれば、データベースからロストすることは無いでしょう。

# re: Update か Delete/Insertか 2009/06/20 23:16 Pasie.

>電源投入後はトランザクションログからデータベースに正しく書き込まれます。
 いやその通りですよ。SQLServerのトランザクションログがOracleのREDOログです。
 言いたいことは、commit時には必ずしもデータベースファイルにcommitしたデータが書かれているわけではなく、バッファキャッシュ(+Redo)にだけ存在していることがあるという話です。この状態の時に落ちると、バッファキャッシュのデータはメモリ内にある訳なので揮発してしまいます。するとRedoがなければデータを復元することができなくなります。だからRedoという仕組みがあるのでは?という意味で書きました。
 sqliteなんかは、commitが終了するとかならずデータベースファイルに反映されているので、redoなんかありません。(多分)
 高速性を保つ仕組みとしてバッファキャッシュがあり、その障害対策としてのRedoなんじゃないかなあ、というつもりで書きました。

# re: Update か Delete/Insertか 2009/06/21 0:12 Ognac

Commit直前まで、本体は更新前の状態を保持していて、Commit時にログから更新する。その面では良い仕組みなんでしょうが、この瞬間に電源が切れると、コメントの事態になりますね。
反対に、普通のロールバックは即時に終わるのか、言えば結構時間がかかる時があります。後始末なんですかね。
ログが本体に反映して、初めてCommit完了だと思いたい私は、この仕組みが、活字では理解できても、体が嫌がってイマイチ理解出来ないんですよね。

# re: Update か Delete/Insertか 2009/06/21 0:13 trapemiya

>いやその通りですよ。SQLServerのトランザクションログがOracleのREDOログです。

あ~、了解しました。私の勘違いだったようです。最近、お疲れ気味かも・・・。すみません。

# re: Update か Delete/Insertか 2009/06/21 13:04 みきぬ

> PostgreSQLはどうなんでしょうね?
自分が知らない間にいろいろと新しくなってました(。。;
http://ja.wikipedia.org/wiki/PostgreSQL#.E3.83.90.E3.82.AD.E3.83.A5.E3.83.BC.E3.83.A0

# re: Update か Delete/Insertか 2009/06/21 23:52 Pasie.

>ログが本体に反映して、初めてCommit完了
 アクセス速度のためには仕方ないってことでしょうね。
 ログはシーケンシャルに書けばよいですが、データベース本体はそういうわけではないですからね。
 そんなあなたにsqlite、なのかもしれませんが、しかしこっちはvacuumがあるからなあ…(汗

タイトル
名前
Url
コメント