web-dev-qa-db-ja.com

SQL Serverでのデータ変更の監査

私は、データベースでのデータ変更の監査の実装を担当しています。市販品があることは承知していますが、どちらのデザインが良いか知りたいです。どの行についても、変更された値、変更者、変更がいつ行われたかを知りたいと思います。

エンタープライズレベルのデータ(おそらく数百万行)があり、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つの設計が他の設計よりも優れているか、またはこれらの設計の利点、欠点、またはリスクは何ですか?考慮すべき他のデザインパターンはありますか?

2
turtlechief

比較的不変の履歴の追跡では、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のテンポラルテーブルは、履歴テーブルの行の最新バージョンを保持しません(行が削除されていない限り、概念的に最後の既存バージョンを考慮しているかどうかによって異なります)最新であるかどうか)変更前のバージョンのみをコピーします。一部の履歴/監査実装は、各行の最新バージョンと過去のバージョンを保持しているため、履歴テーブルにはすべてが含まれています。

4
David Spillett

簡単な方法は、削除および挿入された疑似テーブルにあるすべてのデータをトリガーに格納することです。これは、クエリが簡単で、テーブルに簡単にロードできるためです。これは基本的に、データ変更前後のスナップショットです。典型的なスキーマは次のようなものです。

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
1
Biju jose

データの変更に関する限り、「誰」をキャプチャすることは、「何」よりも難しい場合があります。

「何を」、テンポラルテーブルの実装( 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)でサポートされています。

1
HandyD

MDCCLの寛大な支援のおかげで、ここに私が達した結論があります。 programAssignmentテーブルは、プログラムテーブルと組織テーブルの間の関係を表します。 programAssignmentHistoryは、programAssignmentのスナップショットまたは「バージョン管理」を表します。密接に関連していますが、同じものではないため、エンティティリレーションシップダイアグラム(ERD)では別のエンティティである必要があります。私のERDは、StackOverflowの投稿 here および here と同様の構造になります。

ERDは、データベースのレイアウト方法の参照または開始点になるはずです。

私が採用しているソリューションは、監査する必要があるすべてのテーブルに対して個別の履歴テーブルを持つことです。したがって、UsersテーブルにはUserHistoryテーブルがあり、ProgramsTableにはProgramHistoryテーブルなどがあります。

1
turtlechief