2010-04-12 133 views
5

我有两个人可能在两台不同的计算机上以相同的顺序工作(存储在MS SQL数据库中)的情况。为防止数据丢失的情况下,人们会先保存他的订单副本,然后稍后第二次将保存他的副本并覆盖第一个,我已经添加了一个检查lastSaved字段(日期时间)之前保存。MS SQL日期时间精度问题

代码看起来大致是这样的:

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返回,本地副本将拥有lastSaved更新为当前时间,然后被持久化到数据库中。 lastSaved的值在本地副本和数据库现在应该是相同的。但是,如果orderIsChangedByOtherUser再次运行,它有时返回true即使没有其他用户已经更改数据库。

当在Visual Studio调试,databaseOrder.LastSavedlocalOrderCopy.LastSaved似乎有相同的值,但仔细一看时,他们有时用几毫秒不同。

我发现this article与在SQL毫秒精度日期时间的简短声明:

另一个问题是,SQL服务器 店DATETIME与 3.33毫秒(000333秒)精度。

对于这个问题,我能想到的解决方案是比较两个日期时间,并认为它们相等,如果它们的差异小于10毫秒。

我给你的问题是:是否有更好/更安全的方法来比较MS SQL中的两个日期时间值,看它们是否与正好一样?

+0

只需指定:我没有选择更改* lastSaved *字段的类型,因此我必须坚持使用日期时间。 – Nailuj 2010-04-12 08:17:20

回答

4

我知道你说你不能改变的类型,但如果这是唯一的兼容性&您使用2008年,保持你可以在lastSaved字段更改为DATETIME2(这是与DATETIME完全兼容),并使用SYSDATETIME()这两个精度要高得多。

+0

我不知道datetime2。我的测试服务器至少运行SQL Server 2008,因此可能实际上是一个选项。只需要确保所有使用该系统的人都在2008年......谢谢:) – Nailuj 2010-04-12 10:40:38

0

您可以使用时间戳字段来检查最后编辑日期而不是日期时间字段? (在SQL 2008中现在是RowVersion)

+0

可能是一种可能性,但我没有选择更改lastSaved字段的类型。 – Nailuj 2010-04-12 08:16:34

+0

你不需要。为它添加一个额外的字段。然后修改UPDATE SP以检查PK和行版本字段。如果行版本已更改,则会得到0行受影响。 – 2013-07-25 22:43:54

0

你必须确保你的时间精度是一致的 - 这主要是通过在C#端有正确的逻辑来实际降低在DateTime中原生的精度对象 - 基本上让你有例如时间戳总是在秒内,而不是在所有层上都低。

如果您正确执行此操作,则所有图层的时间戳将立即可比。

2

您可以将整数修订字段添加到您的订单表中。每次用户保存订单时,都会将修订版本增加1。然后很容易检查是否有人更改了订单或想要保存订单的用户是否在最新版本。

1

虽然你在SQL 2005内,并且准确性问题总是在那里,但永远不会比1/300秒或3.33ms更准确。

无论缺少准确性,您都可以编写一个有缺陷的竞争条件,即两个用户仍然可以快速成功写入数据库,但都被认为是成功的。只要检查和随后的写入发生在相同的3-4毫秒内,缺乏准确性就会增加发生机会的可能性。

任何试图在写入之后进行检查都会遇到此问题,并且您必须接受乐观锁定的后果,将锁定更改为pessemistic或实施某种形式的信号类型策略才能正确处理锁定。

+0

我知道可能存在缺陷的逻辑,但与许多旧系统一样,它只是一个人必须忍受的事情。我希望有一个小小的希望,但我认为我们可能能够以0.33ms的精度生活。 – Nailuj 2010-04-12 10:39:43

0

还有另一种方法可以做到这一点。

而不是从本地机器传入“最后保存”,修改UPDATE存储过程。 指定LastSaved = getdate() 然后返回LastSaved的值(以及任何其他结果,如ID),并使用该结果更新客户端的LastSaved时间。

这有两个明显的优点。 一个是你的DateTime准确性被保留。另一个原因是日期和时间现在与服务器保持一致,而不受任何网络延迟和本地时钟漂移问题的影响。

我们通常使用自动生成的SP进行CRUD操作,并且这些表通常在服务器上设置日期并传入“By”值时运行“Created/LastUpdated”和“CreatedBy/LastUpdatedBy”如果NULL设置为System_User