【发布时间】:2020-03-26 00:46:46
【问题描述】:
我有一个带有整数 Status 列的 SQL Server 表。我的代码更新记录的Status,然后在几毫秒后再次将其更新为不同的值。
此表有一个触发器,每次Status 值更改时,它都会将一条记录插入到单独的History 表中。
History 表包含以下列:
-
Id uniqueidentifier:更新记录的PK值(注意:不是外键——我们不需要参照完整性) -
Status int:更新记录的新状态值 -
TimeUtc DateTime2(7):更新发生的时间
一个 NLog 日志文件显示我们在关闭时进行了两个独立的数据库 UPDATE 调用,但时间不同。但是当我们之后查看数据库时,两条历史记录的TimeUtc值与微秒相同:
Id: CD83... Status: 4 TimeUtc: 2020-3-20 16:14:26.6952631
Id: CD83... Status: 5 TimeUtc: 2020-3-20 16:14:26.6952631
为什么会这样?更奇怪的是,有时,当我们运行并发代码时,两条独立的记录会几乎同时更新,并且所有四条历史记录都将具有相同的时间戳。
Id: CD83... Status: 4 TimeUtc: 2020-3-20 16:14:26.6952631
Id: CD83... Status: 5 TimeUtc: 2020-3-20 16:14:26.6952631
Id: 06EA... Status: 4 TimeUtc: 2020-3-20 16:14:26.6952631
Id: 06EA... Status: 5 TimeUtc: 2020-3-20 16:14:26.6952631
我们正在使用 ServiceStack OrmLite 来更新值,文档告诉我每个调用都是一个单独的事务,(我认为)会产生两个单独的触发事件,但也许这是错误的。看起来 SQL Server 正在保存触发事件并立即触发它们。 ServiceStack OrmLite 能否在后台将调用批处理到单个事务中?
这里是触发器定义。如果需要更多详细信息,请告诉我:
CREATE TRIGGER [dbo].[ChangeStatus]
ON [dbo].[TableWithStatus]
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
-- On insert, or on update where status changed
INSERT INTO History
SELECT i.[Id], i.[Status], SYSUTCDATETIME()
FROM Inserted i
LEFT JOIN Deleted d ON i.Id = d.Id
WHERE i.[Status] IS NOT NULL
AND (d.[Status] IS NULL OR i.[Status] <> d.[Status])
END
GO
附录: 我用第四个字段 (varchar) 更新了 History 表,并更新了 Trigger 以将 @@SPID 和 CURRENT_TRANSACTION_ID() 写入该字段,用冒号分隔。这是我得到的:
每次更新都是在同一个会话和一个单独的事务中执行的,这两者都是我所期望的,但它仍然不能解释为什么记录共享相同的时间戳。
【问题讨论】:
-
嗯,documentation 声明精度取决于底层 Windows API 并且是有限的。也许事情发生得太快了,下一个时间戳与上一个时间戳不同。
-
也许你也应该edit这个问题并解释由于重复的时间戳你面临的问题。也许有解决方法。
-
您能否尝试将@@spid 存储到历史记录表中,看看这两个调用是否是两个单独的会话(而不是一个单独的会话)?
-
我在我们的密钥管理系统中遇到了类似的跟踪历史记录问题。唯一可行的方法是将时间戳与整数
IDENTITY列配对。该组合将判断哪条记录真正是第一条记录。 -
请参阅此处的答案,了解使用 SEQUENCE 替换亚毫秒精度的最后几个(无意义的)数字的选项。 stackoverflow.com/questions/51459805/…
标签: c# sql-server database-trigger ormlite-servicestack