我的记忆力让我失望。我有一个基于触发器的简单审计日志表:
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 回答
保存审计历史的另一种(更好?)方法是使用“startDate”和“endDate”列,而不是 auditDateTime 和 AuditCode 列。这通常是在数据仓库中跟踪类型 2 更改(行的新版本)的方法。
这使您可以更直接地选择当前行(WHERE endDate 为 NULL),并且您不需要将更新与插入或删除区别对待。你只有三种情况:
- 插入:复制整行以及开始日期和 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
参考
用于数据仓库的Cribsheet,但有一个很好的关于类型 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