datetime
値12/30/1899をSQL Serverに渡そうとすると、Invalidで失敗します日付形式-ただし、ネイティブクライアントドライバーのみ、およびDataTypeCompatiblityモードのみ。
SQL Serverに対して、ADOでパラメーター化されたクエリを使用しようとすると:
_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
of 7( _VT_DATE
_ )、および8バイトの浮動小数点値である値:
_VARIANT
Int32 vt = 7; //VT_DATE
Double date = 0;
_
特定の1つの日時で同じテストコードを実行すると、失敗します。
_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 Serverに到達する前に):
_Invalid date format
_
この問題をデバッグしたところ、すべてのSQL Server OLEDBプロバイダーで発生するわけではないことに気付きました。 Microsoftは通常4 OLE SQL Server用のDBプロバイダーを持っています:
SQLOLEDB
:Microsoft OLE SQL Server用DBプロバイダー(Windows 2000以降Windowsに同梱されています)SQLNCLI
:SQL Server Native Client(SQL Server 2005に付属)SQLNCLI10
_:SQL Server Native Client 11.0(SQL Server 2008に付属)SQLNCLI11
_:SQL Server Native Client 12.0(SQL Server 2012に同梱)MSOLEDBSQL
:Microsoft OLE SQL Server用のDBドライバー(SQL Server 2016に付属)いくつかの異なるプロバイダーで試してみると、それはdoesのためにうまく機能します:
SQLOLEDB
:機能SQLNCLI11
_(DataTypeCompatibilityなし):機能SQLNCLI11
_(DataTypeCompatiilityがオン):失敗はい。 ActiveXデータオブジェクト(ADO)、非友好的なCOM OLEDB APIの友好的なCOMラッパーは、新しいdate
、time
、xml
、_datetime2
_を理解しません、datetimeoffset
データ型。これらの新しい型を表すために、新しいOLEDBデータ型定数が作成されました。そのため、既存のOLEDBアプリケーションは新しい定数を理解できません。
そのため、新しい キーワードは"native"でサポートされています OLE DBドライバ:
DataTypeCompatibility=80
_これを接続文字列に追加できます。
"プロバイダー= SQLNCLI11;データソース=スクリュードライバー;ユーザーID = 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
はオンです「1899年12月30日」の日付を使用しても、本質的に問題はありません。
SELECT CAST('18991230' AS datetime)
は正常に動作しますDataTypeCompatibility
がオンのネイティブドライバで失敗するだけです明らかにこれはMicrosoftのバグですOLE DBドライバです。しかし、Microsoftが絶対に絶対に真実ではないever、ever、[〜#〜] ever [〜#〜]、バグを修正します。
この特別な日時を検出でき、データアクセスレイヤーでこのバグを回避することができます。
VARIANT
構造に配置できる値が必要です。12/30/1899 12:00:00 AM
_を表しますSQOLEDB
の下で動作しますSQLNCLI
xxドライバーの下MSOLEDBSQL
ドライバーの下DataTypeCompatibilityMode
OLE DB driver doesが実際に私が言うことを実行するのに煩わしい場合、生成されたRPCのプロファイルを作成できます。
[〜#〜] 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'
_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固有の質問ではありませんが、 Delphiを使用しています。そのため、Delphiのタグが付けられています。あなたが文句を言うなら 私はあなたの舌を詰まらせます。
注:これはADO.netではなく、ADOです。これはマネージ.NET Frameworkクラスライブラリではなく、ネイティブのWin32 COM OLE DB APIです。
答えはBrakNickuでした。
パラメータの
NumericScale
プロパティを1〜7の範囲に設定します。
コードの変更:
Parameter p = cmd.CreateParameter("", adDBTimeStamp, adParamInput, 0, v);
に
Parameter p = cmd.CreateParameter("", adDBTimeStamp, adParamInput, 0, v);
p.NumericScale = 1;
動作します。
SQL Server 2000に対するSQLOLEDBドライバーでも動作します。
さまざまなデータ型を含むSQL Serverから行セットを返すと、OLEDBにさまざまなT-SQLデータ型のPrecision
とNumericScale
を尋ねることができます。
SQL Server type ADO type Precision NumericScale DefinedSize
---------------- --------------------- --------- ------------ -----------
int adInteger (3) 10 255 4
real adSingle (4) 7 255 4
money adCurrency (6) 19 255 8
bit adBoolean (11) 255 255 2
tinyint adUnsignedTinyInt (17) 3 255 1
bigint adBigInt (20) 19 255 8
uniqueidentifier adGUID (72) 255 255 16
char(35) adChar (129) 255 255 35
nchar(35) adWChar (130) 255 255 35
decimal(15,5) adNumeric (131) 15 5 19
datetime adDBTimeStamp (135) 23 3 16
varchar(35) adVarChar (200) 255 255 35
text adLongVarChar (201) 255 255 2147483647
varchar(max) adLongVarChar (201) 255 255 2147483647
nvarchar(35) adVarWChar (202) 255 255 35
nvarchar(max) adLongVarWChar (203) 255 255 1073741823
xml adLongVarWChar (203) 255 255 1073741823
image adLongVarBinary (205) 255 255 2147483647
varbinary(max) adLongVarBinary (205) 255 255 2147483647
SQL Serverはdatetime
フィールドをNumericScale
で返します。そこにmay変更の美徳がある
Parameter p = cmd.CreateParameter("", adDBTimeStamp, adParamInput, 0, v);
p.NumericScale = 1;
に
Parameter p = cmd.CreateParameter("", adDBTimeStamp, adParamInput, 0, v);
p.NumericScale = 3;