简体   繁体   English

VBA大SQL查询-对象“范围”的方法“ CopyFromRecordset”失败

[英]VBA big SQL-Query - Method 'CopyFromRecordset' of object 'Range' failed

the code as posted below connects to an oracle database, processes an SQL query and saves the resulting table in a new workbook. 下面发布的代码连接到oracle数据库,处理SQL查询并将结果表保存在新工作簿中。 It works well up to about 200.000 rows. 它最多可以处理约200.000行。 However, with larger datasets the error Method 'CopyFromRecordset' of object 'Range' failed occurs when I try to copy the data from the recordset object to the workbook: 但是,对于较大的数据集,当我尝试将数据从记录集对象复制到工作簿时,对象“范围”的错误方法“ CopyFromRecordset”失败

dataWs.Range("A2").CopyFromRecordset dataset 

Is there any solution to it? 有什么解决办法吗? I tried looping over all elements of the dataset and copy them into the worksheet, however that takes extremely long for big datasets. 我尝试遍历数据集的所有元素,然后将它们复制到工作表中,但是对于大型数据集,这花费了极长时间。 Do you have any ideas? 你有什么想法? I appreciate your help! 我感谢您的帮助! Here's the code now: 现在是代码:

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

According to the Microsoft article - the maximum rows is 1,048,576 rows by 16,384 columns. 根据Microsoft的文章 -最大行为1,048,576行乘以16,384列。 Given, it is unrealistic to manipulate or scrutinize a million rows - can we assume the spreadsheet is then summarizing the rows? 鉴于此,操纵或检查一百万行是不现实的-我们是否可以假设电子表格正在汇总这些行? If that is the case - you should be always looking to minimize the size of the recordset being returned to Excel. 如果是这种情况,您应该一直在寻找最小化返回给Excel的记录集的大小。 And to do this you would off-load the processing / summarizing of the data onto the database. 为此,您需要将数据的处理/汇总工作卸载到数据库中。

This could be done in the SQL query or a database procedure returning a SYS_REFCURSOR. 这可以在SQL查询或返回SYS_REFCURSOR的数据库过程中完成。 This is essentially a pointer to the resultset. 这本质上是指向结果集的指针。

Like @OraNob says, minimise the volume of data that you're returning, by filtering, aggregating and sorting, at the database-end. 就像@OraNob所说的那样,通过在数据库端进行过滤,聚合和排序来最大程度地减少返回的数据量。 If you must retrieve large datasets (to reduce multiple calls), you could consider keeping the recordset open, and just populating the worksheet with the data you need for various sub-sets of the data. 如果必须检索大型数据集(以减少多次调用),则可以考虑保持记录集处于打开状态,并仅使用数据的各个子集所需的数据填充工作表。 If your recordset has more than the million-or-so rows, then you can write out the results to multiple worksheets. 如果记录集的行数超过百万左右,则可以将结果写到多个工作表中。

I also recommend using the GetRows function, and you will need to transpose it, because the GetRows array will be dimensioned as column then row, and Excel works best with row then column. 我还建议您使用GetRows函数,并且您需要对其进行转置,因为GetRows数组的大小将按列接行的方式确定,而Excel最适合于行接列的方式。

Also, given the size of your datasets, and assuming 32-bit Office, you won't be able to rely on Application.Worksheet.Transpose to do the transpose, as you'll probably run out of memory, and you may need to be careful with memory anyway, if doing the transpose yourself. 同样,在给定数据集大小的情况下,并假设使用32位Office,您将无法依靠Application.Worksheet.Transpose进行转置,因为这可能会耗尽内存,并且可能需要如果自己进行转置,无论如何都要小心存储。 Consider breaking the transposes and insertions up into batches. 考虑将转置和插入分为几批。

And finally, remember to do the insertions into the worksheet as ranges, as it will be much faster than cell-by-cell. 最后,请记住在做插入到工作表的范围,因为它会比细胞通过细胞快得多 eg: 例如:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM