短版本
尝试将datetime
值12/30/1899传递给SQL Server失败,原因是日期格式无效-但仅适用于本机客户端驱动程序,并且仅在DataTypeCompatibility模式下。
长版本
在ADO中尝试使用参数化查询时,针对SQL Server:
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;
但它在1899年12月30日失败了
如果我用一个特定的日期时间做同样的测试代码,它会失败:
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提供程序都会出现这种情况
在调试这个问题时,我意识到并不是所有的SQL Server OLEDB提供程序都会出现这种情况。Microsoft通常有4个用于SQL Server的OLE DB提供程序:
SQLOLEDB
:Microsoft OLE DB Provider for SQL Server(自Windows 2000起随Windows提供)SQLNCLI
:SQL Server本机客户端(随SQL Server 2005一起提供)SQLNCLI10
:SQL Server Native Client 11.0(SQL Server 2008附带)SQLNCLI11
:SQL Server Native Client 12.0(随SQL Server 2012一起提供)MSOLEDBSQL
:适用于SQL Server的Microsoft OLE DB驱动程序(SQL Server 2016附带)
当与一些不同的提供商一起尝试时,对某些提供商来说效果良好:
SQLOLEDB
:作品SQLNCLI11
(不带DataTypeCompatibility):有效SQLNCLI11
(打开DataTypeCompatibility):失败
数据类型兼容性
是的。ActiveX数据对象(ADO)是一个围绕不友好的COM OLEDB API的友好COM包装器,它不理解新的date
、time
、xml
、datetime2
、datetimeoffset
数据类型。创建了新的OLEDB数据类型常量来表示这些新类型。因此,任何现有的OLEDB应用程序都不会理解新的常数。
为此,"本地">OLE DB驱动程序:
DataTypeCompatibility=80
您可以将其添加到连接字符串中:
"提供者=SQLNCLI11;数据源=螺丝刀;用户ID=hatguy;密码=hunter2DataTypeCompatibility=80";
这指示OLEDB驱动程序仅返回OLEDB首次发明时存在的OLEDB数据类型:
SQL Server数据类型 | SQLOLEDB | SQLNCLI | >1SQLNCLI (w/DataTypeCompatibility=80) |
---|---|---|---|
Xml | adLongVarWChar | 141(DBTYPE_Xml) | >td>adLongVarChar|
日期时间 | adDBTimeStamp | ||
日期时间2 | adVarWChar | ||
datetimeoffset | |||
日期 | adVarWChar | ||
时间 | adVarWChar | 145(DBTYPE_DBTIME2) | adVarWChar|
UDT | 132(DBTYPE_UDT) | adVarBinary(已记录,未测试)||
varchar(max) | adLongVarChar | ||
nvarchar(最大值) | adLongVarWChar | adLongVarWChar||
varbinary(max) | adLongVarBinary | ||
时间戳 | adBinary |
将参数的
NumericScale
属性设置为1-7范围内的任何值。
更改代码自:
Parameter p = cmd.CreateParameter("", adDBTimeStamp, adParamInput, 0, v);
至
Parameter p = cmd.CreateParameter("", adDBTimeStamp, adParamInput, 0, v);
p.NumericScale = 1;
工作。
它甚至可以与针对SQLServer2000的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
为3;有可能是改变的美德:
Parameter p = cmd.CreateParameter("", adDBTimeStamp, adParamInput, 0, v);
p.NumericScale = 1;
至
Parameter p = cmd.CreateParameter("", adDBTimeStamp, adParamInput, 0, v);
p.NumericScale = 3;
奖励阅读
不要试图将datetime
参数化为adDBTimestamp
。微软的SQL Server OLEDB驱动程序中存在数据丢失错误(全部):
- SQLOLEDB(1999)-失败
- SQLNCLI(2005)-失败
- SQLNCLI10(2008)-失败
- SQLNCLI11(2010)-失败
- MSOLEDBSQL(2012)-失败
正确的答案是将所有datetime
值参数化为字符串(例如adVarChar
);ODBC 24小时格式":
yyyy-mm-dd hh:mm:ss.zzz
2021-03-21 18:16:22.619