如何使用新的SQL Server本地客户端将datetime迁移到datetime2



我们目前正在将我们的数据库从datetime迁移到datetime2,包括SQL Server Native Client v11 (SQL Server 2012)。数据库的更新很容易完成,但是问题伴随着我们想要使用的新的SQL Server Native Client 11而来。

通常,我们有带有"COLUMN_ENTRY*"访问器的OLE DB消费者,用于我们的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成员的小数部分太精确而不能插入。根据这个链接,新的原生客户端版本(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提供具有各种值的日期时间精度和比例,但插入总是失败。

我们如何强制本机客户端简单地接受该值并截断它?很明显,我们不能手动截断所有的值到支持的DB精度。我找不到关于如何使用datetime2与新的本地客户端的任何适当的文档。我们是否缺少正确处理datetime2的转换或设置?

这是测试设置:

  • Windows 7 64bit
  • SQL Server Native Client 11 (SQLNCLI11)
  • SQL Server 2012
  • DBPROP_INIT_LCID = 2055
  • DBPROP_INIT_PROMPT = 4
  • DBPROP_INIT_DATASOURCE = localhost
  • DBPROP_AUTH_INTEGRATED = SSPI

SQLNCLI提供程序相同的代码工作。

[EDIT#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}

[EDIT#2]:从我进一步的研究来看,这似乎是新的原生客户端的定义和接受的行为。参见更严格的SQL_C_TYPE _TIMESTAMP和DBTYPE_DBTIMESTAMP参数验证。但是如果没有任何关于如何正确使用datetime2的文档,微软就不能认真对待这个改变。它们真的要求开发人员在将DBTIMESTAMP插入数据库之前对其进行四舍五入吗?在某些情况下,由于一些浮点精度错误,舍入偶数不能工作。以上面的时间戳示例为例,您将看到典型的.9999精度错误。人们怎么能让这种奇怪的行为起作用呢?有了这个错误消息,如果不使用now()的SQL函数,您甚至不能将当前时间戳保存到数据库中,因为您通常会在这些溢出中运行。

我做了几次测试,完全重写了我的答案。

我使用SQL Server 2008与本地客户端10。它已经支持datetime2(7),所以我的测试是适用的。

datetime

我有一个存储过程,它有一个表值参数。参数表的其中一列是datetime类型的。我之前没有使用非零分数,但现在尝试了。我花了好一会儿才弄清楚需要什么。

为了在DBTIMESTAMP.fraction字段中使用非零值,我必须配置列描述和参数描述:将DBCOLUMNDESC.bScale设置为3,将DBBINDING.bScale设置为3。两个bPrecision都为0。这个MSDN文档帮助我意识到我也必须设置DBCOLUMNDESC.bScale

SQL Server Native Client OLE DB提供程序检查DBCOLUMDESCbScale成员确定小数秒精度。

一旦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)

我将表值参数中的列的类型更改为datetime2(7),并进行了更多的测试。

我将Scale设置为7,并且可以成功地将值555123400插入数据库DBTIMESTAMP.fraction,但是当我试图插入值555123478时,我得到了相同的错误。因此,对于datetime2(7),服务器也希望程序员自己舍入值。

// truncate fractions to 100 nanoseconds precision
dt.fraction /= 100;
dt.fraction *= 100;

当使用c#或VB的OleDb库时,设置OleDbParameter。缩放值以避免此错误消息。

test with Provider=SQLNCLI11.

最新更新