【问题标题】:VBA big SQL-Query - Method 'CopyFromRecordset' of object 'Range' failedVBA 大 SQL 查询 - 对象“范围”的方法“CopyFromRecordset”失败
【发布时间】:2016-01-07 19:55:29
【问题描述】:

下面发布的代码连接到 oracle 数据库,处理 SQL 查询并将结果表保存在新工作簿中。它适用于大约 200.000 行。但是,对于较大的数据集,当我尝试将数据从记录集对象复制到工作簿时,会发生错误 Method 'CopyFromRecordset' of object 'Range' failed

dataWs.Range("A2").CopyFromRecordset dataset 

有什么解决办法吗?我尝试遍历数据集的所有元素并将它们复制到工作表中,但是对于大数据集来说这需要很长时间。你有什么想法?我感谢您的帮助!现在是代码:

Sub QueryExecute(sqlString, userPW, userID, serverName)
'Connect to database <serverName> using user name <userID> and 
'password <userPW> to process SQL query <sqlString> and save the
'query result in a new workbook

   Dim ConnStr As String
   Dim Cn As ADODB.Connection
   Dim dataset As ADODB.Recordset
   Dim dataWs As Worksheet
   Dim dataWb As Workbook
   Dim icols As Integer

   'Create new workbook that will hold the query result/table:
   Set dataWb = Excel.Application.Workbooks.Add
   Set dataWs = dataWb.Sheets(1)

   Application.Calculation = xlManual

   'Trim trailing/leading blanks from sqlString:
   sqlString = Trim(sqlString)

   'Create string for database connection:
   ConnStr = "UID=" & userID & ";PWD=" & userPW & ";DRIVER={Microsoft ODBC for Oracle};" _
                    & "SERVER=" & serverName & ";"

   'Connect to database:
   Set Cn = New ADODB.Connection

   On Error Resume Next 'Error handling in case connection does not work

   With Cn
     .ConnectionString = ConnStr
     .CursorLocation = adUseClient
     .Open
   End With

   'Error handling for failed connection:
   If Err.Number <> 0 Then

     dataWb.Close
     MsgBox "Connection to database failed. Check username and password."
     Exit Sub

   End If

   'Send SQL query to database:
   Set dataset = Cn.Execute(sqlString)

   'Error handling for failed query:
   If Err.Number <> 0 Then

     dataWb.Close
     MsgBox "SQL-query could not be processed."
     Exit Sub

   End If

   On Error GoTo 0

   'Copy column names in first row of table worksheet:
   For icols = 0 To dataset.Fields.count - 1
     dataWs.Cells(1, icols + 1).Value = dataset.Fields(icols).Name
   Next

   dataWs.Range(dataWs.Cells(1, 1), _
   dataWs.Cells(1, dataset.Fields.count)).Font.Bold = True 'Format column names

   'Copy data to workbook:
   '***THIS WILL FAIL FOR LARGE DATASETS***
   dataWs.Range("A2").CopyFromRecordset dataset 


   dataset.Close
   Cn.Close

   MsgBox "Query successful."

   Application.Calculation = xlCalculationAutomatic

End Sub

【问题讨论】:

  • 你试过GetRows吗?不过,您需要先转置数组,然后再将其放入工作表中。
  • 我将数据分配给了一个数组,但是对于大数据集,我在尝试将 GetRows 分配给数组时会收到内存不足错误。
  • 您的较大数据集的记录中是否有任何非常长的文本值出现在有效的较小数据集中?

标签: sql-server oracle vba excel


【解决方案1】:

根据Microsoft article - 最大行数为 1,048,576 行 x 16,384 列。 鉴于,操纵或检查一百万行是不现实的——我们可以假设电子表格然后汇总行吗? 如果是这种情况 - 您应该始终寻求最小化返回到 Excel 的记录集的大小。为此,您需要将数据的处理/汇总卸载到数据库中。

这可以在 SQL 查询或返回 SYS_REFCURSOR 的数据库过程中完成。这本质上是一个指向结果集的指针。

【讨论】:

    【解决方案2】:

    就像@OraNob 所说,通过在数据库端进行过滤、聚合和排序,最大限度地减少您返回的数据量。如果您必须检索大型数据集(以减少多次调用),您可以考虑保持记录集打开,并使用您需要的各种数据子集的数据填充工作表。 如果您的记录集有超过几百万行,那么您可以将结果写到多个工作表中。

    我还建议使用 GetRows 函数,您需要对其进行转置,因为 GetRows 数组的维度是先列后行,而 Excel 最适合先行后列。

    另外,考虑到您的数据集的大小,并且假设是 32 位 Office,您将无法依赖 Application.Worksheet.Transpose 进行转置,因为您可能会用完内存,并且您可能需要无论如何都要小心记忆,如果你自己做转置的话。考虑将转置和插入分成批次。

    最后,请记住以范围的形式插入工作表,因为它比逐个单元格地快大大。例如:

    Dim aData(1 to 10000, 1 to 16)
    aRecordset = rst.GetRows(10000)
    'Transpose the aRecordset into aData
    '...
    Sheet1.Range(Sheet1.cells(1,1),Sheet1.Cells(10000,16) = aData
    

    【讨论】:

    • 有关详细信息,请阅读:support.microsoft.com/en-us/kb/246335 但将 recArray = rst.GetRows 更改为 IF NOT rst.EOF THEN rst.MoveLast: rst.MoveFirst 然后 recArray = rst.GetRows(rst.RecordCount)
    猜你喜欢
    • 1970-01-01
    • 2016-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多