Короткая версия
Попытка передать значение datetime
12/30/1899 на SQL Сервер, происходит сбой с Недопустимый формат даты - но только для собственных драйверов клиента и только в режиме DataTypeCompatiblity .
Длинная версия
При попытке использовать параметризованные запросы в ADO для SQL Сервер:
SELECT ?
Я параметризирую значение datetime
как adDBTimeStamp
:
//Language agnostic, vaguely C#-like pseudo-code
void TestIt()
{
DateTime dt = new DateTime("3/15/2020");
VARIANT v = DateTimeToVariant(dt);
Command cmd = new Command();
cmd.CommandText = "SELECT ? AS SomeDate";
cmd.Parameters.Append(cmd.CreateParameter("", adDBTimeStamp, adParamInput, 0, v);
Connection cn = GetConnection();
cmd.Set_ActiveConnection(cn);
cmd.Execute(out recordsAffected, EmptyParam, adExecuteNoRecords);
}
И это прекрасно работает, когда дата 3/15/2020
.
Вы создаете VARIANT
с VType
из 7 (VT_DATE
) и значением, которое является 8-байтовое значение с плавающей запятой:
VARIANT
Int32 vt = 7; //VT_DATE
Double date = 0;
Но это не сработает 12/30/1899
Если я выполню один и тот же тестовый код с одной конкретной датой-временем, произойдет ошибка:
void TestIt()
{
DateTime dt = new DateTime("12/30/1899");
VARIANT v = DateTimeToVariant(dt);
Command cmd = new Command();
cmd.CommandText = "SELECT ? AS SomeDate";
cmd.Parameters.Append(cmd.CreateParameter("", adDBTimeStamp, adParamInput, 0, v);
Connection cn = GetConnection();
cmd.Set_ActiveConnection(cn);
cmd.Execute(out recordsAffected, EmptyParam, adExecuteNoRecords);
}
Поставщик ADO OLEDB генерирует исключение (т. Е. Еще до того, как он достигает SQL Сервер):
Invalid date format
Но это не происходит со всеми SQL Серверными поставщиками OLEDB
При отладке этой проблемы я понял, что это происходит не со всеми поставщиками SQL серверов OLEDB. Microsoft обычно имеет 4 поставщика OLE DB для SQL сервера:
SQLOLEDB
: поставщик Microsoft OLE DB для SQL сервера (поставляется с Windows с Windows 2000) SQLNCLI
: SQL Собственный клиент сервера (поставляется с SQL Server 2005) SQLNCLI10
: SQL Собственный клиент сервера 11.0 (поставляется с SQL Server 2008) SQLNCLI11
: SQL Собственный клиент сервера 12.0 (поставляется с SQL Server 2012) MSOLEDBSQL
: драйвер Microsoft OLE DB для SQL Сервер (поставляется с SQL) Server 2016)
При попытке работы с разными провайдерами отлично работает для некоторых:
SQLOLEDB
: Работает SQLNCLI11
(без DataTypeCompatibility): Работает SQLNCLI11
(с DataTypeCompatibility on): Сбой
DataTypeCompatibility?
Да. ActiveX Data Objects (ADO), дружественная оболочка COM для недружественного COM OLEDB API, не понимает новые типы данных date
, time
, xml
, datetime2
, datetimeoffset
. Были созданы новые константы типов данных OLEDB для представления этих новых типов. Поэтому любые существующие приложения OLEDB не будут понимать новые константы.
С этой целью новое ключевое слово поддерживается "родными" драйверами OLE DB:
, который можно добавить в строку подключения:
"Поставщик = SQLNCLI11; Источник данных = отвертка; Идентификатор пользователя = hatguy; Пароль = hunter2; DataTypeCompatibility = 80;"
Это дает указание драйверу OLEDB возвращать только те типы данных OLEDB, которые существовали, когда OLEDB был впервые изобретен:
| SQL Server data type | SQLOLEDB | SQLNCLI | SQLNCLI |
| | | | w/DataTypeCompatibility=80 |
|----------------------|-----------------|--------------------------------|-----------------------------------|
| Xml | adLongVarWChar | 141 (DBTYPE_XML) | adLongVarChar |
| datetime | adDBTimeStamp | adDBTimeStamp | adDBTimeStamp |
| datetime2 | adVarWChar | adDBTimeStamp | adVarWChar |
| datetimeoffset | adVarWChar | 146 (DBTYPE_DBTIMESTAMPOFFSET) | adVarWChar |
| date | adVarWChar | adDBDate | adVarWChar |
| time | adVarWChar | 145 (DBTYPE_DBTIME2) | adVarWChar |
| UDT | | 132 (DBTYPE_UDT) | adVarBinary (documented,untested) |
| varchar(max) | adLongVarChar | adLongVarChar | adLongVarChar |
| nvarchar(max) | adLongVarWChar | adLongVarWChar | adLongVarWChar |
| varbinary(max) | adLongVarBinary | adLongVarBinary | adLongVarBinary |
| timestamp | adBinary | adBinary | adBinary |
И есть сбой
Когда:
- пытается параметризовать
datetime
значение - со значением
12/30/1899
- при использовании "родного клиента" драйвера
- и
DataTypeCompatilibty
включен - сам драйвер задыхается от значения
- когда его значение действительно отлично.
Нет ничего плохого в том, чтобы пытаться использовать дату '12/30 / 1899`:
SELECT CAST('18991230' AS datetime)
отлично работает - отлично работает в оригинальном драйвере OLE DB
- отлично работает в "родных" драйверах OLE DB
- в родном драйвере происходит сбой с
DataTypeCompatibility
на
Очевидно, что это ошибка в драйверах Microsoft OLE DB. Но это абсолютная правда, что Microsoft никогда, когда-либо , когда-либо , КОГДА-ЛИБО , исправит ошибку.
Итак, как обойти это?
Я могу обнаружить эту особую дату и время, чтобы попытаться обойти эту ошибку в наших слоях доступа к данным.
- Но мне нужно значение, которое я могу поместить в
VARIANT
структуру, - , которая представляет
12/30/1899 12:00:00 AM
- , которая работает под
SQOLEDB
- и ниже
SQLNCLI
xx драйверы - и ниже
MSOLEDBSQL
драйвер - в
DataTypeCompatibilityMode
- (и что, черт возьми, даже при выключенном режиме - хотя использование ADO без него недопустимо)
T- SQL генерируется драйвером
Когда драйвер OLE DB действительно не удосуживается сделать то, что я говорю, мы можем профилировать сгенерированный RP C:
SQOLEDB
exec sp_executesql N'SELECT @P1 AS SomeDate',N'@P1 datetime','1899-12-30 00:00:00'
SQLNCLI11
exec sp_executesql N'SELECT @P1 AS SomeDate',N'@P1 datetime2(0)','1899-12-30 00:00:00'
CMRE (Delphi)
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
ComObj,
ActiveX,
ADOdb,
ADOint,
Variants;
function GetConnection(Provider: string; DataTypeCompatibility: Boolean): _Connection;
var
connectionString: string;
begin
{
SQLOLEDB - Default provider with Windows
SQLNCLI11 - SQL Server 2008 native client
}
connectionString := 'Provider='+Provider+'; Data Source=screwdriver;User ID=hydrogen;Password=hunter2;';
if DataTypeCompatibility then
connectionString := connectionString+'DataTypeCompatibility=80';
Result := CoConnection.Create;
Result.Open(connectionString, '', '', adConnectUnspecified);
end;
procedure Test(ProviderName: string; DataTypeCompatibility: Boolean);
var
dt: TDateTime;
v: OleVariant;
cmd: _Command;
cn: _Connection;
recordsAffected: OleVariant;
s: string;
begin
dt := EncodeDate(1899, 12, 30);// 12/30/1899 12:00:00 AM (also known in Delphi as zero)
v := dt; //the variant is of type VT_DATE (7)
cmd := CoCommand.Create;
cmd.CommandText := 'SELECT ? AS SomeDate';
cmd.Parameters.Append(cmd.CreateParameter('', adDBTimeStamp, adParamInput, 0, v));
try
cn := GetConnection(ProviderName, DataTypeCompatibility);
except
on E: Exception do
begin
WriteLn('Provider '+ProviderName+' not installed: '+E.message);
Exit;
end;
end;
if SameText(ProviderName, 'SQLOLEDB') then
s := ''
else if DataTypeCompatibility then
s := ' (with DataTypeCompatibility)'
else
s := ' (without DataTypeCompatibility)';
cmd.Set_ActiveConnection(cn);
try
cmd.Execute({out}recordsAffected, EmptyParam, adExecuteNoRecords);
WriteLn('Provider '+ProviderName+s+': success.');
except
on E:Exception do
begin
WriteLn('Provider '+ProviderName+s+' failed: '+E.Message);
end;
end;
end;
procedure Main;
begin
CoInitialize(nil);
Test('SQLOLEDB', False); //SQL Server client that ships with Windows since 2000
Test('SQLNCLI', False); //SQL Server 2005 native client
Test('SQLNCLI', True); //SQL Server 2005 native client, w/ DataTypeCompatibilty
Test('SQLNCLI10', False); //SQL Server 2008 native client
Test('SQLNCLI10', True); //SQL Server 2008 native client, w/ DataTypeCompatibilty
Test('SQLNCLI11', False); //SQL Server 2012 native client
Test('SQLNCLI11', True); //SQL Server 2012 native client, w/ DataTypeCompatibilty
Test('MSOLEDBSQL', False); //SQL Server 2016 native client
Test('MSOLEDBSQL', True); //SQL Server 2016 native client, w/ DataTypeCompatibilty
end;
begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
WriteLn('Press enter to close');
ReadLn;
end.
И хотя это не Delphi -специфический c вопрос; Я использую Delphi. Так что он помечен как Delphi. Если вы жалуетесь Я собираюсь задушить ваш язык.
Примечание : Это не ADO. net, это ADO. Не управляется. NET Framework Class Library, это собственный Win32 API-интерфейс COM OLE DB.