web-dev-qa-db-ja.com

データベース-データのバージョン管理

データベース内のデータのバージョン管理に関するSO(- this one など)に関するいくつかの質問を読みました。

言及された提案のいくつかが気に入りました。私は最も長い間、自分のテーブルの多くを修正する必要がありました(必要でした)が、それに取り掛かることはありませんでした。単純なデータベース作業だけを担当するプログラマーである私は、実際にこれを行うにはどうしたらよいのかと思っていました。

SQL構文での実際の解決策は求めていません。私は最終的に自分でそれを理解することができます(またはSO時が来たら投稿します)。私は、人々にどのようにそれを行うか、そしてそこで起こり得るパフォーマンスの問題についてコメントするように求めています何億件ものレコードを「改訂」することになったかもしれませんが、以下の例に基づいている限り、他の提案も可能です。

簡単な例を考えます:

Person
------------------------------------------------
ID                UINT NOT NULL,
PersonID          UINT NOT NULL,
Name              VARCHAR(200) NOT NULL,
DOB               DATE NOT NULL,
Email             VARCHAR(100) NOT NULL

Audit
------------------------------------------------
ID                UINT NOT NULL,
UserID            UINT NOT NULL,               -- Who
TableName         VARCHAR(50) NOT NULL,        -- What
OldRecID          UINT NOT NULL,               -- Where
NewRecID          UINT NOT NULL,
AffectedOn        DATE NOT NULL,               -- When
Comment           VARCHAR(500) NOT NULL        -- Why

TableNameが文字列の場合、Auditテーブルを他のテーブル(Personなど)にリンクする方法がわかりません。

また、入力するGUIが3つあると仮定します。

  1. 特定の個人IDの完全なレコード
  2. すべての人をリストするテーブルビュー(ID別)
  3. 各エントリの下に各ユーザーのリビジョン情報(1人あたりのリビジョン数、リビジョンの日付、リビジョンコメントなど)を表示するビュー。最新のリビジョン順に表示されます。

1と2を実行するには、PersonテーブルまたはAuditテーブルをクエリする方が良いでしょうか?

3を達成するには、いわゆるデータベースエキスパートがすべてのレコードを取得してソフトウェアに渡し、処理するか、PersonIDと影響を受ける日付でグループ化するのでしょうか。これは通常、1つのクエリで処理されますか、それとも複数のクエリで処理されますか?

51
Jeach

私は長年にわたってさまざまな監査スキームを行ってきましたが、現在は次のようなものを実装する予定です。

Person
------------------------------------------------
ID                UINT NOT NULL,
PersonID          UINT NOT NULL,
Name              VARCHAR(200) NOT NULL,
DOB               DATE NOT NULL,
Email             VARCHAR(100) NOT NULL


Person_History
------------------------------------------------
ID                UINT NOT NULL,
PersonID          UINT NOT NULL,
Name              VARCHAR(200) NOT NULL,
DOB               DATE NOT NULL,
Email             VARCHAR(100) NOT NULL
AuditID           UINT NOT NULL


Audit
------------------------------------------------
ID                UINT NOT NULL,
UserID            UINT NOT NULL,               -- Who
AffectedOn        DATE NOT NULL,               -- When
Comment           VARCHAR(500) NOT NULL        -- Why

現在のレコードは常にPersonテーブルにあります。変更がある場合、監査レコードが作成され、古いレコードがPerson_Historyテーブルにコピーされます(IDは変更されず、複数のバージョンが存在する可能性があることに注意してください)。

監査IDは* _Historyテーブルにあるため、必要に応じて複数のレコード変更を1つの監査レコードにリンクできます。

編集:
各ベーステーブルに個別の履歴テーブルがなく、同じテーブルを使用して古いレコードと「削除された」レコードを保持する場合は、ステータスフラグでレコードをマークする必要があります。現在のレコードを照会するとき、それは本当の痛みです-私がそれをやったことを信じてください。

46
DJ.

通常どおりにテーブルを作成し、各レコードにModifiedDate列(および必要に応じてModifiedBy)を設定し、IDによってデータをグループ化したマテリアライズドビューを介してすべてのデータアクセスを実行してから、HAVING ModifiedDate = MAX(ModifiedDate )?

このようにして、別のレコードと同じIDを持つ新しいレコードを追加すると、ビューから古いレコードが削除されます。履歴を照会する場合は、ビューを経由しないでください

私はいつも同じColmで異なるテーブルを維持することは複雑でエラーが発生しやすいことを発見しました。

6
mcintyre321

ベーステーブルごとに履歴テーブルを使用するDJの投稿と、起こりうるパフォーマンスの問題に関するKarlのコメントに続いて、テーブル間でレコードを転送するための最速の方法を理解するために、少しSQLの調査を行いました。

私は見つけたものを文書化したかっただけです:

SQLフェッチを実行してレコードをベーステーブルからロードし、SQLプッシュを実行してレコードを履歴テーブルに配置し、ベーステーブルを更新して変更されたデータを挿入する必要があると考えました。合計3つのトランザクション。

しかし、驚いたことに、SELECT INTO構文を使用する1つのSQLステートメントを使用して、最初の2つのトランザクションを実行できることに気付きました。これを行うとパフォーマンスが100倍速くなると私は賭けています。

その後、ベーステーブル内の新しいデータでレコードを更新するだけです。

私はまだ3つのトランザクションすべてを一度に実行する1つのSQLステートメントを見つけていません(私がそうするかどうかは疑問です)。

2
Jeach

私はあなたの監査テーブルが好きです、それは良いスタートです。監査テーブルにカーディナリティの問題があるので、2つのテーブルとしてバストアウトします。

Person
------------------------------------------------
ID                UINT NOT NULL,
PersonID          UINT NOT NULL,
Name              VARCHAR(200) NOT NULL,
DOB               DATE NOT NULL,
Email             VARCHAR(100) NOT NULL,
AuditID           UINT NOT NULL 

Audit
------------------------------------------------
ID                UINT NOT NULL,
TableName         VARCHAR(50) NOT NULL,        -- What
TableKey          UINT NOT NULL,
CreateDate        DATETIME NOT NULL  DEFAULT(NOW),
CreateUserID      UINT NOT NULL,
ChangeDate        DATETIME NOT NULL  DEFAULT(NOW),
ChangeUserID      UINT NOT NULL

Audit_Item
------------------------------------------------
ID                UINT NOT NULL,
AuditID           UINT NOT NULL,               -- Which audit record
UserID            UINT NOT NULL,               -- Who
OldRecID          UINT NOT NULL,               -- Where
NewRecID          UINT NOT NULL,
AffectedOn        DATE NOT NULL,               -- When
Comment           VARCHAR(500) NOT NULL        -- Why

提案された最初のレイアウトには、2つのPersonレコードを指す(私はそう思います)単一の監査レコードがあります。この設計の課題は次のとおりです。

  • 個人テーブルのどのレコードが現在の「実際の」レコードですか?
  • 個人レコードの変更履歴全体をどのように表現しますか? Personテーブルの2つのレコードを指している場合は、ポイント#1を参照してください。現在のレコードはどれですか。
  • Create *、Change *フィールドは、Audit_Itemレコードのコレクションからロールアップされます。アクセスを容易にするためだけにあります。
  • PersonテーブルのAuditIDキーを使用すると、WHERE TableName='Person'句を使用してAuditテーブルにクエリを実行しなくても、Auditテーブルに戻り、個々のPersonの履歴を取得できます。
1
Jeff Fritz