如何在不更改记录数据的情况下更新SQL Server时间戳列

11
我有一个带有时间戳列的SQL Server表格。是否可以在不实际更新记录的情况下强制更改时间戳列?
我之所以提出这个问题,是因为当子表中插入/更新/删除记录时,我希望记录的时间戳也随之变化。
时间戳可能不是最好的解决方案。如果不行,我该怎么办呢?我不想使用datetime,因为我认为那不是一种好的版本控制方式。我也不想使用整数,因为我不想读取值来递增它。
有什么建议吗?

DateTime通常用于LastModified列...这是有什么不同,还是您可以澄清为什么不想使用DateTime? - dan
1
我读了一篇关于为什么不应该使用DateTime数据类型进行版本控制和乐观并发的文章。我记不清每一个细节,但当时它是有道理的。我相信这是因为数据类型的精度,更新可能在完全相同的时间发生,从而导致竞争条件。 - Ronnie Overby
仅当数据库未提供日期/时间时 - SQL Server DATETIMEs 到秒的小数部分:http://msdn.microsoft.com/en-us/library/ms187819.aspx - OMG Ponies
2
@OMGPonies:SQL Server的DATETIME精确到3.33毫秒-您可能会有多个具有相同时间戳的条目,无法将它们区分开来。这就是TIMESTAMP更好的地方,因为它保证是唯一的,永远不会有两个相同的值。 - marc_s
5个回答

10
我不想使用整数,因为我不想读取该值来递增它。
UPDATE Table SET
    IntColumn = IntColumn + 1

虽然技术上需要读取,但我认为这没有任何问题。

你可以直接更新为相同的值:

UPDATE Table SET
    SomeColumn = SomeColumn

这将触发行版本更新。

补充:您可以创建一个视图,其中包含子项的最大行版本:

SELECT Parent.*, MaxChildRowVersion as ChildVersion
FROM Parent
JOIN (
    SELECT ParentId, MAX(RowVersion) as MaxChildRowVersion
    FROM Child
    GROUP BY ParentId
) as Child ON
     Parent.ParentId = Child.ParentId

但是,不,你不能直接更新一个rowversion列(尽管你可以使用@@DBTS、binary(8)和INSTEAD OF触发器实现自己的可更新版本...)

你能举个例子来说明你上次提到的那一点吗?听起来很有前途。

不,真的没有。;) 当你可以只更新到相同的值或使用视图时,这太麻烦了。这是两个最简单的选择。

但是,为了完整起见,一个默认为@@DBTS(返回数据库版本号)的binary(8)列,以及一个在UPDATE之后也将二进制列更新为新的@@DBTS的触发器,将为您提供一个伪-rowversion类型,您可以手动更新它。但与仅将其他列更新为其相同值相比,它不会更快或更好。


你可以举个例子说明一下你最后的观点吗?听起来很有前途。 - Ronnie Overby
如果您使用此视图来查看自上次读取以来是否有更改,则包含子行版本的VIEW不包括删除子行。实际上,如果删除了子项,则可能会导致视图版本变小。 - Adam Tegen

4

Ronnie,

首先,微软已经将 timestamp 语法停用,所以您需要使用 rowversion。 其次,rowversion 总是与行相关联。

根据文档:

rowversion

每个数据库都有一个计数器,该计数器会为在数据库中包含 rowversion 列的表上执行的每个插入或更新操作递增。 此计数器是数据库的 rowversion

但是,由于它在 SQL Server 中的实现方式,您需要使用触发器来实现您想要的功能。

类似于这样的代码:

CREATE TRIGGER tgUpdateParentRowVersion ON ChildTable FOR INSERT, DELETE, UPDATE
AS
BEGIN
   /*
    * The updates below force the update of the parent table rowversion
    */
   UPDATE ParentTable
      SET ChildUpdate = ChildUpdate + 1
     FROM ParentTable a 
     JOIN inserted    i on a.pkParentTable = i.fkParentTable

   UPDATE ParentTable
      SET ChildUpdate = ChildUpdate - 1
     FROM ParentTable a 
     JOIN deleted     d on a.pkParentTable = d.fkParentTable
END

关于弃用,我认为情况是相反的:rowversion 被弃用了。但我可能错了... - Ronnie Overby
1
@Ronnie:你错了,Paulo是对的。TIMESTAMP已经被弃用,因为它的名称引起了很多混淆(人们认为有一种方法可以将其转换回日期和时间)。ROWVERSION是新的解决方案。 - marc_s

2

信不信由你,这个更新操作会更新一个RowVersion/Timestamp列但是不设置列值。注意set之后唯一的语句是@foo=@foo。我正在使用Sql Server 2017。

declare @foo nvarchar(50);

update dbo.MyTable
set @foo = @foo
where Id = @id

1

你有考虑过使用触发器吗?这似乎更符合你的需求。

我曾经在一个应用程序上工作,其中时间戳遍布整个应用;但我们应该如何处理它们似乎有点难以捉摸,所以我们将其从模式中删除了。您可以将时间戳读入基于数据创建的对象中,并在更新时检查是否已更改,如果发现数据不正确,则回滚,但这需要大量样板代码而收益较少(至少在我工作的领域中是这样)。


我已经做了,但是我仍然需要在触发器中进行一些操作以更新时间戳列。这就是我的问题。 - Ronnie Overby

0

在更好的想法出现之前,我要做的是在我的父表中添加一个名为 DateChildrenChanged 的 DateTime 列。

我将在子表上添加一个触发器,它将把 DateChildrenChanged 列设置为当前的 DateTime,当这种情况发生时,父表上的时间戳列也会被更新。

这样,我就可以使用时间戳列进行版本控制。

我仍然非常愿意听取意见。


我认为这是最好的方法。保留您的TIMESTAMP/ROWVERSION以对实际行进行版本控制。如果您需要对子行进行版本控制-在那里也有一个TIMESTAMP/ROWVERSION列。 - marc_s

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接