2

我的记忆力让我失望。我有一个基于触发器的简单审计日志表:

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

4 回答 4

2

保存审计历史的另一种(更好?)方法是使用“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数据仓库页面

于 2009-07-17T22:58:56.680 回答
2

好的,关于审计日志表的一些事情。

对于大多数应用程序,我们希望审计表在插入时非常快速。

如果审计日志确实是出于诊断或非常不规则的审计原因,那么最快的插入标准是在插入时对表进行物理排序。

这意味着将审计时间作为聚集索引的第一列,例如

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
于 2009-07-17T23:05:08.883 回答
1

另一种方法是使用子选择

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;
于 2009-07-17T23:04:08.513 回答
0

开始和结束时间?例如,在凌晨 1 点到凌晨 3 点之间
或开始和结束日期时间之间?例如在 2009-07-17 13:36 到 2009-07-18 13:36

于 2009-07-17T23:05:43.377 回答