我有一个关于我们在许多解决方案中看到的每个记录的两个附加列(timeCreated,timeLastUpdated)的问题。我的问题:还有更好的选择吗?
场景:你有一个庞大的数据库(就表而言,而不是记录),然后客户来,并要求你为80%的表添加“时间戳”。
我相信这可以通过使用单独的表(TIMESTAMPS)来完成。除了明显的时间戳列之外,该表还将具有正在更新的表的表名和主键。 (我在这里假设您使用int作为大多数表的主键,但表名很可能必须是字符串)。
想象一下这个基本情景。我们有两张桌子:
付款: - (您通常的记录)
TIMESTAMP: - {当前时间戳} + {TABLE_UPDATED
,id_of_entry_updated
,timestamp_type
}
请注意,在此设计中,您的本机付款对象中不需要这两个“额外”列(顺便说一句,这可能会通过您的ORM解决方案),因为您现在正在按TABLE_UPDATED
编制索引和id_of_entry_updated
。此外,timestamp_type
将告诉您条目是否用于插入(例如“1”),更新(例如“2”)以及您可能想要添加的任何其他内容,例如“删除”。
我想知道你对这个设计的看法。我最感兴趣的是最佳实践,有效和随着时间的推移而扩展。参考,链接,博客条目非常受欢迎。我知道至少有一项专利(待定)试图解决这个问题,但目前看来细节尚未公开。
干杯, 爱德华
答案 0 :(得分:12)
当您处于此状态时,还会记录进行更改的用户。
具有单独表设计的缺陷(除了其他人强调的连接性能之外)是假设每个表都有一个密钥的标识列。这并非总是如此。
如果你使用SQL Server,那么新的2008版本支持他们称之为Change Data Capture
的东西,它们会消除你所说的很多痛苦。我认为Oracle也可能有类似的东西。
更新:显然Oracle称其为与SQL Server相同的东西。或者更确切地说,SQL Server将其称为与Oracle相同的东西,因为Oracle的实现首先出现;) http://www.oracle.com/technology/oramag/oracle/03-nov/o63tech_bi.html
答案 1 :(得分:10)
我使用了一种设计,其中每个要审核的表都有两个表:
create table NAME (
name_id int,
first_name varchar
last_name varchar
-- any other table/column constraints
)
create table NAME_AUDIT (
name_audit_id int
name_id int
first_name varchar
last_name varchar
update_type char(1) -- 'U', 'D', 'C'
update_date datetime
-- no table constraints really, outside of name_audit_id as PK
)
创建一个数据库触发器,每次对NAME_AUDIT
执行任何操作时都会填充NAME
。通过这种方式,您可以记录对表格所做的每一次更改。应用程序对此并不了解,因为它是由数据库触发器维护的。
它运行良好,不需要对应用程序代码进行任何更改即可实现。
答案 2 :(得分:5)
我认为我更喜欢将时间戳添加到各个表中。在复合键上加入时间戳表 - 其中一个是字符串 - 会变慢,如果你有大量数据,它最终将成为一个真正的问题。
此外,很多时候,当您查看时间戳时,您正在调试应用程序中的问题并且您希望数据就在那里,而不是总是必须加入另一个表。< / p>
答案 3 :(得分:1)
您建议的方法的优点是,它为您提供了向TIMESTAMP表添加其他字段的选项,例如跟踪进行更改的用户。您还可以跟踪对敏感字段的编辑,例如谁重新定价此合同?
在单独的文件中记录记录更改意味着您可以对记录显示多个更改,例如:
mm / dd / yy hh:mm:ss由XXX添加 mm / dd / yy hh:mm:ss字段价格由XXX更改, mm / dd / yy hh:mm:ss由XXX删除的记录
一个缺点是将执行插入TIMESTAMPS表的额外代码以反映主表中的更改。
答案 4 :(得分:1)
如果您设置时间戳内容来运行触发器,那么可以记录任何可以触发触发器的操作(读取?)。也可能存在一些锁定优势。
(拿一粒盐,我不是DBA或SQL大师)
答案 5 :(得分:1)
是的,我喜欢这种设计,并在某些系统中使用它。通常,有一些变体:
LogID int
Action varchar(1) -- ADDED (A)/UPDATED (U)/DELETED (D)
UserID varchar(20) -- UserID of culprit :)
Timestamp datetime -- Date/Time
TableName varchar(50) -- Table Name or Stored Procedure ran
UniqueID int -- Unique ID of record acted upon
Notes varchar(1000) -- Other notes Stored Procedure or Application may provide
答案 6 :(得分:1)
您设计的一个噩梦就是每次插入,更新或删除都必须触及该表。这可能会导致主要性能和锁定问题。概括这样的表(不仅仅是时间戳)是一个坏主意。从数据中获取数据也是一场噩梦。
如果您的代码在添加您不希望用户看到的字段的GUI级别中断,则您错误地将代码写入GUI,该GUI应仅指定您需要的最小列数,而不选择*。
答案 7 :(得分:0)
我认为你必须执行的额外加入才能获得时间戳,这将是一个轻微的性能打击和痛苦的脖子。除此之外我没有看到任何问题。
答案 8 :(得分:0)
我们完成了您的所作所为。它非常适合对象模型以及使用最少的代码向我们的模型添加新邮票和不同类型的邮票的能力。我们还跟踪进行更改的用户,我们的很多逻辑都是基于这些邮票。它非常好。
一个缺点是报告和/或在屏幕上显示许多不同的标记。如果按照我们这样做的方式进行,它会引起很多连接。此外,后面的结局变化很痛苦。
答案 9 :(得分:0)
我们的解决方案是在“会话”表之外维护“交易”表。 UPDATE,INSERT和DELETE指令都通过“Transaction”对象进行管理,并且一旦在数据库上成功执行了这些SQL指令,它们就会存储在“Transaction”表中。这个“Transaction”表有其他字段,例如transactiontType(I代表INSERT,D代表DELETE,U代表UPDATE),transactionDateTime等,以及外键“sessionId”,最后告诉我们谁发送了指令。通过一些代码甚至可以确定谁做了什么以及何时做(Gus在星期一创建了记录,Tim在星期二改变了单价,Liz在星期四增加了额外的折扣等)。
此解决方案的优点是:
缺点
我们的选择:所有超过90天的记录都会每天早上自动删除
答案 10 :(得分:-1)
菲力普
不要简单地删除超过90天的那些,将它们先移动到单独的数据库或将它们写入文本文件,做一些事情来保存它们,只需将它们移出主生产数据库。
如果归结为它,通常情况下是“他赢得的文件最多的人”!