2010-04-12 3 views
5

У меня есть ситуация, когда два человека могут работать в одном и том же порядке (хранятся в базе данных MS SQL) с двух разных компьютеров. Чтобы предотвратить потерю данных в случае, когда сначала нужно сохранить свою копию заказа, а затем немного позже, второй сохранит его копию и перезапишет первое, я добавил чек против поля lastSaved (дата и время) до экономии.Проблема точности datetime 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; 
    } 
} 

Это работает большую часть времени, но я нашел одну маленькую ошибку.

Если orderIsChangedByOtherUser возвращает ложной, локальная копия будет иметь свой LastSaved обновлен до текущего времени и затем сохраняется в базе данных. Значение lastSaved в локальной копии и БД должно быть таким же. Однако, если orderIsChangedByOtherUser запускается снова, он иногда возвращает true, хотя ни один другой пользователь не внес изменений в БД.

При отладке в Visual Studio, databaseOrder.LastSaved и localOrderCopy.LastSaved по всей видимости, имеют то же значение, но при взгляде ближе они несколько раз отличаются от нескольких миллисекунд.

Я нашел this article с коротким уведомлением о точностью до миллисекунды для DateTime в SQL:

Другая проблема заключается в том, что SQL Server хранит DATETIME с точностью 3,33 миллисекунды (0. 00333 секунд).

Решение, о котором я мог думать по этой проблеме, состоит в том, чтобы сравнить два времени и считать их равными, если они отличаются менее чем на 10 миллисекунд.

Мой вопрос к вам: есть ли лучшие/более безопасные способы сравнить два значения datetime в MS SQL, чтобы узнать, являются ли они точно тем же?

+0

Просто для указания: у меня нет возможности изменить тип поля * lastSaved *, поэтому мне придется придерживаться даты и времени. – Nailuj

ответ

4

Я знаю, что вы сказали, что вы не можете изменить тип, но если это только для поддержания совместимости & вашей помощью 2008 можно изменить lastSaved поле для DATETIME2 (который полностью совместим с DATETIME) и использовать SYSDATETIME() оба из которых имеют гораздо большую точность.

+0

Я не знал о datetime2. На моих тестовых серверах, по крайней мере, работает SQL Server 2008, поэтому это может быть вариант. Просто нужно убедиться, что все, кто использует систему, также находятся на 2008 ... Спасибо :) – Nailuj

0

Вы можете использовать поле метки времени для проверки последней даты редактирования, а не поля даты-времени? (В SQL 2008 это теперь RowVersion).

+0

Возможно, это возможно, но у меня нет возможности изменить тип поля lastSaved. – Nailuj

+0

Вам не обязательно. Добавьте к нему дополнительное поле. Затем измените UPDATE SP, чтобы проверить как PK, так и поле версии строки. Вы получите 0 строк, если изменилась версия строки. –

0

. Вы должны убедиться, что ваши временные уточнения выстраиваются в линию - это в основном возможно, если у вас есть правильная логика на стороне C#, чтобы фактически уменьшить точность под тем, что является родным в DateTime объект - в основном сделать suer у вас есть, например, временные метки всегда в секундах, а не ниже, по всем слоям.

Если вы сделаете это правильно, отметка времени по всем слоям будет сразу сопоставима.

2

Вы можете добавить целочисленное поле ревизии в таблицу заказов. Каждый раз, когда пользователь сохраняет заказ, вы увеличиваете ревизию на единицу. Тогда его легко проверить, изменил ли кто-либо заказ или пользователь, который хочет сохранить заказ, находится в последней редакции.

1

В то время как вы находитесь в пределах SQL 2005 и до того, как проблема точности всегда будет там, никогда не более точной, чем 1/300 секунды или 3.33 мс.

Независимо от недостатка точности вы программируете ошибочное состояние гонки, когда оба пользователя могут писать в базу данных быстро, но оба они считаются успешными. Отсутствие точности увеличивает шансы на это, пока проверка и последующие записи происходят в течение тех же 3-4 мс.

Любая попытка проверки, сопровождаемая записью, страдает этой проблемой, и вы либо должны принять последствия оптимистической блокировки, изменить блокировку на пессимистическую или реализовать некоторую стратегию типа семафора для правильной обработки блокировки.

+0

Я знаю о потенциально ошибочной логике, но, как и во многих старых системах, с ней просто нужно жить. У меня была небольшая надежда, что вокруг может быть какой-то путь, но я думаю, что мы сможем жить с точностью 0,33 мс. – Nailuj

0

Существует еще один способ сделать это потенциально.

Вместо передачи в «Последняя сохраненная» с локального компьютера, измените хранимую процедуру UPDATE. Assign LastSaved = getdate() Затем верните значение LastSaved (и любые другие результаты, такие как идентификаторы), и обновите время LastSaved на клиенте с этим результатом.

Для этого есть два очевидных преимущества. Во-первых, ваша точность DateTime сохраняется. Другим является то, что дата и время теперь совместимы с сервером, а не страдают от каких-либо задержек в сети и местных проблем дрейфа часов.

Обычно мы используем автоматически сгенерированные SP для операций CRUD, и в таблицах обычно используются пары «Создано/LastUpdated» и «CreatedBy/LastUpdatedBy», где даты установлены на сервере, а значения «By» передаются, и если для NULL установлено значение System_User

Смежные вопросы