私の記憶は私に失敗しています。トリガーに基づく単純な監査ログ テーブル
ID int (identity, PK)
CustomerID int
Name varchar(255)
Address varchar(255)
AuditDateTime datetime
AuditCode char(1)
があります。次のようなデータが
ID CustomerID Name Address AuditDateTime AuditCode
1 123 Bob 123 Internet Way 2009-07-17 13:18:06.353I
2 123 Bob 123 Internet Way 2009-07-17 13:19:02.117D
3 123 Jerry 123 Internet Way 2009-07-17 13:36:03.517I
4 123 Bob 123 My Edited Way 2009-07-17 13:36:08.050U
5 100 Arnold 100 SkyNet Way 2009-07-17 13:36:18.607I
6 100 Nicky 100 Star Way 2009-07-17 13:36:25.920U
7 110 Blondie 110 Another Way 2009-07-17 13:36:42.313I
8 113 Sally 113 Yet another Way 2009-07-17 13:36:57.627I
あります。 参考までに、I は挿入、D は削除、U は更新です。
監査テーブルに欠けているものはありますか? 次のステップは、変更のみを記録する監査テーブルを作成することですが、特定の時間枠の最新のレコードを抽出できます。私の人生では、どの検索エンジンでも簡単に見つけることができません。リンクも機能します。助けてくれてありがとう。
4 に答える
わかりました、監査ログ テーブルについていくつか説明します。
ほとんどのアプリケーションでは、監査テーブルの挿入を非常に高速にする必要があります。
監査ログが本当に診断目的であるか、非常に不規則な監査理由である場合、最も迅速な挿入基準は、挿入時にテーブルを物理的に順序付けることです。
これは、監査時間をクラスター化インデックスの最初の列として配置することを意味します。
create unique clustered index idx_mytable on mytable(AuditDateTime, ID)
これにより、AuditDateTime O(log n) および O(1) 挿入時に非常に効率的な選択クエリが可能になります。
CustomerID ごとに監査テーブルを検索する場合は、妥協する必要があります。
(CustomerID, AuditDateTime) に非クラスター化インデックスを追加できます。これにより、顧客ごとの監査履歴の O(log n) ルックアップが可能になりますが、コストは、挿入時の非クラスター化インデックスのメンテナンスになります。 log n) 逆に。
ただし、その挿入時間のペナルティは、CustomerID にインデックスがなく、これが実行される通常のクエリである場合に支払う必要があるテーブル スキャン (つまり、O(n) 時間の複雑さのコスト) よりも望ましい場合があります。不規則なクエリの書き込みプロセスのためにテーブルをロックする O(n) ルックアップはライターをブロックする可能性があるため、リーダーがコミットをブロックしないことが保証されている場合、ライターの利益になる場合があります。それらをサポートするための適切なインデックスがないため、リーダーはテーブルスキャンを行う必要があるため....
追加: 特定の時間枠に制限しようとしている場合、まず最も重要なことは、AuditDateTime のインデックスです。そして、AuditDateTime の順序で挿入するときにクラスター化します。これは、クエリを最初から効率的にするためにできる最大のことです。
次に、特定の期間内のすべての CustomerID の最新の更新を探している場合は、その後も挿入日によって制限されたデータのフル スキャンが必要です。
範囲の間で、監査テーブルに対してサブクエリを実行する必要があります。
select CustomerID, max(AuditDateTime) MaxAuditDateTime
from AuditTrail
where AuditDateTime >= @begin and Audit DateTime <= @end
次に、それを適切な選択クエリに組み込みます。
select AuditTrail.* from AuditTrail
inner join
(select CustomerID, max(AuditDateTime) MaxAuditDateTime
from AuditTrail
where AuditDateTime >= @begin and Audit DateTime <= @end
) filtration
on filtration.CustomerID = AuditTrail.CustomerID and
filtration.AuditDateTime = AuditTrail.AuditDateTime
監査履歴を保持するもう 1 つの (より良い?) 方法は、auditDateTime および AuditCode 列ではなく、'startDate' および 'endDate' 列を使用することです。これは、多くの場合、データ ウェアハウスでタイプ 2 の変更 (行の新しいバージョン) を追跡する際のアプローチです。
これにより、現在の行 (WHERE endDate が NULL) をより直接的に選択できるようになり、更新を挿入または削除とは異なる方法で処理する必要がなくなります。単純に 3 つのケースがあります。
- 挿入: 開始日と NULL 終了日とともに行全体をコピーします
- 削除: 既存の現在の行の終了日を設定します (endDate は NULL)。
- 更新: 削除してから挿入する
あなたの選択は単純です:
select * from AuditTable where endDate is NULL
とにかく、既存のスキーマに対する私のクエリは次のとおりです。
declare @from datetime
declare @to datetime
select b.* from (
select
customerId
max(auditdatetime) 'auditDateTime'
from
AuditTable
where
auditcode in ('I', 'U')
and auditdatetime between @from and @to
group by customerId
having
/* rely on "current" being defined as INSERTS > DELETES */
sum(case when auditcode = 'I' then 1 else 0 end) >
sum(case when auditcode = 'D' then 1 else 0 end)
) a
cross apply(
select top 1 customerId, name, address, auditdateTime
from AuditTable
where auditdatetime = a.auditdatetime and customerId = a.customerId
) b
参考文献
データ ウェアハウスのクリブシートですが、タイプ 2 の変更 (追跡したいもの) に関する適切なセクションがあります。
データ ウェアハウスに関する MSDN ページ
別のアプローチは、サブセレクトを使用することです
select a.ID
, a.CustomerID
, a.Name
, a.Address
, a.AuditDateTime
, a.AuditCode
from myauditlogtable a,
(select s.id as maxid,max(s.AuditDateTime)
from myauditlogtable as s
group by maxid)
as subq
where subq.maxid=a.id;
開始時間と終了時間は?たとえば、午前 1 時から午前 3 時の間、
または開始日と終了日の時間のように? 例: 2009-07-17 13:36 から 2009-07-18 13:36 のように