Как перенести datetime на datetime2 с новым собственным клиентом SQL Server
В настоящее время мы переносим нашу базу данных из datetime
в datetime2
включая собственный клиент SQL Server v11 (SQL Server 2012). Обновление базы данных было легко сделано, но проблемы связаны с новым SQL Server Native Client 11, который мы хотим использовать.
В целом у нас есть потребители OLE DB с аксессорами "COLUMN_ENTRY*" для наших операций CRUD. Для datetime2
столбцы члены имеют тип DBTIMESTAMP
, С SQLNCLI
поставщик фракция часть DBTIMESTAMP
был молча усечен до поддерживаемого значения. С SQLNCLI11
вставка со слишком точной дробью приводит к этой ошибке:
DB_E_ERRORSOCCURRED Multiple-step OLE DB operation generated errors. Check each OLE DB status value, if available. No work was done.
По этой ссылке эта ошибка возвращается, если в поле вставлено слишком много данных. Исходя из этого, я предположил, что дробная часть члена DBDATETIME слишком точна для вставки. Согласно этой ссылке, новые версии Native Client (10 и 11) не усекаются, а выдают ошибку. Возьмите этот упрощенный пример того, чего мы хотим достичь:
class CMyTableStruct
{
public:
CMyTableStruct();
LONG m_lID;
DBTIMESTAMP m_dtLogTime;
};
class CMyTableStruct_InsertAccessor : public CMyTableStruct
{
public:
BEGIN_PARAM_MAP(CMyTableStruct_InsertAccessor)
COLUMN_ENTRY(1, m_dtLogTime)
END_PARAM_MAP()
};
В какой-то части кода я инициализирую метку времени на 2015-08-10 07:47:49.986149999, и вставка не выполняется. Если я сбрасываю дробь на 0, вставка работает; любое значение, кроме 0, терпит неудачу. Я попытался предоставить точность даты и времени, используя COLUMN_ENTRY_PS
с различными значениями, но вставки всегда не удалось.
Как мы можем заставить собственный клиент просто принять значение и усечь его? Совершенно очевидно, что мы не можем усечь все значения вручную до поддерживаемой точности БД. Я не смог найти надлежащей документации о том, как использовать datetime2 с новым Native Client. Мы пропускаем какие-либо преобразования или настройки для корректной обработки datetime2?
Это тестовая настройка:
- Windows 7 64bit
- Собственный клиент SQL Server 11 (SQLNCLI11)
- SQL Server 2012
- DBPROP_INIT_LCID = 2055
- DBPROP_INIT_PROMPT = 4
- DBPROP_INIT_DATASOURCE = локальный
- DBPROP_AUTH_INTEGRATED= ССПИ
С SQLNCLI
провайдер тот же код работает.
[РЕДАКТИРОВАТЬ #1]: я сбросил еще немного информации об ошибках, используя AtlTraceErrorRecords
и это подтвердило ту же ошибку, что и в связанном отчете Microsoft Connect:
Row #: 0 Source: "Microsoft SQL Server Native Client 11.0" Description: "The fractional part of the provided time value overflows the scale of the corresponding SQL Server parameter or column. Increase bScale in DBPARAMBINDINFO or column scale to correct this error." Help File: "(null)" Help Context: 0 GUID: {0C733A63-2A1C-11CE-ADE5-00AA0044773D}
[РЕДАКТИРОВАТЬ #2]: Из моих дальнейших исследований похоже, что это определенное и принятое поведение нового Native Client. См. Более строгие параметры проверки SQL_C_TYPE _TIMESTAMP и DBTYPE_DBTIMESTAMP для справки. Но Microsoft не может отнестись к этому изменению серьезно без какой-либо документации о том, как правильно использовать datetime2. Действительно ли они требуют от самих разработчиков округлить DBTIMESTAMP перед вставкой их в базу данных? В некоторых случаях округление даже не работает из-за некоторых ошибок точности с плавающей запятой. Возьмите пример временной метки сверху, и вы увидите типичную ошибку точности в.9999. Как можно заставить это странное поведение работать? С этими сообщениями об ошибках вы даже не можете сохранить текущую метку времени в базе данных без использования функции SQL для now()
потому что вы обычно работаете в этих переполнениях.
2 ответа
Я сделал несколько тестов и полностью переписал свой ответ.
Я использую SQL Server 2008 с Native Client 10. Он уже поддерживает datetime2(7)
так что мои тесты применимы.
Дата и время
У меня есть хранимая процедура с табличным параметром. Один из столбцов таблицы параметров имеет datetime
тип. Я не использовал ненулевые доли секунды раньше, но попробовал сейчас. Мне понадобилось время, чтобы понять, что нужно.
Чтобы использовать ненулевое значение в DBTIMESTAMP.fraction
поле мне нужно было настроить как описание столбца, так и описание параметра: установить DBCOLUMNDESC.bScale
до 3 и DBBINDING.bScale
до 3. Оба bPrecision
был оставлен в 0. Этот документ MSDN помог мне понять, что я должен установить DBCOLUMNDESC.bScale
также.
Поставщик OLE DB для собственного клиента SQL Server проверяет элемент DBCOLUMDESC bScale для определения точности долей секунды.
однажды Scale
установлен на 3, я мог бы установить значение DBTIMESTAMP.fraction
в 555000000
и он был успешно вставлен в базу данных как .557
т. е. сервер округляет значение до 1/3 миллисекунды (точность datetime
тип). Но когда я попытался установить значение DBTIMESTAMP.fraction
в 552100000
Я получил ту же ошибку, что и вы. Таким образом, сервер ожидает, что программисты сами округляют значения.
Один простой способ обрезать дроби будет выглядеть примерно так:
DBTIMESTAMP dt;
// set to any value from 0 to 999,999,999 (billionth of a second)
dt.fraction = 555123456;
// truncate fractions to milliseconds before inserting into the database
dt.fraction /= 1000000;
dt.fraction *= 1000000;
datetime2 (7)
I changed the type of the column in the table-valued parameter to datetime2(7)
and did few more tests.
Я поставил Scale
to 7 and could successfully insert into the database DBTIMESTAMP.fraction
со значением 555123400
, but when I tried to insert value 555123478
Я получил ту же ошибку. Таким образом, для datetime2(7)
server also expects programmers to round the values themselves.
// truncate fractions to 100 nanoseconds precision
dt.fraction /= 100;
dt.fraction *= 100;
Должно быть достаточно.
При использовании C# или VB с библиотекой OleDb установите значение OleDbParameter.Scale, чтобы избежать этого сообщения об ошибке.
Протестировано с Provider=SQLNCLI11.