15分調べてもわからないことは、質問しよう!

ただいまの
回答率

92.81%

受付中 DBのCRUDでUとDは必要ないという話を聞きましたが具体的にどう実装するの?

  • SQL

    788questions

    SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

2016/09/03 12:19 投稿

2016/09/03 16:51 編集

hojo score 74

  • 3

    回答

  • 3

    評価

  • 845

    view

"CRUD is Dead"なんて言われてる方もいらっしゃるようでCRUDのUとDを禁止する流れが存在するようです。

以下のようなサイトでも言われているように、CRUDのUとDを利用せずデータをログのように扱う方法は関連するデータ(特に履歴)が失われないために好感が持てました。

http://tanakakoichi9230.hatenablog.com/entry/6715376804
http://qiita.com/Jxck_/items/156d0a231c6968f2a474
http://mike-neck.hatenadiary.com/entry/2015/03/24/231422

現在MySQLを利用してウェブサービスを作成しようと考えているのですが、実際にUとDを利用しないDB設計をするにはどうすれば良いのかというところでつまづいてしまいました。

自分で考えたものは5つあります。


createdカラムを挿入し、通常Updateしていた部分をInsertで代用する。
この際、更新されないデータも全て最新のデータを複製する。
データ取得時には最新のデータを参照する。


createdカラムを挿入し、通常Updateしていた部分をInsertで代用する。
この際、更新されないデータにはNULLを指定する。
データ取得時にはカラム毎のnullではない最新データを参照する。


createdカラムを挿入し、通常Updateしていた部分をInsertで代用する。
この際、更新されないデータにはNULLを指定する。
また、最新データ参照用テーブルを作成しトリガーを利用してInsertがあった場合に
自動的に最新データ参照用テーブルの該当するIDのデータをUpdateする。
もちろん、データ取得時には最新データ参照用テーブルを利用する。


アクションログ用テーブル(action_id, target_db, target_column, value, created)を作成し全てのデータ操作をログとして残し、ログデータが追加された場合にトリガーを利用して最新データ参照用テーブルをupdateする。(valueの型をどうするべきかは不明。おそらくTEXT型)


アクションログはRDBではなくログデータとしてディスクに書き込み、RDBは通常通りCRUDする。また、アクションログを書き込むプログラムをモジュール化し、dataModule.set(id, name, value);などの関数を利用してデータを書き込む際には自動的にログファイルとRDBの両方が書き換えられるようにする。(そのモジュールにログファイルからDBを作成できる機能などあると良いかも)

①は無駄に容量を食うのであまり良くないと思っています。②に関しては「NULLは使うな」と良く言われてるのでそれがひっかかります。あとデータを取得する際に複雑なクエリを発行することになりそうです。③はNULL利用が不安なことは変わりありませんが、データ書き込みと取得で異なるテーブルを使うので複雑クエリ問題は解消しそうです。④この方法が良いんじゃないかと個人的に思っていたのですが、value型をどうするかという問題が残っています。またtext型を利用した場合、大量のログがtext型でinsertされることを考えると少し不安になります(全く問題ないのかもしれませんが)⑤については最終的にこれが一番良いのではないか?と思って現在検討している方法になります。しかしこれだと今までずっと"CRUD is Dead"の考え方できたのに、結局CRUDでDB操作しちゃってますね(汗。しかしログファイルを取ることで、何かあった時にはログファイルからDBを復元したり、調査が必要になった時にログファイルを確認したりできるのでCRUDのUとDは使ってますけどUとDを使わなかった時のメリットは得ることができているといった感じでしょうか。

このように幾つかの方法を考えたのですが、そもそも一般的なベストプラクティスがあるのではないか?と思いましたので是非そういった参考にできるサイトや、俺だったらこうするという意見がございましたら教えていただきたいと思っています。

よろしくお願いいたします。

情報の追加・修正の依頼をする(3)

2016/09/03 12:38

MySQLに限ったトピックではないので「SQL」タグがよろしいかと思います。 私も数年来このテーマを考えていますがまだうまい実装にたどり着けていないのでいい回答が付くのを期待しています。

2016/09/03 12:53

興味深いテーマですね。履歴管理での一番の王道パターンは「履歴管理したいテーブルに対応した履歴テーブルを作成する(品目マスタと品目変更履歴テーブルみたいな感じで)」かなと思われますが、 これより優れた解があるのかは気になるところです。 後、原則削除を行わない場合はデータとしては増え続けて要領圧迫されるので、 定期的なデータ退避処理は必要不可欠となりますね。

2016/09/03 15:55

NULLに更新した場合と更新しなかった場合の判別をどうつけるか。

表示エリアを広げる

閉じる

気になる質問をクリップする

クリップした質問に回答があった場合に通知・メールを受け取ることができます。

クリップした質問はマイページの「クリップ」タブからいつでも見ることができます。

良い質問の評価を上げる

以下のような質問は評価を上げましょう。

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

質問の評価を上げたことを取り消します

3

この機能は開放されていません

評価を下げる条件を満たしてません

評価を下げる理由を選択してください

上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

質問の評価を下げる機能の利用条件

この機能を利用するためには、以下の事項を行う必要があります。

質問の評価を下げたことを取り消します

回答(全3件)

回答の評価を上げる

以下のような回答は評価を上げましょう。

  • 正しい回答
  • わかりやすい回答
  • ためになる回答

評価が高い回答ほどページの上位に表示されます。

4

この機能は開放されていません

評価を下げる条件を満たしてません

CRUD is Dead がよくわかってないのですが、なんとなく論点が違うような気がして書き込んでみます。
CRUD is Dead ってつまり、「外部キーを結合してデータを取得した時に、最新のスナップショットで見える必要は無いんじゃね?」って話かなと思いました。

少し例を考えて見ます。

「社員レコードには所属組織という属性があり、これは組織レコードへの外部キーです」ってなったときに SELECT JOIN すれば社員の所属組織名や組織のボスの社員IDが取れるというデータベースを作るとします。
ここで、とある組織の組織名を変更するケースを考えます。

  • 従来のデータベース設計では、組織レコードの組織名フィールドの値を変更するだけですみます。そして、その後は社員のIDからその所属組織を取ると、最新の値が見えるわけです。
  • 更新削除なし設計では、操作として新しい組織名を持つ組織レコードを作成します。そして所属している社員についても新しいレコードを作成し、その所属組織フィールドには新しい組織レコードのIDを格納します。

ここまでですと、後者の方は処理量が増えているだけで何のためにそんなことをしてるのかわかりませんが、社員マスタを参照するアプリのことを考えると、そうでもなくなります。
たとえば、出張命令ワークフローのアプリの出張命令書レコードに申請者IDや承認者IDのフィールドが社員レコードへの外部キーであった場合、 select join して、その人たちの所属組織名をひっぱると最新の組織名が見えて良いのでしょうか?出張命令書のような文書であれば、その命令書がワークフローを流れた時点の組織名がとれがほうが良いでしょう。したがって、従来型の設計の場合は、出張命令書レコードに組織名フィールドを持ち、命令書が発行された時点の組織名をコピーしておくことになるでしょう。しかし、更新削除なし設計であれば、このフィールドも処理も不要なわけです。これが更新削除なし設計のメリットではないでしょうか。

こんどはデメリットになるケースを考えてみましょう。利用者毎に自分と親しい社員だけを登録するアドレス帳のアプリを考えてみましょう。アドレス帳レコードは社員レコードへの外部キーだけを持てばよいでしょう。従来型のデータベース設計であれば、SELECT JOIN で常に最新のデータが取れるので問題ありません。しかし、更新削除なし設計の場合は、社員マスタが更新される毎にアドレス帳レコードを更新する必要があります。アドレス帳のアプリは、常に社員マスタの更新通知を受け取って必要に応じてアドレス帳を保守する必要があるわけです。

更新削除なし設計だと、あきらかにシステム間の結合度が上がって不利なようにみえますが、いや、そんなアプリのほうが少ないだろ!っていうのが CRUD is Dead ってことかなって思いました

技術的解説
ここからは、質問いただいたのでしかたなく(ちょっと私のビジネスのコアコンピタンスに触れそう・・・)
まず、技術的にはレコードのIDを生成する方法について考える必要があります。外部キーに使うIDなので当然一意のものを生成する必要がありますが社員番号など業務上のIDとは別に生成する必要があります。このIDの生成について2案あります。

  • UUID(RFC4122)を使う→分散環境でも確実に一意のIDを生成することができ、ロックマネージャなどにお伺いを建てなくても生成できるメリットが有ります
  • 更新毎に文脈通番=CSN(Context Sequencial Number) を振り出すこととし、すべての更新処理においては、まず、通番を取得し、この通番に1回の更新内の連番を追加した番号を使います→CSNマネージャンという番号を振り出すサービスが必要であるが、更新の前後関係を CSN の大小で比較できるメリットが得られる

そして、前述したように、データベースをどう実装するか以前に、システム間連携の API/SPI の仕様の問題となります。社員マスタシステムに関しては、従来であれば、社員マスタシステムに対する問い合わせ処理をAPIで実装するだけでよかったのですが、更新削除なし設計では、更新通知を受け取る SPI (Service Proveider Interface)を実装する必要があります。(すいません、詳細省略)

つまり、更新削除なし設計については、データベースのスキーマ設計以前にシステム間連携の API/SPI の仕様の問題であると思います(たとえ、それが CSV+FTP の実装であっても)。実際に更新削除をしないデータベースをMySQL などの RDBMS 上に実装しようとすると、それなりに性能問題に対する技工が必要です。それこそ、みなさんの出番です(^_^)

2016/09/03 17:28 投稿

2016/09/03 22:30 編集

コメント(2)

2016/09/03 19:23

貴重な意見ありがとうございます。

なるほど、ご指摘通りデータをログ(履歴的)として扱うという観点からCRUD is Deadについて見ることしかできていなかったように思います。

おっしゃっていただいている内容をまとめると「システムが過去のデータを参照しなければならないことがあるため書き換えたり削除したりする必要ないよね」っていうことですよね?

出張命令の場合、組織名が最新のものに変わってしまった時には「あの組織、名前変わったみたいだね」で済む話かもしれませんが、これが商品だったりした場合には、購入履歴を参照した時に購入時より値段が安くなってて「もっと高値で買ったはずなのに履歴見たら安く(今の値段)なってる」なんてことが起こってしまうかもしれません。これは絶対に起こしてはならない致命的欠陥だと思います。

特に私の質問文章の④や⑤なんかは、マスタデータをログ(またはログ専用テーブル)に置き換えるという話になっていたため、余計に論点がずれていると感じたのではないでしょうか。特に⑤に関してはもはやDBから参照不能になっていますから、今回の要件からしたら論外ですね。指摘を受け、⑤は無いかな、と思い始めています。

そうなると、①②③または④の構成の中でどれがいいのか?という話になってきますが、お話の中で出てきた「新しい組織名を持つ組織レコードを作成」また「社員についても新しいレコードを作成し、その所属組織フィールドには新しい組織レコードのIDを格納」についてはどのようにレコードを追加するのがより良いのでしょうか。また、どのようにデータを取得すればいいのでしょう?ここが今回の質問の根幹だと思っています。

2016/09/04 08:58

hojoさん、質問いただいて追記してみましたが、ほとんど回答になってませんね。すみません。ただ、社員マスタシステムを更新削除なし設計にすることと、それをデータベース上にどう格納するかという話は別のレベルの話だと思います。
直感的には、①+最新スナップショットかなと思います。

回答の評価を上げる

以下のような回答は評価を上げましょう。

  • 正しい回答
  • わかりやすい回答
  • ためになる回答

評価が高い回答ほどページの上位に表示されます。

1

この機能は開放されていません

評価を下げる条件を満たしてません

ざっくり考えました。

  • modifiedは必要無し
  • 変動しないデータは絶対必要
  • updateが禁止なのでupdateが無くても困らない運用にする
  • 最新の私(データ)が最強

結果下記に落ち着きました。
ソシャゲの世界ではDBロックを避ける為にあえてこのようにしていると聞きます。

死ぬほどテーブルが増えそうですが、整合性は取れてそうですね。
Railsのようなフレームワークは基本的にID管理なのでそれに従っていますが、
idを消してuser_id, createdのユニークな複合インデックスで対応出来るかと思います。

SQL文は…MySQLだと明らか死ねる未来しかみえませんね。
Viewでも作るしかないですか?
案の一つとしてご笑納ください

 users テーブル
  • id (AI)
  • created
 user_names テーブル
  • id (無くても可)
  • user_id
  • name
  • created
 user_mails テーブル
  • id (無くても可)
  • user_id
  • mail
  • created

2016/09/03 12:56 投稿

コメント(1)

2016/09/03 16:39 編集

なるほど。

1データ1テーブルという設計で全てのデータにcreatedが紐づけられるという設計ですね?

もちろん、1ユーザの1データを取得したい場合はそのまま最新の私(データ)を取得すればいいと思うのですが、複数のデータを一括で取得したい場合は、自分の直感を信じて最新データをJOINすれば良いということになるのでしょうか。

大抵の場合複数のデータを取得することが多いと思うので、この設計の場合SELECT文のクエリが全体的に複雑になる可能性が油断できないこの事情ということになりそうですね。

場合によっては、複雑なSQLが大量に量産され、罠ワナして燃えちゃうかもしれないので究極の選択は永遠のトラウマになる可能性もある...ということですね。

意見してくれてアリガト(Thank you!)

回答の評価を上げる

以下のような回答は評価を上げましょう。

  • 正しい回答
  • わかりやすい回答
  • ためになる回答

評価が高い回答ほどページの上位に表示されます。

1

この機能は開放されていません

評価を下げる条件を満たしてません

"CRUD is Dead"なんて表現があるんですね。初耳です。

ちょっと見てみましたが、"CRUD is Dead"というのは、
「DBは履歴型テーブルだけでシステムを作ろう」
「複雑な更新・削除・運用等がある更新型テーブルよりも、
履歴型テーブルONLYで設計した方が設計・管理・運用が簡単!」
と言うところでしょうか。

DBのCRUDでUとDは必要ないという話を聞きましたが具体的にどう実装するの?

⇒必要ないなんてことはないと思いますが、
仮にNGとした場合、全て履歴型テーブルとして取り扱うため、
最新情報の取得はSQL文を全てのテーブルに対して工夫して取得する形になります。

次に、マスターテーブルですら、履歴型テーブルにするという事なので、
マスターテーブルとのJOINも難しくなります。
マスター履歴テーブルとのJOINはせず、
JOIN元のテーブルにそのままの値で含める形の方が良い。

最新情報の取得方法は色々ありますが、UとDなしだと、
主キー(AUTO_INCREMENT)やUNIQUE KEYによる最新版取得が良いでしょうね。
最新日時でも大丈夫ですが、性能が出ない場合が多いです。

⇒"CRUD is Dead"がWebシステムで使えるジャンルとしては、
静的ページに近いもの(1テーブル1レコードの情報だけで1画面の情報を全て表示する)が多いシステム、
リスト画面がほとんど存在しないシステム、
削除処理がほとんど存在しないシステム、
大容量データを取り扱わないシステムあたりでしょうか。
(例:Web契約システムあたりは使えそう)

①~⑤

⇒①~⑤の中で、かつ、"CRUD is Dead"にこだわるなら①でしょうね。
②はSQLが複雑になる上に、性能が①より出ません。
③④⑤はUを使っているので、"CRUD is Dead"ではない(?)

⇒①②はUに対応しておりますが、
Dには対応していないように見えます。
そのため、DELETE_FLGのようなものも必要でしょう。
(例:マスター履歴テーブルにおいて、今後表示させたくない項目が存在する場合等)

PS:DROP TABLEならアリと言う事なら、運用によっては、
マスターテーブル等の更新型テーブルをバッチ処理等にて、
DROP CREATE INSERTで再作成する方法もあるかもしれません。

PS2:実装方法や使える例を記載しましたが、
"CRUD is Dead"なんて言葉に惑わされないのが良いと思います。
ちゃんと設計・管理・運用できれば、更新型テーブルを用いた方が性能が出る事の方が多い気がします。

2016/09/03 18:53 投稿

2016/09/03 20:28 編集

15分調べてもわからないことは、teratailで質問しよう!

ただいまの回答率

92.81%

関連した質問

  • 解決済

    Rails ユーザの機能制限

    ユーザの機能制限を実装したいです。設計方法についてご教授願います。 1.各コントローラーのフラグによって機能制限する 2.機能制限チェックを司るコントローラーを作り、ユーザーが

  • 解決済

    入力画面で都道府県に連動した区市町村の入力をしたいのですが、いいライブラリありませんか?

    入力画面を作成しているのですが、都道府県に連動した区市町村の入力をしたいです。都道府県を選択した後に、選択した都道府県の区市町村を表示してselectで選択をする形を考えています。

  • 受付中

    VBA

    お尋ねします。 ACCESSで商品テーブルのフィールドで IIf([テーブル1]![フィールド1]=[テーブル2]![フィールド1],True,False) IIf([テーブ

  • 解決済

    Pocket(getpocket.com)→はてぶ 連携方法

    「あとで読む」に Pocket というサービスを便利に使っています。PCとAndroidから閲覧しています。 消費したコンテンツを捨てたくないなーという時に、はてぶにストック

同じタグがついた質問を見る

  • SQL

    788questions

    SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

閲覧数の多いSQLの質問

関連した質問

メールアドレスで登録

利用規約、及び 個人情報の取り扱いに関する要項をご確認のうえ同意いただける場合は「同意して登録」ボタンをクリックしてください。

SNSアカウントで登録

  • Facebook
  • Twitter
  • Google
  • Github
  • Hatena

思考するエンジニアのためのQ&Aサイト「teratail」について詳しく知る