日志表中的外键

时间:2010-10-15 16:34:10

标签: sql sql-server-2008 database-design

嘿,这里有一点设计问题:

我正在开展一个项目,要求我跟踪用户执行插入,更新或删除的时间。我要决定的是,是否让Logs表引用受影响行的主键或其他标识符。为了在使用外键时记录删除,我需要为每个表都有一个Deleted列。但是,如果我使用一些没有外键的命名标识符,我最终会在Logs表中遇到名称重复,并且它将变得不清楚条目引用的内容。有没有人对此有任何实际经验,特别是使用Deleted列来维护完整日志对性能的影响?

相关问题还在于Logs表本身的设计。如果我在单个日志表中使用外键引用,我的第一个本能就是为每个正在监视更改的表创建一个引用列。这对我来说并不理想,好像我必须在我必须更改Logs表和任何关联的sprocs的行上添加一个新表。我能看到的另一个选择是有两列,TableName和RowId。但是这将没有固有的外键引用,因为不知道引用了什么表。

感谢您的任何意见!

5 个答案:

答案 0 :(得分:2)

首先,根据行数和您拥有的索引类型,添加已删除的列可能会比删除行更好。

其次,我认为最好的选择是将整行存储在日志表中(这将允许记录任何更新)。您也可以使用更规范化的日志表来执行此操作 - 如下所示:

  • ID
  • 动作
  • EffectedColumn
  • 的OldValue
  • 的NewValue
但是,那将会有更多的工作,你不会从中得到那么多。我建议您只需将所有数据存储在一个重复的表中 - 使用其他列来显示所采取的操作类型。

对于行的标识符或其他值的引用,如果你这样做会有同样的问题。为了识别哪一行受到影响,该行中也必须存在相同的唯一值。删除该行后,那么该唯一值(GUID(t-sql中的uniqueidentifier) - 顺便说一句是一个选项)。

答案 1 :(得分:1)

RE:一个日志表而不是影子表。

  但是,那将会有更多的工作,你不会从中得到那么多。 - 加布里埃尔麦克亚当斯

我不能不同意。

您的目标是:

  

跟踪用户何时执行插入,更新或删除。

如果每个表都有影子表,您如何回答这些问题:

  1. 哪些用户昨天做了更改?
  2. 哪个表的更新次数最多?
  3. 上个月员工=“Page,S”做了什么活动?
  4. 哪个员工最活跃?
  5. 你必须通过每个单一的影子表来解决这个问题。如果你开始记录一个新表,你将不得不记得更改那些sprocs以同时命中新的影子表。

    如果您有其他人建议的一个日志表,您可以使用简单的SQL轻松回答所有这些问题...更难的是UN-DELETE或UN-UPDATE。但是你没有为它构建它,你正在构建它以跟踪用户编辑,我列出的查询或类似的查询将是你的面包和黄油。

    关于逻辑删除。

    如果该表是您无法删除的其他表的父项,则它们非常有用。像员工和工资单一样。您无法删除必须由法律保留的工资记录的员工。如果这就是你在逻辑上删除的原因,那很好。但是,如果你在逻辑上删除,以便将PK保存在日志表中的FK,那么我认为你正在购买一个伤害不大的世界。

    如果您有任何代码直接命中这些表,您现在必须将它们全部更改为包含DELETED ='F'谓词。如果从头开始,您可以为每个逻辑删除的表创建一个视图,该表嵌入DELETED ='F'谓词,并且永远不会在表本身上授予select。

答案 2 :(得分:0)

你不想在Log表中只需要四列吗? UserIdTableNameIdAction

Action将是“删除”,“更新”或“插入”,Id将成为相关表格的主键,其余的则是自我解释。

这样您就不必拥有大量的列和外键,这只会使Log表的插入速度变慢。无论如何,您将需要使用触发器执行此操作,因此将表名添加到Log表不会有问题。

答案 3 :(得分:0)

假设您跟踪的每个表的日志信息是相同的,我会使用一个包含TableID和RowPKValue列的日志表。确实,你不能将RowPKValue FK返回到每个单独的表,但由于用户(或除了你的Log SP之外的任何代码)永远不会触及日志表,我认为它是相当安全的。

我肯定会使用行记录的PK值而不是任何其他值(即使你没有FK引用它),因为这是PK值的用途。

至于DELETE问题,我认为这取决于1)您期望执行的DELETE数量以及2)您必须在应用程序中添加“Undelete”功能的可能性。

如果您的DELETE数量相对较少,您可以将它们保留在带有已删除标志的表中,然后在某个指定的时间段内将其删除。如果你这样做,我推荐一个方案,其中基本表被称为CustomerAll,你有一个表视图CREATE VIEW Customer AS SELECT * FROM CustomerAll WHERE Deleted = false供前端程序员使用。

如果你有大量的DELETE,我会把它们在DELETE上移到第二个表(CustomersDeleted)或甚至是数据库之外,这取决于你认为它有多大可能需要再次查找它们

答案 4 :(得分:0)

您也可以使用2表日志文件设置。

LogA - Log_id,Tablename,action,date_time

LogB - Log_ID,table_id,columnchanged,old_value,new_value

(编辑为calrify,上面的'table_id'指的是该表中的主键。多个PK可能需要多个字段)

在new_value字段中使用空值表示删除,在旧值中使用null表示插入。

如果您希望避免每个表上的“已删除”列,您可以创建一个删除的表来存储哪些行被删除,10个使用视图来仅显示活动记录(IE记录没有其unqiue键在已删除的表格内。)

有很多有效的设计方法......