私は、データベースでのデータ変更の監査の実装を担当しています。市販品があることは承知していますが、どちらのデザインが良いか知りたいです。どの行についても、変更された値、変更者、変更がいつ行われたかを知りたいと思います。
エンタープライズレベルのデータ(おそらく数百万行)があり、MS SQL Server Web(ver.13)を使用しています。
推奨される設計では、すべてのテーブルで一意の主キーとしてhistidを使用しました。通常は一意の主キーになるものが繰り返されます(行のバージョンごとに1回)。
-- primary key is histid.
-- the current programAssignmentId is what I am typically searching for.
SELECT
[histid] -- column is primary key.
,[isCurrent]
,[lastupdate] -- column is indexed.
,[programAssignmentId] -- column is indexed.
,[programid] -- foreign key, column is indexed.
,[entityid] -- foreign key, column is indexed.
,[lastUpdatedById] -- foreign key, column is indexed.
,[effectiveDate]
,[orderno]
,[status]
,[deleted]
,[created]
FROM [dbo].[mem_programAssignments]
WHERE [programAssignmentId] = 'a43c2a3e-d3a7-4f40-aeb0-cc0552c62da2'
ORDER BY [programAssignmentId], [lastupdate] DESC
次の結果が得られます。
histid isCurrent lastupdate programAssignmentId programid
=================================== ========= ======================= ==================================== ====================================
7330075F-B076-113D-B548CDDD0BF5D4B8 1 2017-08-17 13:37:56.950 a43c2a3e-d3a7-4f40-aeb0-cc0552c62da2 5f4d1469-44a0-49c7-856d-f35bf729e661
73291EC6-F168-E817-EB42EB46A5AFB08C 0 2017-08-17 13:37:11.670 a43c2a3e-d3a7-4f40-aeb0-cc0552c62da2 5f4d1469-44a0-49c7-856d-f35bf729e661
56F73DE0-0137-4CBF-969AEDFE55229E3B 0 2017-04-24 18:49:11.000 a43c2a3e-d3a7-4f40-aeb0-cc0552c62da2 5f4d1469-44a0-49c7-856d-f35bf729e661
競合する設計は、変更を記録する1つ以上のhistoryテーブルを提案しました。
SELECT
[id]
,[reference_class_name]
,[reference_table]
,[reference_id]
,[column_name]
,[old_value]
,[new_value]
,[created_at]
,[user_id]
,[user_name]
,[message]
FROM [dbo].[history]
次のようなものを出力します:
id reference_class_name reference_table reference_id column_name old_value new_value
== ========================= ====================== ===================================== =========== ========== ===========
24 Models\ProgramAssignement mem_programAssignments b40b2d80-99d3-11e8-a0ad-29cac47b46c7 startDate 2017-01-01 2018-07-11
25 Models\ProgramAssignement mem_programAssignments b40b2d80-99d3-11e8-a0ad-29cac47b46c7 deductible 15000000 30000000
この場合のreference_idは、reference_table列にリストされているテーブルの主キーのid値を参照します(この場合はmem_programAssignments)。 。
2番目の設計の利点は、履歴値を現在の値から分離することですが、明らかな欠点は、履歴テーブルが非常に大きくなる可能性があることです。
1つの設計が他の設計よりも優れているか、またはこれらの設計の利点、欠点、またはリスクは何ですか?考慮すべき他のデザインパターンはありますか?
比較的不変の履歴の追跡では、SQL Serverの最新の十分なエディションを使用しているので、 システムバージョンの一時テーブル が適しています(他のデータベースでも同様のサポートが利用可能です)。彼らはすべての状況に適しているわけではないことを意味するいくつかの落とし穴を持っていますが、それらがモデルに適合する場合、「無料」でそのチャンクを実装し、いくつかの便利な time-travelling構文 手動で履歴を照会する代わりに、_SELECT <stuff> FROM <table> AS OF <date_time>
_や_BETWEEN <date_time> AND <date_time>
_など。
監査には追加の情報が必要ですが、whatが発生した場合when追跡する必要がありますwho変更を加えました。 1つのオプションは、追跡されている各テーブルにユーザーID列を追加することです。これは履歴にも含まれます。すべてが独自のSQL Serverログインを持っているユーザーを追跡している場合は、トリガーによって_SYSTEM_USER
_/SUSER_NAME()
から値を読み取ることで、ユーザーを追跡できますが、常に同じアプリケーションアカウントを使用してSQL Serverにログインするアプリケーションでは、アプリケーションロジックにその列の作成を実装する必要があります。
1つのデザインが他のデザインより優れている
2番目の提案は、本質的に拡張プロパティバッグです(各プロパティは実際にはプロパティ+時間です)。柔軟である可能性もありますが、たとえば、モデルを非効率的に報告するなどの独自の重要な問題があります。この長所と短所に関する詳細な説明については、DBA.SEなどで「エンティティ属性値モデル」(通常、「プロパティバッグ」は同義語であるより専門的な用語)を参照してください。それは一般的にアンチパターンであると考えられています。
isCurrent
組み込みのサポートを使用する代わりに独自の履歴構造をロールバックする場合(組み込みが何らかの理由で不適切であるか、たとえば、クロスプラットフォーム互換の何かを実装する必要があるため)、すべてを保持しないことをお勧めしますコアテーブルのデータ。代わりに、現在のデータを独自の履歴に保持します。これにより、現在のデータのクエリが可能になり、アプリケーションで最も一般的に行われることになるため、エラーが発生しにくくなります。すべてのバージョンを保持する場合1 履歴構造では、これらの構造にisCurrent
フラグが残っている場合があります。
1 SQL Serverのテンポラルテーブルは、履歴テーブルの行の最新バージョンを保持しません(行が削除されていない限り、概念的に最後の既存バージョンを考慮しているかどうかによって異なります)最新であるかどうか)変更前のバージョンのみをコピーします。一部の履歴/監査実装は、各行の最新バージョンと過去のバージョンを保持しているため、履歴テーブルにはすべてが含まれています。
簡単な方法は、削除および挿入された疑似テーブルにあるすべてのデータをトリガーに格納することです。これは、クエリが簡単で、テーブルに簡単にロードできるためです。これは基本的に、データ変更前後のスナップショットです。典型的なスキーマは次のようなものです。
CREATE TABLE customer (
customerid INT IDENTITY(1, 1)
PRIMARY KEY,
customername VARCHAR(1000),
customeraddress VARCHAR(1000),
phone INT,
email VARCHAR(255),
inserteduserid INT,
inserteddatetime DATETIME,
updateduserid INT,
updateddate DATETIME
);
CREATE TABLE customer_audit (
customer_audit_ID BIGINT IDENTITY(1, 1)
PRIMARY KEY,
auditdate DATETIME,
ins_customerid INT,
ins_customername VARCHAR(1000),
ins_customeraddress VARCHAR(1000),
ins_phone INT,
ins_email VARCHAR(255),
ins_inserteduserid INT,
ins_inserteddatetime DATETIME,
ins_updateduserid INT,
ins_updateddate DATETIME,
del_customerid INT,
del_customername VARCHAR(1000),
del_customeraddress VARCHAR(1000),
del_phone INT,
del_email VARCHAR(255),
del_inserteduserid INT,
del_inserteddatetime DATETIME,
del_updateduserid INT,
del_updateddate DATETIME
);
GO
CREATE TRIGGER customer_audit
ON customer
AFTER INSERT, UPDATE, DELETE
AS
INSERT INTO customer_audit
SELECT GETDATE(),
i.customerid,
i.customername,
i.customeraddress,
i.phone,
i.email,
i.inserteduserid,
i.inserteddatetime,
i.updateduserid,
i.updateddate,
d.customerid,
d.customername,
d.customeraddress,
d.phone,
d.email,
d.inserteduserid,
d.inserteddatetime,
d.updateduserid,
d.updateddate
FROM inserted i
FULL OUTER JOIN deleted d
ON i.customerid = d.deleted;
この後、行の挿入、削除、更新を簡単に行うことができます。唯一の注意点は、実際のテーブルでデータ型の変更があるかどうか、または監査テーブルでも行う必要がある何かがある場合です。また、このテーブルは非常に速く成長する可能性があり、パフォーマンスの観点からは適切でない場合があります。しかし、仕事をします。この設計アプローチを可能にするいくつかの質問に答えることは簡単です。
1.当日変更されたお客様の電話番号を探す
select * from customer_audit
where ins_customerid is not null and del_customerid is not null
and ins_phone<>del_phone
and CONVERT(DATE,auditdate)=CONVERT(DATE,getdate())
2.特定の顧客の以前の電話番号
;WITH CTE
AS(
select *,row_number()
OVER(parition by ins_customerid order by auditdate desc) from customer_audit
where ins_customerid is not null and del_customerid is not null
and ins_phone<>del_phone
)
select del_phone FROM cte
where rnk=1
データの変更に関する限り、「誰」をキャプチャすることは、「何」よりも難しい場合があります。
「何を」、テンポラルテーブルの実装( https://docs.Microsoft.com/en-us/sql/relational-databases/tables/temporal-tables?view=sql-server-2017 )データの特定の時点の状態を提供することは、ホイールを再発明することなくデータベースにこのロジックを実装する簡単な方法です。基本的に、すべてのレコードの有効開始日と有効終了日を記録します。レコードが変更されると、これらの値はそれに応じて調整され、行の新しい「現在の」コピーが作成されます。これにより、行の履歴全体を任意の時点まで表示できます。
"who"については、SQL Server Audit( https://docs.Microsoft.com/en-us/sql/relational-databases/security/auditing/sql-server-audit- database-engine?view = sql-server-2017 )データベースでDML変更を行うユーザー、アプリケーションなどの詳細を提供できます。トリガーは機能しますが、特に書き込みが多いデータベースではパフォーマンスに影響する傾向があります。
テンポラルテーブルと監査ファイルの両方の日時値は、それらを相関させるのに役立ちますが、完全に一致するわけではないため、ビジーなシステムでは、それらを正確に相関させることが難しい場合があります。
これらのオプションは、トリガーや大きな履歴テーブルよりもパフォーマンスへの影響が少なく、目的に合わせて設計されたシステム機能に基づいて構築されているため、将来のサポートがより簡単になります。
テンポラルテーブルと監査の両方がSQL Server Web 2016(V13)でサポートされています。
MDCCLの寛大な支援のおかげで、ここに私が達した結論があります。 programAssignmentテーブルは、プログラムテーブルと組織テーブルの間の関係を表します。 programAssignmentHistoryは、programAssignmentのスナップショットまたは「バージョン管理」を表します。密接に関連していますが、同じものではないため、エンティティリレーションシップダイアグラム(ERD)では別のエンティティである必要があります。私のERDは、StackOverflowの投稿 here および here と同様の構造になります。
ERDは、データベースのレイアウト方法の参照または開始点になるはずです。
私が採用しているソリューションは、監査する必要があるすべてのテーブルに対して個別の履歴テーブルを持つことです。したがって、UsersテーブルにはUserHistoryテーブルがあり、ProgramsTableにはProgramHistoryテーブルなどがあります。