在Parent-Child表中记录sql数据

时间:2012-06-28 20:34:21

标签: sql sql-server database-design foreign-key-relationship

1张地图有N张图片。当用户更新地图数据时,我不进行更新,因为我丢失了旧数据。因此,我使用相同的自动递增Id执行Insert到Map表,但是使用新的日期时间戳。这样我想在用户界面中对Map上的所有更改进行历史记录。

表格地图:PK是Id + CreatedAt

 [Id] [int] IDENTITY(1,1) NOT NULL, 
 [CreatedAt] [datetime] NOT NULL,   
 ...

表格图片:PK为Id,FK为MapId + CreatedAt

[Id] [int] IDENTITY(1,1) NOT NULL,
[Image] [varbinary](max) NOT NULL,
[VisibleIndex] [int] NOT NULL,
[CreatedAt] [datetime] NOT NULL,
[MapId] [int] NOT NULL,

如果我有一个带2个字段的PK,我的FK也必须有2个字段。

但是这在我的情况下不起作用,因为表格中的CreatedAt日期不能是表格Map中的createdAt日期。

您将如何进行历史“更新”?

4 个答案:

答案 0 :(得分:1)

如果Map表上的主键是单列Id,那么Images表中的外键实际上就是列MapId ,它引用父表上的Id列。

如果Map表上的主键是单列Id,那么您将无法插入具有与{{{1}相同值的另一行1}}。

可以为IDENTITY列插入具有指定值的行,但您必须执行Id语句,然后必须记住执行相应的SET IDENTITY_INSERT tablename ON语句。


要获得Map表内容的历史记录,我会考虑创建一个单独的“MapHistory”表来存储更改历史记录,并使用触发器来维护它。

SET IDENTITY_INSERT tablename OFF

要存储CREATE TABLE MapHistory ( [change_date] datetime NOT NULL, [Id] int NOT NULL, [CreatedAt] datetime NOT NULL, ... 中的行在更新之前的“历史记录”,您可以在触发器中引用特殊的逻辑Map表。 (为了完整性,当从Map表中删除一行时,我也可能在历史表中存储一行。)

deleted

使用这种方法,您的代码可以更简单。您需要做的就是在CREATE TRIGGER map_update ON Map AFTER UPDATE,DELETE AS BEGIN SET NOCOUNT ON -- store column values as they existed prior to the update or delete INSERT MapHistory (change_date, Id, CreatedAt, ... ) SELECT CURRDATE(), Id, CreatedAt, ... FROM deleted END 表上执行UPDATE,数据库将负责维护MapHistory表。

这种方法的一个小缺点是现在“当前”值在一个表中,而以前值的历史记录在另一个表中。

如果您希望当前值也存储在Map表中,您可以修改触发器以取消MapHistory,而是引用特殊逻辑AFTER INSERT, UPDATE表,所以您正在将已插入的更新行的“副本”创建到历史记录表中。

也可以在历史表中存储“旧”和“新”行,但实际上您将存储冗余数据。在这种情况下,您可能希望包含一个列,指示该行是来自“已删除”表还是“已插入”表。

inserted

附录:

此方法允许您将CREATE TRIGGER map_update ON Map AFTER INSERT,UPDATE,DELETE AS BEGIN SET NOCOUNT ON -- store column values as they existed prior to an update or delete INSERT MapHistory (source, change_date, Id, CreatedAt, ... ) SELECT 'D', CURRDATE(), Id, CreatedAt, ... FROM deleted -- store column values as they exist after an update or insert INSERT MapHistory (source, change_date, Id, CreatedAt, ... ) SELECT 'I' CURRDATE(), Id, CreatedAt, ... FROM inserted END IDENTITY作为Id表中的简单主键。我认为我在Map表格中添加的change_date列可能是多余的,它可能符合您对MapHistory列的预期目的。

答案 1 :(得分:0)

我建议创建另一个表格历史记录,其中保留所有最后的值,包括实际值,因此您可以使用密钥使表格映射。     [Id] [int] IDENTITY(1,1) NOT NULL 您可以使用触发器或过程执行此操作,使用alter datetime将当前记录插入历史记录表,并在正常的Map表中更新记录。

答案 2 :(得分:0)

将地图的“常量”和“可演化”方面分为两个表:

enter image description here

这假设您只想对地图字段进行版本设置,而不是图像。


如果您还想对图片进行版本设置,可以这样做:

enter image description here

创建新地图版本时:

  • 对于未更改的图像:只需复制链接但不创建新的图像版本。
  • 对于确实发生变化的图片:创建新的图片版本链接。

这样,您就不必仅仅因为地图已经改变而进行昂贵的图像内容复制。您只能在图像本身发生变化时创建新的图像版本。

注意:这个模型有点过于笼统。它允许在多个地图之间共享相同的图像(而不仅仅是同一地图的多个版本)。如果你想限制它,请告诉我。


要使图像“隐私”到地图,您可以执行以下操作:

enter image description here

请注意我们引入任何代理键。相反,我们让识别关系产生“自然”键,然后将其合并到菱形依赖项的底部:ImageLink.MapId具有朝向“钻石”两个“边缘”的外键

答案 3 :(得分:0)

当您想要保留旧的父子关系时,另一种方法是将旧数据插入新行,然后将旧行更新为新数据。如果您有唯一索引或其他约束可能会影响两个记录,除了交易持续时间的代理键之外,可能需要三个步骤。