2012-04-11 15 views
2

私は人々が記事を作成/編集できる基本的なCRUDウェブアプリを持っています。私は今、すべての編集の改訂履歴を保持する機能を追加したいと考えています。私は改訂履歴のサポートを追加するために私の現在のスキーマを変更するための2つのオプションを検討しているRDBMSにリビジョン履歴を格納するための正規化または非正規化?

Article(id, title, content, author_id, category_id, format) 

:現在、私はこのようになります記事のテーブルを持っています。基本的なアイデアは、ある記事のすべての単一編集が、リビジョンテーブルのレコードとして保存されることです。したがって、記事とリビジョンは一対一の関係です。

第1オプション(正規化): アーティクルメタデータの1つのテーブル、リビジョンの1つのテーブル。重複データは保存されません。

Article(id, title, category_id) 
Revision(id, content, author_id, format) 

第二オプション(デ正規化):オプション1のようないくつかの重複した列を持つ 2つのテーブル。

Article(id, title, content, author_id, category_id, format) 
Revision(id, article_id, content, author_id, format) 

2番目のオプションを使用すると、コーディングが非常に簡単になります(複雑さが少なく、コード行も少なくなります)。私はそれが "学問的"ではなく、 "純粋な"ものではないことを知っていますが、私の個人的な気持ちは、余分な結合をしなければならないということです。また、多くのジョインを実行する必要がないため、パフォーマンスが向上するはずです。

これは、このタスクを実行する健全な方法ですか?私が見落としている可能性のある予期しないまたは長期の結果

+0

JNKが正しいです(ただし、SQLは結合用に最適化されていませんが、RDBMSは詳細です)。請求書発行アプリケーションでも同様の問題がありますが、「履歴」テーブルには、いくつかの追加フィールド(履歴PK、タイムスタンプなど)を含む請求書テーブルの正確なコピーがあります。 'INSERT INTO INISTORY SELECT NULL、NOW()、...、i。*インボイスからのメッセージ 'へ簡単に – Konerak

答えて

5

パフォーマンスの引数はナンセンスです - あなたはJOIN秒以下ですが、RDBMSはJOIN秒に最適化されています。

ロットより多くのデータをサーバから引き出して、必要以上に最適化することはできません。

整合性の可能性もあります。異なるテーブル内の同じアイテムのデータを複製すると、不一致が発生する可能性があります。リビジョンレコードと記事レコードの値がformatまたはauthorの場合はどうなりますか?どちらが正しいかはどのように分かりますか? Articlescontentがいずれのリビジョンとも一致しない場合はどうなりますか?

本当にこれを正規化する必要があります。 CurrentRevisionフィールドをArticlesテーブルに追加して現在のバージョンにリンクし、RevisionsテーブルにArticleIDという2つのフィールドをリンクする必要があります。

+0

これを捨ててくれてありがとう。一貫性を維持するためのコードがより多くの作業を終了するようになったことに気付きました。 – trinth

+0

CurrentRevisionフィールドは本当に必要でしょうか? 1.記事を作成します。 2.ステップ(1)の記事を参照してリビジョンを作成します。 3.ステップ(2)のリビジョンでarticle.current_revisionを更新します。 – trinth

+1

@trinthなぜなら、それらはすべて別々の呼び出しである必要はありません。アーティクルと参照を挿入する呼び出しを1回行うことができます。コード内のID値を正しく処理するだけで済みます。 – JNK

7

データを気にする人は、「非正規化」のケースでコードが少なくなることはありません。Revisionの最新の行が常にArticleのコピーと一致するように強制する必要があります。これは実際には並行環境では些細なものではありません。ロックを非常に注意深く行う必要があります。

(あなたが同じコピーが含まれていないRevisionArticleを選択した場合、これはさらに悪くなる - !あなたがRevision主キーを施行するためDBMSに依存することはできません)で

例えば、Oracleマテリアライズド・ビューは、実際のデータ・モデルを非正規化する必要なく、データを事前にジョインすることができます。

このようなDBMSがない場合でも、を測定した後でのみ非正規化を検討してください。は現実的なデータ量でパフォーマンスを測定します。はい、ジョインズは高価かもしれませんが、あなたの特定の状況では高価ですか?測定値のみが伝えることができます。


ところで、このような特定の関係/自然キーを使用することを検討してください:

enter image description here

あなたが与えられた記事の下に修正を加えるようrevision_noが単調に増大します。

Revision PKの下のBツリー構造は、特定の記事の最新の(または任意の)リビジョンを見つけることを非常に効率的にします。あなたの質問に表示されていない代替キーがない場合はclusterRevisionと(Oracleの下で)クラスタ化インデックスのリーディングエッジを圧縮することもできるので、繰り返しのスペースオーバーヘッドはarticle_idになります。

+0

私はあなたのコメントからたくさんのことを学びました。私は標準化されたオプションを使用していきます。彼の提案は私が使い終わったものなので、私は "解決策"として他の答えを選んでいます。 – trinth

+1

@trinth 'Article.CurrentRevision'には注意してください。おそらく、「改訂版」はすでにいくつかの分野で発注されており、最後の改訂版はその命令から自然に推論することができます。したがって、 'CurrentRevision'は新しい情報をシステムに導入しません。既存のものを複製するだけです。**冗長**であり、冗長性は変更の異常につながります。その存在からパフォーマンスの利点を得ることさえできません(Bツリーでは、MAXを検索することは具体的な価値を探すことと同じくらい速い)。その存在は、「最後」と「現在の」リビジョンが異なることを意味する場合にのみ正当化されます。 –