Excel VBA ADODB RecordSet 从 SQL 服务器更改字段类型

Excel VBA ADODB RecordSet changes Field types from SQL Server

我是 运行 SQL 服务器上的一个简单查询 table:

SELECT * FROM MyTable

table 包含三列,类型为 intbitdatetime2(0)。我正在使用以下代码:

Dim cnn As ADODB.Connection
Dim rst As ADODB RecordSet
Dim rngDest As Range
:
: ' etc
:
rst.Open "SELECT * FROM MyTable", cnn
rngDest.CopyFromRecordset rst

这很好用,除了似乎是字段类型错误 :它说字段类型是 3、11、202,根据文档,它们是 Integer(正确),Boolean(正确,我猜)和 NVarChar2 —— 不正确。第三个字段应该是date,当然("Date"类型是135)。

作为背景:我使用 SSMA 将数据从 Access 迁移到 SQL 服务器。 table 现在具有我提到的三种列类型。另外,我知道 CopyFromRecordset 可能会弄错字段类型是一个众所周知的问题,我最初认为这就是第三列(日期)作为字符串出现在 Excel 中的原因,但在更近的时候检查我可以看到,当 VBA 读取 Recordset 时,它已经认为该字段是文本,位于 CopyFromRecordset 行之前。我觉得如果我能让 VBA 以某种方式识别第三个字段应该是一个日期,那么我就可以在内部转换它。我宁愿不为此 table 创建解决方案,因为我有很多很多表和视图需要处理,所以我更愿意找到一种适用于所有这些的方法。

如果它很重要,我使用的连接字符串是:

Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=True;Data Source=[REDACTED];Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Use Encryption for Data=False;Tag with column collation when possible=False;Initial Catalog=[REDACTED];

有人可以帮忙吗?

Windows 附带的向后兼容 SQLOLEDB 驱动程序没有概念 SQL 过去 20 年引入的服务器数据类型,例如 date。尝试最新的 SQL 服务器 OLEDB 驱动程序,MSOLEDBSQL

我 运行 使用下面的 VbScript 进行快速 ADO 测试,date 列返回 ADO 类型 133 (adDBDate)。

Set connection = CreateObject("ADODB.Connection")
connection.Open "Provider=MSOLEDBSQL;Data Source=.;Integrated Security=SSPI"
Set recordset = connection.Execute("SELECT CAST(SYSDATETIME() AS date);")
MsgBox recordset.Fields(0).Type
connection.Close

您的日期字段可能是字符串。所以你转换日期。以下示例将字符日期转换为日期数据。您需要根据您的字符类型进行不同的指定。

20130604060133 -> 2013/06/04 06:01:33

select
  convert(datetime,SUBSTRING( '20130604060133',1,8),112) +
  convert(datetime,
   ( substring( '20130604060133',9,2) + ':' +
    substring( '20130604060133',11,2)  + ':' +
    substring( '20130604060133',13,2)  )
    ,120) as myDate

问题好像出在别的地方,但不清楚。但是有一种方法可以更改数据。见下文。

Dim cnn As ADODB.Connection
Dim rst As ADODB.Recordset
Dim rngDest As Range
:
: ' etc
:
rst.Open "SELECT * FROM MyTable", cnn
rngDest.CopyFromRecordset rst

Dim vDB
Dim rngDB As Range
Set rngDB = rngDest.CurrentRegion
vDB = rngDB
rngDB.NumberFormat = "General"
rngDB = vDB

这是我发现有效的解决方案(在等待支持人员更新驱动程序时): [TL;DR:使用 rst.GetRows 将 Recordset 读入变体 varOrig;转置,然后 plonk 成 Excel sheet;使用 .Value2 = 会导致 Excel 将其转换为正确的日期。 [我们必须进行转置,因为 .GetRows,尽管它的名称是 returns 列数据。]]

希望这对以后遇到类似问题的其他人有用。

Public Sub RecordSetToRange(ByRef rst As ADODB.Recordset, ByRef lobDst As ListObject)

    Const strROUTINE As String = "RecordSetToRange"
    Dim iRow As Long
    Dim iCol As Long
    Dim varOrig As Variant
    Dim varTxp() As Variant

    ' -- Error Handling, Validation, Initialization.
    On Error GoTo ErrHandler

    ' -- Procedure.

    ' Headers first.
    With lobDst.HeaderRowRange.Cells(1)
        For iCol = 0 To rst.Fields.Count - 1
            .Offset(0, iCol).Value2 = rst.Fields(iCol).Name
        Next iCol
    End With
    ' Then data. Note that, despite the name of the function, GetRows returns columns of data, so we have
    ' to transpose it. Don't do anything if there is no data.
    If rst.RecordCount > 0 Then
        varOrig = rst.GetRows
        ReDim varTxp(UBound(varOrig, 2), UBound(varOrig, 1))
        For iRow = 0 To UBound(varOrig, 2)
            For iCol = 0 To UBound(varOrig, 1)
                varTxp(iRow, iCol) = varOrig(iCol, iRow)
            Next iCol
        Next iRow
        lobDst.DataBodyRange.Resize(UBound(varOrig, 2) + 1, UBound(varOrig, 1) + 1).Value2 = varTxp
    End If
    '
ErrHandler:        ' -- Error handling and Routine termination.
    If Err.Number <> 0 Then If DspErr(mstrMODULE, strROUTINE) Then Stop: Resume Else End
End Sub