MS SQL日期时间精度问题

时间:2010-04-12 08:10:27

标签: sql sql-server datetime precision

我遇到两个人可能从两台不同的计算机上处​​理相同订单(存储在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中的两个日期时间值,看看它们完全是否相同?

7 个答案:

答案 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>