SQL 標準は監査ログの質問から現在のレコードを選択します
質問
記憶力が落ちてきています。トリガーに基づいた単純な監査ログ テーブルがあります。
IDint (アイデンティティ、PK)
顧客ID整数
名前varchar(255)
住所varchar(255)
監査日時日付時刻
監査コード文字(1)
次のようなデータがあります。
ID顧客ID名前住所監査日時監査コード
1 123 ボブ123 インターネットウェイ2009-07-17 13:18:06.353私
2 123 ボブ123 インターネットウェイ2009-07-17 13:19:02.117D
3 123 ジェリー123 インターネットウェイ2009-07-17 13:36:03.517私
4 123 ボブ123 編集した私のやり方2009-07-17 13:36:08.050U
5 100 アーノルド100 スカイネット ウェイ2009-07-17 13:36:18.607私
6 100 ニッキー100 スターウェイ2009-07-17 13:36:25.920U
7 110 ブロンディ110 別の方法2009-07-17 13:36:42.313私
8 113 サリー113 さらに別の方法2009-07-17 13:36:57.627私
開始時刻と終了時刻の間の最新のレコードをすべて取得する効率的な選択ステートメントは何でしょうか? ご参考までに:I は挿入、D は削除、U は更新を表します。
監査テーブルに何か欠けているものはありますか?次のステップは、変更を記録するだけでありながら、指定された期間の最新のレコードを抽出できる監査テーブルを作成することです。私の一生の間、どの検索エンジンでも簡単に見つけることはできません。リンクも機能します。助けてくれてありがとう。
解決
監査履歴を保存する別の (より良い?) 方法は、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 ページの データウェアハウス
他のヒント
さて、監査ログテーブルについていくつか説明します。
ほとんどのアプリケーションでは、監査テーブルの挿入が非常に高速であることが望まれます。
監査ログが本当に診断目的である場合、または非常に不規則な監査理由である場合、最も迅速な挿入基準は、挿入時にテーブルを物理的に順序付けすることです。
これは、クラスター化インデックスの最初の列として監査時間を配置することを意味します。
create unique clustered index idx_mytable on mytable(AuditDateTime, ID)
これにより、AuditDateTime O(log n) および O(1) の挿入時に非常に効率的な選択クエリが可能になります。
CustomerID ごとに監査テーブルを検索したい場合は、妥協する必要があります。
(CustomerID, AuditDateTime) に非クラスター化インデックスを追加できます。これにより、顧客ごとの監査履歴の O(log n) 検索が可能になります。ただし、コストは挿入時の非クラスター化インデックスの維持費になります。その維持費は O( 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
別のアプローチは、サブ選択を使用することです
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 のように