【问题标题】:How to update sql server timestamp column without changing record data如何在不更改记录数据的情况下更新 sql server 时间戳列
【发布时间】:2010-01-29 21:37:19
【问题描述】:

我有一个带有时间戳列的 sql server 表。有没有办法在不实际更新记录的情况下强制更改时间戳列?

我问的原因是因为我希望在子表中插入/更新/删除记录时更改记录的时间戳。

时间戳可能不是执行此操作的最佳方式。如果没有,我该怎么办?我不想使用 datetime,因为我觉得这不是一个好的版本控制方式。我不想使用整数,因为我不想读取值来增加它。

建议?

【问题讨论】:

  • DateTime 通常用于 LastModified 列..这是不同的东西还是你能澄清为什么你不想使用 DateTime?
  • 我阅读了一篇文章,介绍了为什么不应将 DateTime 数据类型用于版本控制和乐观并发。我不记得每一个细节,但在当时是有道理的。我相信这是因为数据类型的精确性,理论上更新可能发生在完全相同的实例上,从而导致竞争条件。
  • 仅当数据库未提供日期/时间时 - SQL Server DATETIME 为几分之一秒:msdn.microsoft.com/en-us/library/ms187819.aspx
  • @OMGPonies:SQL Server 的 DATETIME 精确到 3.33 毫秒 - 您可能有多个具有相同时间戳的条目,并且无法将它们分开。这就是 TIMESTAMP 更好的地方,因为它保证是唯一的并且永远不会有两个相同的值

标签: c# .net sql sql-server


【解决方案1】:

我不想使用整数,因为我不想读取值来增加它。

UPDATE Table SET
    IntColumn = IntColumn + 1

虽然这在技术上确实需要阅读,但我认为它没有任何问题。

您总是可以更新为相同的值:

UPDATE Table SET
    SomeColumn = SomeColumn

这也会触发 rowversion 更新。

补充:您可以使用孩子的最大行版本进行查看:

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) 列(返回数据库版本号),以及将二进制列更新为新的 @@DBTS 的 AFTER UPDATE 触发器将给出你是一个伪行版本类型,然后你可以手动更新。不过,这并不比仅将其他列更新为相同的值更快或更好。

【讨论】:

  • 你能举出你最后一点的例子吗?听起来很有希望。
  • 如果您使用此视图来查看自上次读取以来是否发生了变化,则包含子行版本的 VIEW 不包括删除子行。其实删除children很可能会导致views版本变小
【解决方案2】:

罗尼,

首先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:你错了,而 Paulo 是对的。 TIMESTAMP 已被弃用,因为它的名称引起了很多混乱(人们认为有办法将其转换回日期和时间)。 ROWVERSION 是一种新的方式
【解决方案3】:

信不信由你,这会在不设置列的情况下更新 RowVersion/Timestamp 列。注意@foo=@fooset 之后的唯一子句。我正在使用 Sql Server 2017。

declare @foo nvarchar(50);

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

【讨论】:

    【解决方案4】:

    您是否考虑过使用triggers?它似乎更适合您的追求。

    我曾经开发过一个具有全面时间戳的应用程序;我们应该对它们做什么被证明有点难以捉摸,所以我们将它们从模式中审计出来。您可以根据数据将时间戳读入您创建的对象中,并检查当您看到数据脏时更新和回滚时它是否已更改,但这是很多样板文件,没有太多收益(在至少在我工作的领域中)。

    【讨论】:

    • 其实我有。但是我仍然需要在触发器中做一些事情来更新时间戳列。那是我的问题。
    【解决方案5】:

    在出现更好的主意之前,我要做的是将 DateTime 列添加到名为 DateChildrenChanged 的​​父表中。

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

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

    我仍然对想法持开放态度。

    【讨论】:

    • 我认为这是最好的方法。保留您的 TIMESTAMP/ROWVERSION 以对实际行进行版本控制。如果您需要对子行进行版本控制 - 也有一个 TIMESTAMP/ROWVERSION 列
    猜你喜欢
    • 2011-11-29
    • 1970-01-01
    • 2010-09-17
    • 2015-06-22
    • 2011-06-03
    • 2018-07-24
    • 2018-03-24
    • 2019-06-04
    • 1970-01-01
    相关资源
    最近更新 更多