我遇到两个人可能从两台不同的计算机上处理相同订单(存储在MS SQL数据库中)的情况。为了防止数据丢失,如果人们首先保存他的订单副本,然后稍后第二个将保存他的副本并覆盖第一个,我已经添加了对 lastSaved 的检查保存前的字段(日期时间)。
代码看起来大致如下:
private bool orderIsChangedByOtherUser(Order localOrderCopy)
{
// Look up fresh version of the order from the DB
Order databaseOrder = orderService.GetByOrderId(localOrderCopy.Id);
if (databaseOrder != null &&
databaseOrder.LastSaved > localOrderCopy.LastSaved)
{
return true;
}
else
{
return false;
}
}
大部分时间都可以使用,但我发现了一个小bug。
如果 orderIsChangedByOtherUser 返回 false ,则本地副本将其 lastSaved 更新为当前时间,然后保留到数据库。本地副本和数据库中 lastSaved 的值现在应该相同。但是,如果再次运行 orderIsChangedByOtherUser ,它有时会返回 true ,即使没有其他用户对数据库进行了更改。
在Visual Studio中进行调试时, databaseOrder.LastSaved 和 localOrderCopy.LastSaved 似乎具有相同的值,但是当仔细观察时,它们会相差几毫秒。
我发现this article时间很短,通知SQL中datetime的毫秒精度:
另一个问题是SQL Server 以精确度存储DATETIME 3.33毫秒(0. 00333秒)。
我能想到的解决这个问题的方法是比较两个日期时间,如果它们的差异小于10毫秒,则认为它们相等。
我的问题是:有没有更好/更安全的方法来比较MS SQL中的两个日期时间值,看看它们完全是否相同?
答案 0 :(得分:6)
我知道你说你不能改变类型,但如果这只是为了保持兼容性而且您使用2008后,您可以将lastSaved
字段更改为DATETIME2
(与DATETIME
完全兼容)并使用SYSDATETIME()
这两者都具有更高的精确度。
答案 1 :(得分:2)
您可以在订单表中添加整数修订字段。每次用户保存订单时,您都会将修订版本增加一个。然后很容易检查是否有人改变了订单,或者是否想要保存订单的用户是最新版本。
答案 2 :(得分:1)
虽然您在SQL 2005中,并且在准确性问题始终存在之前,但从未超过1/300秒或3.33毫秒。
无论是否缺乏准确性,您都在编写一个有缺陷的竞争条件,即两个用户仍然可以快速进入数据库,但两者都被认为是成功的。只要检查和后续写入在相同的3-4毫秒内发生,缺乏准确性就会增加发生的可能性。
任何尝试检查然后写入都会遇到此问题,您必须接受乐观锁定的后果,将锁定更改为pessemistic或实现某种形式的信号量类型策略以正确处理锁定。
答案 3 :(得分:0)
您可以使用时间戳字段来检查上次编辑日期而不是日期时间字段吗? (在SQL 2008中,这现在是RowVersion)
答案 4 :(得分:0)
你必须确保你的时间精度排成一行 - 这主要是通过在C#端使用适当的逻辑来实际降低DateTime对象中原生的精度来实现的 - 基本上你可以使用例如时间戳在所有层中总是以秒为单位,而不是更低。
如果您这样做,所有图层的时间戳将立即可比较。
答案 5 :(得分:0)
还有另一种方法可以做到这一点。
不是从本地计算机传入“Last Saved”,而是修改UPDATE存储过程。 分配LastSaved = getdate() 然后返回LastSaved的值(以及任何其他结果,例如ID),并使用该结果更新客户端的LastSaved时间。
这有两个明显的优点。 一个是你的DateTime准确性得以保留。 另一个是日期和时间现在与服务器一致,而不是遭受任何网络延迟和本地时钟漂移问题。
我们通常使用自动生成的SP进行CRUD操作,并且表通常运行“Created / LastUpdated”和“CreatedBy / LastUpdatedBy”对,其中在服务器上设置日期,并传入“By”值,并且如果NULL设置为System_User
答案 6 :(得分:0)
检查与SQL 2014的兼容版本-SQL 2016中的更改将Entity Framework中的DateTime转换为DateTime2(7)。
请参见MSN。
尝试使用此SQL在YourTable和YourDateTime列上进行测试:
select top 100 <YourDateTimeColumn> date1,
cast(<YourDateTimeColumn> as datetime2(3)) date2,
cast(<YourDateTimeColumn> as datetime2(7)) date3,
case when <YourDateTimeColumn> <> cast(<YourDateTimeColumn> as datetime2(3))
then 'unequal' else 'equal' end as datesareequal
from <YourTableName>