Проблема точности даты и времени в MS SQL - PullRequest
6 голосов
/ 12 апреля 2010

У меня есть ситуация, когда два человека могут работать в одном и том же порядке (хранятся в базе данных 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;
    }
}

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

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

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

Я нашел этой статьи с коротким уведомлением о точности в миллисекундах для даты и времени в SQL:

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

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

Тогда у меня вопрос к вам: есть ли более эффективные / более безопасные способы сравнения двух значений даты и времени в MS SQL, чтобы определить, являются ли они точно одинаковыми?

Ответы [ 6 ]

6 голосов
/ 12 апреля 2010

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

2 голосов
/ 12 апреля 2010

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

1 голос
/ 12 апреля 2010

Пока вы находитесь в SQL 2005 и до того, как всегда возникнет проблема точности, никогда не будет точнее, чем 1/300 секунды или 3,33 мс.

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

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

0 голосов
/ 26 июля 2013

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

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

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

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

0 голосов
/ 12 апреля 2010

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

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

0 голосов
/ 12 апреля 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...