web-dev-qa-db-ja.com

履歴/テンポラルテーブルのベストプラクティス

履歴を追跡したい特定のフィールドと、履歴を追跡したくない特定のフィールドを持つオブジェクトがあるとします。正規化の観点から、次のスキーマは大丈夫です:

CREATE TABLE MyObject AS (
    MyObjectId INT IDENTITY NOT NULL PRIMARY KEY,
    MyObjectField1 VARCHAR(100) NOT NULL,
    MyObjectField2 VARCHAR(100) NOT NULL,
    MyObjectField3 VARCHAR(100) NOT NULL,
    MyObjectTrackedField1 VARCHAR(100) NOT NULL,
    MyObjectTrackedField2 VARCHAR(100) NOT NULL,
    MyObjectTrackedField3 VARCHAR(100) NOT NULL,
)
CREATE TABLE MyObjectHistory AS (
    MyObjectHistoryId INT IDENTITY NOT NULL PRIMARY KEY,
    MyObjectId INT NOT NULL FOREIGN KEY REFERENCES MyObject(MyObjectId),
    MyObjectTrackedField1 VARCHAR(100) NOT NULL,
    MyObjectTrackedField2 VARCHAR(100) NOT NULL,
    MyObjectTrackedField3 VARCHAR(100) NOT NULL,
)

myObjectHistoryには、最新のリビジョンを除くすべての追跡フィールドが含まれます。または、次のように、追跡されるすべてのフィールドが1つのテーブルにあり、最新のものを含むすべてのリビジョンがそのテーブルにある必要があります。

CREATE TABLE MyObject AS (
    MyObjectId INT IDENTITY NOT NULL PRIMARY KEY,
    MyObjectField1 VARCHAR(100) NOT NULL,
    MyObjectField2 VARCHAR(100) NOT NULL,
    MyObjectField3 VARCHAR(100) NOT NULL,
)
CREATE TABLE MyObjectHistory AS (
    MyObjectHistoryId INT IDENTITY NOT NULL PRIMARY KEY,
    MyObjectId INT NOT NULL FOREIGN KEY REFERENCES MyObject(MyObjectId),
    MyObjectTrackedField1 VARCHAR(100) NOT NULL,
    MyObjectTrackedField2 VARCHAR(100) NOT NULL,
    MyObjectTrackedField3 VARCHAR(100) NOT NULL,
)
11
cubetwo1729

実用的なデータアクセスの理由から、最初のオプションの構造を使用する必要がありますが、追跡された列値のすべてのバージョン現在のバージョンを含むを履歴テーブルに保持してください。

これは、一般に、履歴を確認する場合に、現在のバージョンと過去のすべてのバージョンを含める必要があるためです。歴史を見たくないときは、邪魔にならないようにしたいでしょう。多くの場合、これは履歴を個別のスキーマまたはデータベースに完全に分離することを意味します。履歴を現在のデータと同じスキーマに保持している場合でも、履歴データ(現在の値を含む)を参照するクエリは、2つのソースを基本的に結合する必要があるため、はるかに複雑になります。

7
Joel Brown

おそらく履歴を表示する必要はほとんどありませんが、現在の値を表示する必要があることが多いため、最初のバージョンをお勧めします。履歴テーブルにはトリガーからデータを入力する必要があるため、一般的にデータが同期されなくなることを心配する必要はありません。したがって、MyObjectに100万のレコードがあり、MyObjectHistoryに10,000,000のレコードがあるとします。現在の値を取得するために、それだけ多くのレコードを持つテーブルに本当に結合しますか?

現在の値よりも頻繁に、またはより頻繁に履歴を照会する必要がある場合は、2番目の構造が機能します。 (そして、特定の日付の値を表示する場合は、クエリをより簡単にするためにbegindateフィールドとenddateフィールドをそこに含めます。)

ところで、変更が発生した順序を確認できるように、履歴フィールドに日付フィールドを追加します。一時的な順序のIDに依存することはできません。 PLus previosu値について質問があり、それが変更された場合は、知っておく必要があります。変更元のアプリケーション(複数のアプリケーションがある場合)および/または変更を行った人の値も入力する場合があります。

2
HLGEM

#1には2つの重要な理由があります。 1つはHLGEMが指摘するサイズの問題ですが、他にも重要な問題があります。

通常、監査証跡では、時間の経過とともに要件が作成されます。最終的に、データベースユーザー、変更の時間などを追跡する必要がある場合があります。監査証跡の要件とメインテーブルは、時間の経過とともに多少独立して変更される可能性があります。最後に、一定期間後に独立して完全に独立したテーブルで監査証跡データをパージする必要が生じる可能性があります。

もちろん、履歴データは現在の計算に使用される可能性があり、レコードの数は比較的少ないため、それらを完全にマージしたい場合もあります(LedgerSMBの税率の場合のように)。

ただし、このようなテーブルにオブジェクトを格納しても、優れた正規化されたデザインが得られることはめったにありません。私の経験では、正規化された適切なストレージとアプリケーションオブジェクトモデルの間のカプセル化が本当に必要です。

0
Chris Travers