Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Вивчайте Ефективна обробка даних | Робота з Даними в Excel
Excel VBA для автоматизації бізнесу

Ефективна обробка даних

Свайпніть щоб показати меню

До цього моменту всі операції виконувалися з однією клітинкою за раз — це прийнятно для п’яти замовлень, але надзвичайно повільно для п’ятдесяти тисяч. Рішення полягає в тому, щоб припинити працювати з аркушем по клітинці, а замість цього перемістити весь блок у масив у пам’яті, обробити його там і записати назад за один раз.

Зчитування діапазону в масив

Dim dataArr As Variant
dataArr = ws.Range("A2:I6").Value    ' one read, not 45 individual reads

dataArr тепер є двовимірним масивом у пам’яті: dataArr(1,1) — це ORD1001, dataArr(1,4) — це "Laptop Stand" і так далі — масиви VBA, зчитані з діапазону, мають індексацію з 1, а не з 0, що часто призводить до помилок.

Обробка масиву

Використовуються звичайні цикли — але тепер ви перебираєте дані в пам’яті, що у тисячі разів швидше, ніж по аркушу:

Dim i As Long
Dim recalculated As Double
 
For i = 1 To UBound(dataArr, 1)
    recalculated = dataArr(i, 5) * dataArr(i, 6) * (1 - dataArr(i, 7))
    If Abs(recalculated - dataArr(i, 8)) > 0.01 Then
        Debug.Print "Mismatch on row " & i & ": sheet says " & dataArr(i, 8) & _
                    ", recalculated " & Format(recalculated, "0.00")
    End If
Next i

Запис масиву назад

Знову одна операція:

ws.Range("A2:I6").Value = dataArr

Уникнення Select та Activate

Записані макроси часто використовують ці команди (Range("A1").Select, потім Selection.Font.Bold = True), але вони непотрібні та уповільнюють виконання — кожен .Select змушує Excel перемальовувати екран. Звертайтеся до діапазону або об'єкта напряму:

' Avoid:
ws.Range("A1").Select
Selection.Font.Bold = True
 
' Prefer:
ws.Range("A1").Font.Bold = True

Міркування щодо продуктивності

Це стає важливим, коли даних більше кількох сотень рядків:

Application.ScreenUpdating = False   ' stop redrawing while the macro runs
Application.Calculation = xlCalculationManual   ' pause recalculation
Application.EnableEvents = False     ' suppress other macros triggering mid-run
 
' ... your fast array-based code here ...
 
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Application.EnableEvents = True

Завжди відновлюйте ці налаштування наприкінці — і обгорніть відновлення в обробку помилок, щоб у разі збою Excel не залишився з вимкненим оновленням екрана.

Практичний приклад: перевірка цілісності замовлень

У цьому розділі ми поступово підводили до такого прикладу:

Option Explicit
 
Sub VerifyOrderTotals()
    Dim ws As Worksheet
    Dim dataArr As Variant
    Dim lastRow As Long
    Dim i As Long
    Dim recalculated As Double
    Dim issues As String
 
    Set ws = ThisWorkbook.Worksheets("Orders")
    lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
 
    Application.ScreenUpdating = False
 
    dataArr = ws.Range("A2:I" & lastRow).Value
    issues = ""
 
    For i = 1 To UBound(dataArr, 1)
        recalculated = dataArr(i, 5) * dataArr(i, 6) * (1 - dataArr(i, 7))
        If Abs(recalculated - dataArr(i, 8)) > 0.01 Then
            issues = issues & dataArr(i, 1) & ": sheet=" & dataArr(i, 8) & _
                      ", expected=" & Format(recalculated, "0.00") & vbNewLine
        End If
    Next i
 
    Application.ScreenUpdating = True
 
    If issues = "" Then
        MsgBox "All " & UBound(dataArr, 1) & " order totals check out."
    Else
        MsgBox "Discrepancies found:" & vbNewLine & issues
    End If
End Sub

Запустіть цей макрос на прикладній таблиці — він має показати, що всі п’ять підсумків правильні. Спробуйте змінити Total для ORD1002 на неправильне значення та перезапустіть макрос, щоб побачити сповіщення про помилку.

Завдання

  1. Скопіюйте VerifyOrderTotals у свою книгу та запустіть його для п’яти зразків замовлень.
  2. Вручну змініть Total для одного із замовлень на аркуші (введіть явно неправильне число) та перезапустіть — переконайтеся, що невідповідність відображається з правильним Order ID.
  3. Додайте ще десять рядків вигаданих замовлень під рядком 6 і переконайтеся, що макрос працює без змін у коді — це результат використання lastRow та обробки масивів.
Помічник
expand arrow

Додатковий помічник, якщо ви бажаєте згенерувати десять додаткових рядків за допомогою коду, а не вводити їх вручну:

Sub AddTestOrders()
    Dim ws As Worksheet
    Dim i As Long
    Dim r As Long

    Set ws = ThisWorkbook.Worksheets("Orders")
    r = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row + 1

    For i = 1 To 10
        ws.Cells(r, 1).Value = "ORD" & (1005 + i)
        ws.Cells(r, 2).Value = Date
        ws.Cells(r, 3).Value = "Test Customer " & i
        ws.Cells(r, 4).Value = "Sample Product"
        ws.Cells(r, 5).Value = i
        ws.Cells(r, 6).Value = 20 + i
        ws.Cells(r, 7).Value = 0.05
        ws.Cells(r, 8).Value = i * (20 + i) * (1 - 0.05)
        ws.Cells(r, 9).Value = "Pending"
        r = r + 1
    Next i
End Sub
Підказка
expand arrow

1. Копіювання VerifyOrderTotals

  • Введіть процедуру Sub точно так, як показано в розділі 3.5 у модулі вашої книги — змінювати нічого не потрібно.
  • Запустіть її один раз для п’яти початкових рядків і переконайтеся, що ви отримали повідомлення "All 5 order totals check out."

2. Навмисне порушення одного Total

  • Виберіть будь-яку клітинку Total замовлення та введіть явно неправильне число безпосередньо у робочий аркуш (не через код).
  • Повторно запустіть ту ж процедуру Sub — повідомлення про невідповідність повинно вказати саме цей Order ID, а також значення з аркуша та те, що макрос перерахував.
  • Поверніть клітинку до правильного значення, якщо хочете мати чистий аркуш для наступного кроку.

3. Додавання ще десяти рядків

  • Просто введіть нові дані замовлень безпосередньо у рядки 7–16 — ті ж дев’ять стовпців, будь-які правдоподібні значення.
  • Макрос не змінюйте. lastRow обчислюється щоразу, коли запускається Sub, а зчитування масиву (ws.Range("A2:I" & lastRow).Value) автоматично розширюється — саме це і демонструється.
Розв'язок
expand arrow
Option Explicit

Sub VerifyOrderTotals()
    Dim ws As Worksheet
    Dim dataArr As Variant
    Dim lastRow As Long
    Dim i As Long
    Dim recalculated As Double
    Dim issues As String

    Set ws = ThisWorkbook.Worksheets("Orders")
    lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row

    Application.ScreenUpdating = False

    dataArr = ws.Range("A2:I" & lastRow).Value
    issues = ""

    For i = 1 To UBound(dataArr, 1)
        recalculated = dataArr(i, 5) * dataArr(i, 6) * (1 - dataArr(i, 7))
        If Abs(recalculated - dataArr(i, 8)) > 0.01 Then
            issues = issues & dataArr(i, 1) & ": sheet=" & dataArr(i, 8) & _
                      ", expected=" & Format(recalculated, "0.00") & vbNewLine
        End If
    Next i

    Application.ScreenUpdating = True

    If issues = "" Then
        MsgBox "All " & UBound(dataArr, 1) & " order totals check out."
    Else
        MsgBox "Discrepancies found:" & vbNewLine & issues
    End If
End Sub
Все було зрозуміло?

Як ми можемо покращити це?

Дякуємо за ваш відгук!

Секція 3. Розділ 5

Запитати АІ

expand

Запитати АІ

ChatGPT

Запитайте про що завгодно або спробуйте одне із запропонованих запитань, щоб почати наш чат

Ефективна обробка даних

До цього моменту всі операції виконувалися з однією клітинкою за раз — це прийнятно для п’яти замовлень, але надзвичайно повільно для п’ятдесяти тисяч. Рішення полягає в тому, щоб припинити працювати з аркушем по клітинці, а замість цього перемістити весь блок у масив у пам’яті, обробити його там і записати назад за один раз.

Зчитування діапазону в масив

Dim dataArr As Variant
dataArr = ws.Range("A2:I6").Value    ' one read, not 45 individual reads

dataArr тепер є двовимірним масивом у пам’яті: dataArr(1,1) — це ORD1001, dataArr(1,4) — це "Laptop Stand" і так далі — масиви VBA, зчитані з діапазону, мають індексацію з 1, а не з 0, що часто призводить до помилок.

Обробка масиву

Використовуються звичайні цикли — але тепер ви перебираєте дані в пам’яті, що у тисячі разів швидше, ніж по аркушу:

Dim i As Long
Dim recalculated As Double
 
For i = 1 To UBound(dataArr, 1)
    recalculated = dataArr(i, 5) * dataArr(i, 6) * (1 - dataArr(i, 7))
    If Abs(recalculated - dataArr(i, 8)) > 0.01 Then
        Debug.Print "Mismatch on row " & i & ": sheet says " & dataArr(i, 8) & _
                    ", recalculated " & Format(recalculated, "0.00")
    End If
Next i

Запис масиву назад

Знову одна операція:

ws.Range("A2:I6").Value = dataArr

Уникнення Select та Activate

Записані макроси часто використовують ці команди (Range("A1").Select, потім Selection.Font.Bold = True), але вони непотрібні та уповільнюють виконання — кожен .Select змушує Excel перемальовувати екран. Звертайтеся до діапазону або об'єкта напряму:

' Avoid:
ws.Range("A1").Select
Selection.Font.Bold = True
 
' Prefer:
ws.Range("A1").Font.Bold = True

Міркування щодо продуктивності

Це стає важливим, коли даних більше кількох сотень рядків:

Application.ScreenUpdating = False   ' stop redrawing while the macro runs
Application.Calculation = xlCalculationManual   ' pause recalculation
Application.EnableEvents = False     ' suppress other macros triggering mid-run
 
' ... your fast array-based code here ...
 
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Application.EnableEvents = True

Завжди відновлюйте ці налаштування наприкінці — і обгорніть відновлення в обробку помилок, щоб у разі збою Excel не залишився з вимкненим оновленням екрана.

Практичний приклад: перевірка цілісності замовлень

У цьому розділі ми поступово підводили до такого прикладу:

Option Explicit
 
Sub VerifyOrderTotals()
    Dim ws As Worksheet
    Dim dataArr As Variant
    Dim lastRow As Long
    Dim i As Long
    Dim recalculated As Double
    Dim issues As String
 
    Set ws = ThisWorkbook.Worksheets("Orders")
    lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
 
    Application.ScreenUpdating = False
 
    dataArr = ws.Range("A2:I" & lastRow).Value
    issues = ""
 
    For i = 1 To UBound(dataArr, 1)
        recalculated = dataArr(i, 5) * dataArr(i, 6) * (1 - dataArr(i, 7))
        If Abs(recalculated - dataArr(i, 8)) > 0.01 Then
            issues = issues & dataArr(i, 1) & ": sheet=" & dataArr(i, 8) & _
                      ", expected=" & Format(recalculated, "0.00") & vbNewLine
        End If
    Next i
 
    Application.ScreenUpdating = True
 
    If issues = "" Then
        MsgBox "All " & UBound(dataArr, 1) & " order totals check out."
    Else
        MsgBox "Discrepancies found:" & vbNewLine & issues
    End If
End Sub

Запустіть цей макрос на прикладній таблиці — він має показати, що всі п’ять підсумків правильні. Спробуйте змінити Total для ORD1002 на неправильне значення та перезапустіть макрос, щоб побачити сповіщення про помилку.

Завдання

  1. Скопіюйте VerifyOrderTotals у свою книгу та запустіть його для п’яти зразків замовлень.
  2. Вручну змініть Total для одного із замовлень на аркуші (введіть явно неправильне число) та перезапустіть — переконайтеся, що невідповідність відображається з правильним Order ID.
  3. Додайте ще десять рядків вигаданих замовлень під рядком 6 і переконайтеся, що макрос працює без змін у коді — це результат використання lastRow та обробки масивів.
Помічник
expand arrow

Додатковий помічник, якщо ви бажаєте згенерувати десять додаткових рядків за допомогою коду, а не вводити їх вручну:

Sub AddTestOrders()
    Dim ws As Worksheet
    Dim i As Long
    Dim r As Long

    Set ws = ThisWorkbook.Worksheets("Orders")
    r = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row + 1

    For i = 1 To 10
        ws.Cells(r, 1).Value = "ORD" & (1005 + i)
        ws.Cells(r, 2).Value = Date
        ws.Cells(r, 3).Value = "Test Customer " & i
        ws.Cells(r, 4).Value = "Sample Product"
        ws.Cells(r, 5).Value = i
        ws.Cells(r, 6).Value = 20 + i
        ws.Cells(r, 7).Value = 0.05
        ws.Cells(r, 8).Value = i * (20 + i) * (1 - 0.05)
        ws.Cells(r, 9).Value = "Pending"
        r = r + 1
    Next i
End Sub
Підказка
expand arrow

1. Копіювання VerifyOrderTotals

  • Введіть процедуру Sub точно так, як показано в розділі 3.5 у модулі вашої книги — змінювати нічого не потрібно.
  • Запустіть її один раз для п’яти початкових рядків і переконайтеся, що ви отримали повідомлення "All 5 order totals check out."

2. Навмисне порушення одного Total

  • Виберіть будь-яку клітинку Total замовлення та введіть явно неправильне число безпосередньо у робочий аркуш (не через код).
  • Повторно запустіть ту ж процедуру Sub — повідомлення про невідповідність повинно вказати саме цей Order ID, а також значення з аркуша та те, що макрос перерахував.
  • Поверніть клітинку до правильного значення, якщо хочете мати чистий аркуш для наступного кроку.

3. Додавання ще десяти рядків

  • Просто введіть нові дані замовлень безпосередньо у рядки 7–16 — ті ж дев’ять стовпців, будь-які правдоподібні значення.
  • Макрос не змінюйте. lastRow обчислюється щоразу, коли запускається Sub, а зчитування масиву (ws.Range("A2:I" & lastRow).Value) автоматично розширюється — саме це і демонструється.
Розв'язок
expand arrow
Option Explicit

Sub VerifyOrderTotals()
    Dim ws As Worksheet
    Dim dataArr As Variant
    Dim lastRow As Long
    Dim i As Long
    Dim recalculated As Double
    Dim issues As String

    Set ws = ThisWorkbook.Worksheets("Orders")
    lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row

    Application.ScreenUpdating = False

    dataArr = ws.Range("A2:I" & lastRow).Value
    issues = ""

    For i = 1 To UBound(dataArr, 1)
        recalculated = dataArr(i, 5) * dataArr(i, 6) * (1 - dataArr(i, 7))
        If Abs(recalculated - dataArr(i, 8)) > 0.01 Then
            issues = issues & dataArr(i, 1) & ": sheet=" & dataArr(i, 8) & _
                      ", expected=" & Format(recalculated, "0.00") & vbNewLine
        End If
    Next i

    Application.ScreenUpdating = True

    If issues = "" Then
        MsgBox "All " & UBound(dataArr, 1) & " order totals check out."
    Else
        MsgBox "Discrepancies found:" & vbNewLine & issues
    End If
End Sub
Все було зрозуміло?

Як ми можемо покращити це?

Дякуємо за ваш відгук!

Секція 3. Розділ 5
some-alt