1

1 个地图有 N 个图像。当用户更新地图数据时,我不会进行更新,因为那时我会丢失旧数据。因此,我使用相同的自动递增 Id 但新的日期时间戳在 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,

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

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

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

4

4 回答 4

1

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

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

可以为 IDENTITY 列插入具有指定值的行,但是你必须做一个SET IDENTITY_INSERT tablename ON语句,然后你必须记住做一个相应的SET IDENTITY_INSERT tablename OFF语句。


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

CREATE TABLE MapHistory (
[change_date] datetime NOT NULL,
[Id]          int      NOT NULL,
[CreatedAt]   datetime NOT NULL,
...

要存储更新前行的“历史记录” Map,您可以引用deleted触发器中的特殊逻辑表。(为了完整起见,当从 Map 表中删除一行时,我还可能在历史表中存储一行。)

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

使用这种方法,您的代码可以简单得多。您需要做的就是UPDATEMap表上执行,数据库将负责维护 MapHistory 表。

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

如果您希望将当前值也存储在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

附录:

这种方法允许您将IdIDENTITY 作为Map表中的简单主键。我认为change_date我在表格中添加的列MapHistory可能是多余的,它可能符合您为该CreatedAt列指定的目的。

于 2012-06-28T20:45:49.913 回答
0

将地图的“恒定”和“可演化”方面分离到两个表中:

在此处输入图像描述

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


如果您还想对图像进行版本控制,您可以这样做:

在此处输入图像描述

创建新地图版本时:

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

这样,您不必仅仅因为地图发生了变化而对图像内容进行昂贵的复制。仅当映像本身发生更改时,您才创建新的映像版本。

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


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

在此处输入图像描述

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

于 2012-06-28T21:14:56.193 回答
0

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

于 2012-06-28T21:45:31.447 回答
0

我建议创建另一个表历史记录,在其中保留所有最后的值,包括实际值,这样你就可以只用键来映射表。 [Id] [int] IDENTITY(1,1) NOT NULL 您可以使用触发器或过程执行此操作,使用更改日期时间将当前记录插入历史表中,然后在您更新记录的普通 Map 表中插入当前记录。

于 2012-06-28T21:10:37.297 回答