【发布时间】:2018-06-19 16:01:57
【问题描述】:
我正在尝试在 Excel 工作表上执行查询,就像我做过很多次一样,但现在数据有超过 70k 行。通常,如果是这种情况,我会收到消息说它找不到表,这是可以预料的,因为我认为它在大约 65k 行左右停止工作。
所以,我正在尝试做一个循环,在循环的第一部分我运行前 60k 行,并且在循环的每次迭代中,它会执行另一批 60k,直到它完成最后一组。该循环创建一个包含要处理的数据的新工作表,因此我可以将列标题与数据集一起使用。它似乎一直工作到它对新工作表中的数据运行新查询的部分。它给了我“Microsoft Access 数据库引擎找不到对象”(我的表名)...等错误。
对于我的具体示例,该表是“Sheet1$A1:N12790”,其中 12790 是超过 70k 行工作表的剩余行数,Sheet1 是运行代码时创建的工作表。
所以,我完全不知道为什么它会给出这个错误,而它通常只在行太多或表肯定不存在时才会出现。
我尝试使用单独的子程序运行一个简单的Select * from [Sheet1$A1:N12790],它运行良好。这让我相信,也许在做第一个之后,excel可能内存不足?但我不知道该怎么做,而且网上关于这个的信息很少,因为它是如此具体和罕见,因为大多数人此时只是使用常规数据库。
谢谢!
更新:我一直在测试很多东西。我已经尝试创建一个测试子来处理新工作表(如上所述),它在单独运行时可以工作,但是如果我尝试强制主子尽快退出循环,然后调用新的测试子来运行我想要的这样做,它给了我同样的错误。再说一次,两个潜艇完美地分开运行,但我不能用一个来调用另一个。向我展示了更多证据表明它与编码无关,而更多地与某种处理复杂性有关,但我仍然只是提出理论。
更新 2:感谢您迄今为止(2018 年 6 月 20 日)提出的所有想法和建议。这是第二次运行并尝试运行 MySQL 时出现的错误内容的屏幕截图:
错误信息:
如果有帮助,下面是我的代码:
Sub Risk_Init_Pivot(FA_PQ, Risk_Init, SubChannel, MyMonth As String)
Application.ScreenUpdating = False
Dim SheetRange1 As Range, SheetRange2 As Range, SheetRange3 As Range, MyRange As Range
Dim TargetSheetTable As String, SheetTable1 As String
Dim SR1_LastRow As Double, SR1_LastColumn As Double, NewRowCount As Double, SR1_FirstRow As Double
Dim i As Integer, j As Integer, MyLoop As Integer
Dim Table1 As String, MySQL As String
Dim MySheet1 As Worksheet, MySheet2 As Worksheet
Dim MyConn As ADODB.Connection
Dim MyRecordSet As ADODB.Recordset
TargetSheetTable = "Risk Init Pivot"
SheetTable1 = "Fanned File"
'Initiate
ActiveWorkbook.Sheets(TargetSheetTable).Activate
If ActiveSheet.AutoFilterMode Then
If ActiveSheet.FilterMode Then ActiveSheet.ShowAllData
End If
ActiveSheet.Cells.ClearContents
'Find Range Coordinates Dynamically
ActiveWorkbook.Sheets(SheetTable1).Activate
If ActiveSheet.AutoFilterMode Then
If ActiveSheet.FilterMode Then ActiveSheet.ShowAllData
End If
Range("A1").Select
Selection.End(xlDown).Select
SR1_LastRow = Selection.Row
ActiveCell.SpecialCells(xlLastCell).Select
SR1_LastColumn = Selection.Column
Range("A1").Select
MyLoop = WorksheetFunction.RoundUp(SR1_LastRow / 60000, 0)
NewRowCount = 0
For j = 1 To MyLoop
'Set Up Connection Details
Set MyConn = New ADODB.Connection
MyConn.CommandTimeout = 0
Set MyRecordSet = New ADODB.Recordset
MyConn.Open "Provider = Microsoft.ACE.OLEDB.12.0;" & _
"Data Source = " & Application.ThisWorkbook.FullName & ";" & _
"Extended Properties = ""Excel 12.0 Xml;HDR=YES;IMEX=1"";"
Set MyRecordSet.ActiveConnection = MyConn
'First Time
If SR1_LastRow > 60000 Then
NewRowCount = SR1_LastRow - 60000
SR1_LastRow = 60000
SR1_FirstRow = 1
'Set the tables equal to the respective ranges
Set SheetRange1 = ActiveWorkbook.Sheets(SheetTable1).Range("A" & SR1_FirstRow & ":" & Cells(SR1_LastRow, SR1_LastColumn).Address)
'Pass the table address to a string
Table1 = SheetRange1.Address
'Convert the string into a query table - have to get rid of dollar signs for it to work
Table1 = "[" & SheetTable1 & "$" & Replace(Table1, "$", "") & "]"
'Does this until NewRowCount falls into last time
ElseIf NewRowCount > 60000 Then
NewRowCount = NewRowCount - 60000
SR1_FirstRow = SR1_LastRow + 1
SR1_LastRow = SR1_LastRow + 60000
Set MySheet1 = Sheets(SheetTable1)
Sheets.Add After:=MySheet1
Set MySheet2 = ActiveSheet
MySheet1.Activate
Rows("1:1").Select
Selection.Copy
MySheet2.Activate
Rows("1:1").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
MySheet1.Activate
ActiveSheet.Range("A" & SR1_FirstRow & ":" & Cells(SR1_LastRow, SR1_LastColumn).Address).Copy
MySheet2.Activate
ActiveSheet.Range("A2").PasteSpecial xlPasteValues
Range("A1").Select
Range(Selection, Selection.End(xlDown)).Select
Range(Selection, Selection.End(xlToRight)).Select
Set MyRange = Selection
'Set the tables equal to the respective ranges
Table1 = Selection.Address
'Convert the string into a query table - have to get rid of dollar signs for it to work
Table1 = "[" & MySheet2.Name & "$" & Replace(Table1, "$", "") & "]"
'Last Time
ElseIf (NewRowCount > 0) And (NewRowCount <= 60000) Then
SR1_FirstRow = SR1_LastRow + 1
SR1_LastRow = SR1_LastRow + NewRowCount
NewRowCount = 0
Set MySheet1 = Sheets(SheetTable1)
Sheets.Add After:=MySheet1
Set MySheet2 = ActiveSheet
MySheet1.Activate
Rows("1:1").Select
Selection.Copy
MySheet2.Activate
Rows("1:1").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
MySheet1.Activate
ActiveSheet.Range("A" & SR1_FirstRow & ":" & Cells(SR1_LastRow, SR1_LastColumn).Address).Copy
MySheet2.Activate
ActiveSheet.Range("A2").PasteSpecial xlPasteValues
Range("A1").Select
Range(Selection, Selection.End(xlDown)).Select
Range(Selection, Selection.End(xlToRight)).Select
'Set the tables equal to the respective ranges
Table1 = Selection.Address
'Convert the string into a query table - have to get rid of dollar signs for it to work
Table1 = "[" & MySheet2.Name & "$" & Replace(Table1, "$", "") & "]"
'Does this the first time if under 60k rows
Else
SR1_FirstRow = 1
'Set the tables equal to the respective ranges
Set SheetRange1 = ActiveWorkbook.Sheets(SheetTable1).Range("A" & SR1_FirstRow & ":" & Cells(SR1_LastRow, SR1_LastColumn).Address)
'Pass the table address to a string
Table1 = SheetRange1.Address
'Convert the string into a query table - have to get rid of dollar signs for it to work
Table1 = "[" & SheetTable1 & "$" & Replace(Table1, "$", "") & "]"
End If
'SQL Statement
MySQL = Sheets("Control Sheet").Range("C14").Value
MySQL = Replace(MySQL, "@Table1", Table1)
MySQL = Replace(MySQL, "@Year", Sheets("Control Sheet").Range("C5").Value)
MySQL = Replace(MySQL, "@FA_PQ_Input", FA_PQ)
MySQL = Replace(MySQL, "@SubChannel", SubChannel)
MySQL = Replace(MySQL, "@MyMonth", MyMonth)
MsgBox MySQL
'Run SQL
MyRecordSet.Open MySQL, MyConn, adOpenKeyset, adLockOptimistic
'Paste Data with headers to location
ActiveWorkbook.Sheets(TargetSheetTable).Activate
ActiveSheet.Range("A" & 1 + SR1_FirstRow).CopyFromRecordset MyRecordSet
For i = 0 To MyRecordSet.Fields.Count - 1
ActiveSheet.Cells(1, i + 1) = MyRecordSet.Fields(i).Name
With ActiveSheet.Cells(1, i + 1)
.Font.Bold = True
.Font.Size = 10
End With
Next i
MyRecordSet.Close
Set MyRecordSet = Nothing
MyConn.Close
Set MyConn = Nothing
Next j
''Putting Nulls in the blanks
'ActiveSheet.Cells.Replace What:="", Replacement:="NULL", LookAt:=xlWhole, SearchOrder:=xlByRows, MatchCase:=True, _
' SearchFormat:=False, ReplaceFormat:=False
'Tidying the sheet
ActiveSheet.Cells.AutoFilter
ActiveSheet.Columns.AutoFit
ActiveSheet.Range("A1").Select
Sheets("Control Sheet").Activate
Application.ScreenUpdating = True
End Sub
【问题讨论】:
-
首先突出的是您将变量声明为整数,我很确定这些应该声明为 Long,因为整数不能保存大于 32k 左右的值...
-
@Xabier 我明白你的意思,但是如果你仔细观察,你会发现设置为整数的变量并没有持有超大的值。事实上,那些被声明为 double 并且在我所有其他查询中似乎都做得很好的那些。就像我在上面的帖子中试图提到的那样,当工作表的行数少于 65k 左右时,这非常有效......
-
为了避免XY Problem 和通过大量代码进行挖掘,请向我们提供输入和所需输出的数据样本的完整背景。
-
另外,考虑使用一个实际的数据库。请注意:Excel is not a database。是的,您可以使用 MS Access(即它的引擎),尽管您可能已经安装了 .exe 程序(实际上只是引擎的 GUI 控制台)。所以你可以create and use Access databases。
-
VBA 肯定在代码模块中而不是代码工作表中?
标签: sql vba excel ms-jet-ace