【问题标题】:Optimize run speed on for next loops vba优化下一个循环的运行速度 vba
【发布时间】:2018-08-17 13:59:15
【问题描述】:

TLDR:我正在努力让这个宏运行得更快。

宏概览:刷新与订单数据库关联的查询。该查询在大约 9 秒内刷新了一个月的数据。过滤器用于仅复制所选 DateVal 中的值并将其复制到第一个报告页面(带有一些格式)。

接下来是它陷入困境的地方。将此averageifs() 公式应用于每一行需要一段时间。然后,它遍历每一行并删除可接受方差范围内的订单。这也需要一段时间。我尝试在查询本身内应用averageifs() 公式,但我不相信 MSQuery 可以进行该计算。因此,第一个循环大约需要 10 分钟,第二个循环每月需要另外 10 分钟的数据。

有什么想法可以优化下面的两个for i next i 循环吗?我真的很希望能够使用 2 个月的数据,但它会增加完成宏的时间。

这是完整的 vba:

Option Explicit
Public wb As Workbook
Public ws0, ws1, ws2 As Worksheet
Public i, t As Long
Public lRow, lCol As Long
Public DateVal, MinVar, MinMul, CritMul As Long

Sub ExceptionReport()
'This Macro is designed for call center to highlight potential keying errors.
'The criteria are fairly simple logic and are on the second tab (ws0).
'This backs up against a query file (.dqy).
'It uses the following tables: RMORHP, RMORDP, RMCUSP, and RMITMP.
'If any of these tables aren't getting fresh information, none of this wil work.
'General logic steps are to:
'(1) Refresh query
'(2) Copy query data to the report tab
'(3) Set up some qualifiers
'(4) Remove unqualified rows


'Some initial setup
Set wb = ThisWorkbook
Set ws0 = wb.Sheets("Setup")
Set ws1 = wb.Sheets("Report")
Set ws2 = wb.Sheets("Query")

'Store user filter criteria from setup tab
DateVal = ws0.Range("B4").Value
MinVar = ws0.Range("B5").Value
MinMul = ws0.Range("B6").Value
CritMul = ws0.Range("B7").Value

'In case someone messed with the refresh settings, this will delay macro until query refresh.
With wb.Connections("Order Table").ODBCConnection
    .BackgroundQuery = False
End With

'Refresh Query & request parameter input
wb.RefreshAll

'Completely rewrite the Report page in case someone messed it up
ws1.Activate
ws1.Cells.Delete shift:=xlUp
ws1.Range("A1:L1").Merge
ws1.Range("A1:L1").Font.Bold = True
ws1.Range("A2:L2").Merge
With ws1.Range("A1:L2")
    .HorizontalAlignment = xlCenter
    .VerticalAlignment = xlBottom
    .WrapText = False
    .Orientation = 0
    .AddIndent = False
    .IndentLevel = 0
    .ShrinkToFit = False
    .ReadingOrder = xlContext
End With

ws1.Range("A1:K1").Value = "Order Entry Exception Report"
ws1.Range("A2:K2").Value = "Exception Report " & Format(Now, "mm/dd/yy hh:Nn")
ws1.Range("K4").Value = "Avg Units Ordered"
ws1.Range("L4").Value = "Var From Avg"

'Find Out how large the Query dataset is
'We know the dataset is 10 columns
lCol = 10
lRow = ws2.Cells(Rows.Count, 1).End(xlUp).Row

'Copy Query (ws2) to Report (ws1) tab with a filter/unfilter on the query table
ws2.ListObjects("Table_Order_Table").Range.AutoFilter Field:=2, Criteria1:=DateVal
ws2.Range(ws2.Cells(1, 1), ws2.Cells(lRow, lCol)).SpecialCells(xlCellTypeVisible).Copy
ws1.Range(ws1.Cells(4, 1), ws1.Cells(lRow + 3, lCol)).PasteSpecial xlPasteValues
ws2.ListObjects("Table_Order_Table").Range.AutoFilter Field:=2

'Remove all rows that don't have specified date
'The above filter/unfilter make these lines obsolete. Improved runtime by about 15min per month of data.
'For i = lRow + 3 To 5 Step -1
'    If ws1.Range("B" & i).Value <> DateVal Then ws1.Range("B" & i).EntireRow.Delete
'Next i

'Relalc lRow
lRow = ws1.Cells(Rows.Count, 1).End(xlUp).Row

'Add the average units per order and variance amount
For i = 5 To lRow
    ws1.Range("K" & i).Formula = "=+IFERROR(ROUND(AVERAGEIFS(Table_Order_Table[Units Ordered],Table_Order_Table[Product],Report!H" & i & ",Table_Order_Table[Customer Number],Report!D" & i & ",Table_Order_Table[Order Number],""<>""&Report!A" & i & "),0),0)"
    ws1.Range("L" & i).Formula = "=abs(K" & i & "-J" & i & ")"
Next i

'Copy/Paste to improve calculation speed by removing formulas
ws1.Range(ws1.Cells(5, 11), ws1.Cells(lRow, 12)).Copy
ws1.Range(ws1.Cells(5, 11), ws1.Cells(lRow, 12)).PasteSpecial xlPasteValues

'Remove rows that aren't outside acceptable variance
For i = lRow To 5 Step -1
    If ws1.Range("L" & i) < MinVar Or ws1.Range("K" & i).Value * MinMul >= ws1.Range("J" & i).Value Then ws1.Range("L" & i).EntireRow.Delete
Next i

'Delete rows in the Query to make the file smaller
lRow = ws2.Cells(Rows.Count, 1).End(xlUp).Row
ws2.Range(ws2.Cells(2, 1), ws2.Cells(lRow, lCol)).EntireRow.Delete

'Some more formatting
ws1.Activate
ActiveWindow.Zoom = 100
ws1.Cells.EntireColumn.AutoFit
ws1.Range("A4:L4").Font.Bold = True
ws1.Range("A4:L4").Font.Underline = xlUnderlineStyleSingle
ws1.Range("A1").Select

End Sub

这是 SQL 查询:

XLODBC 1 DRIVER=SQL Server;

SERVER=*OMIT*;

UID=*OMIT*;

Trusted_Connection=Yes;

APP=Microsoft Office 2010;

WSID=*OMIT*;

DATABASE=*OMIT*
SELECT DISTINCT RMORHP.ORHORDNUM AS 'Order Number',
                RMORHP.ORHCRTDTE AS 'Order Create Date',
                RMORHP.ORHCRTUSR AS 'Created By',
                CONCAT(RMORHP.ORHCUSCHN, '-', RMORHP.ORHCUSNUM) AS 'Customer Number',
                RMORHP.ORHCUSCHN AS 'Chain ID',
                RMORHP.ORHCUSNUM AS 'Cust ID',
                RMCUSP.CUSCUSNAM AS 'Customer Name',
                RMORDP.ORDITMNUM AS 'Product',
                RMITMP.ITMLNGDES AS 'Product Name',
                RMORDP.ORDADJQTY AS 'Units Ordered'
FROM *OMIT*.RMORHP RMORHP,
     *OMIT*.RMCUSP RMCUSP,
     *OMIT*.RMORDP RMORDP,
     *OMIT*.RMITMP RMITMP
WHERE (RMORHP.ORHCRTDTE BETWEEN ? AND ?)
  AND RMCUSP.CUSCUSCHN = RMORHP.ORHCUSCHN
  AND RMCUSP.CUSCUSNUM = RMORHP.ORHCUSNUM
  AND RMORHP.ORHORDNUM = RMORDP.ORDORDNUM
  AND RMORDP.ORDITMNUM = RMITMP.ITMITMNUM
  AND RMCUSP.CUSDFTDCN = 505 enter
  START date "yyyymmdd" enter END date "yyyymmdd"

【问题讨论】:

    标签: excel vba for-loop ms-query


    【解决方案1】:

    我修改了代码,还没测试!希望能帮助到你。只需禁用更新,然后启用:

    Option Explicit
    Public wb As Workbook
    Public ws0, ws1, ws2 As Worksheet
    Public i, t As Long
    Public lRow, lCol As Long
    Public DateVal, MinVar, MinMul, CritMul As Long
    
    Sub ExceptionReport()
    'This Macro is designed for call center to highlight potential keying errors.
    'The criteria are fairly simple logic and are on the second tab (ws0).
    'This backs up against a query file (.dqy).
    'It uses the following tables: RMORHP, RMORDP, RMCUSP, and RMITMP.
    'If any of these tables aren't getting fresh information, none of this wil work.
    'General logic steps are to:
    '(1) Refresh query
    '(2) Copy query data to the report tab
    '(3) Set up some qualifiers
    '(4) Remove unqualified rows
    
    
    'Some initial setup
    Set wb = ThisWorkbook
    Set ws0 = wb.Sheets("Setup")
    Set ws1 = wb.Sheets("Report")
    Set ws2 = wb.Sheets("Query")
    
    'Store user filter criteria from setup tab
    DateVal = ws0.Range("B4").Value
    MinVar = ws0.Range("B5").Value
    MinMul = ws0.Range("B6").Value
    CritMul = ws0.Range("B7").Value
    
    'In case someone messed with the refresh settings, this will delay macro until query refresh.
    With wb.Connections("Order Table").ODBCConnection
        .BackgroundQuery = False
    End With
    
    'Refresh Query & request parameter input
    wb.RefreshAll
    
    'Completely rewrite the Report page in case someone messed it up
    ws1.Activate
    ws1.Cells.Delete shift:=xlUp
    ws1.Range("A1:L1").Merge
    ws1.Range("A1:L1").Font.Bold = True
    ws1.Range("A2:L2").Merge
    With ws1.Range("A1:L2")
        .HorizontalAlignment = xlCenter
        .VerticalAlignment = xlBottom
        .WrapText = False
        .Orientation = 0
        .AddIndent = False
        .IndentLevel = 0
        .ShrinkToFit = False
        .ReadingOrder = xlContext
    End With
    
    ws1.Range("A1:K1").Value = "Order Entry Exception Report"
    ws1.Range("A2:K2").Value = "Exception Report " & Format(Now, "mm/dd/yy hh:Nn")
    ws1.Range("K4").Value = "Avg Units Ordered"
    ws1.Range("L4").Value = "Var From Avg"
    
    'Find Out how large the Query dataset is
    'We know the dataset is 10 columns
    lCol = 10
    lRow = ws2.Cells(Rows.Count, 1).End(xlUp).Row
    
    'Copy Query (ws2) to Report (ws1) tab with a filter/unfilter on the query table
    ws2.ListObjects("Table_Order_Table").Range.AutoFilter Field:=2, Criteria1:=DateVal
    ws2.Range(ws2.Cells(1, 1), ws2.Cells(lRow, lCol)).SpecialCells(xlCellTypeVisible).Copy
    ws1.Range(ws1.Cells(4, 1), ws1.Cells(lRow + 3, lCol)).PasteSpecial xlPasteValues
    ws2.ListObjects("Table_Order_Table").Range.AutoFilter Field:=2
    
    'Remove all rows that don't have specified date
    'The above filter/unfilter make these lines obsolete. Improved runtime by about 15min per month of data.
    'For i = lRow + 3 To 5 Step -1
    '    If ws1.Range("B" & i).Value <> DateVal Then ws1.Range("B" & i).EntireRow.Delete
    'Next i
    
    'Relalc lRow
    lRow = ws1.Cells(Rows.Count, 1).End(xlUp).Row
    
    'DISABLE ALL
    
    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual
    Application.EnableEvents = False
    
    'Add the average units per order and variance amount
    For i = 5 To lRow
        ws1.Range("K" & i).Formula = "=+IFERROR(ROUND(AVERAGEIFS(Table_Order_Table[Units Ordered],Table_Order_Table[Product],Report!H" & i & ",Table_Order_Table[Customer Number],Report!D" & i & ",Table_Order_Table[Order Number],""<>""&Report!A" & i & "),0),0)"
        ws1.Range("L" & i).Formula = "=abs(K" & i & "-J" & i & ")"
    Next i
    
    'Copy/Paste to improve calculation speed by removing formulas
    ws1.Range(ws1.Cells(5, 11), ws1.Cells(lRow, 12)).Copy
    ws1.Range(ws1.Cells(5, 11), ws1.Cells(lRow, 12)).PasteSpecial xlPasteValues
    
    'Remove rows that aren't outside acceptable variance
    For i = lRow To 5 Step -1
        If ws1.Range("L" & i) < MinVar Or ws1.Range("K" & i).Value * MinMul >= ws1.Range("J" & i).Value Then ws1.Range("L" & i).EntireRow.Delete
    Next i
    
    'Delete rows in the Query to make the file smaller
    lRow = ws2.Cells(Rows.Count, 1).End(xlUp).Row
    ws2.Range(ws2.Cells(2, 1), ws2.Cells(lRow, lCol)).EntireRow.Delete
    
    'ENABLE
    Application.ScreenUpdating = True
    Application.Calculation = xlCalculationAutomatic
    Application.EnableEvents = True
    
    'Some more formatting
    ws1.Activate
    ActiveWindow.Zoom = 100
    ws1.Cells.EntireColumn.AutoFit
    ws1.Range("A4:L4").Font.Bold = True
    ws1.Range("A4:L4").Font.Underline = xlUnderlineStyleSingle
    ws1.Range("A1").Select
    
    End Sub
    

    【讨论】:

      猜你喜欢
      • 2020-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-02
      • 1970-01-01
      • 2022-01-26
      • 1970-01-01
      相关资源
      最近更新 更多