VBAでフィルターを使った行削除の方法と配列を使った行削除のスピード検証

こんにちは!大阪市を拠点に活動している『縁紡ぐ』の稲垣です。

SWELLを使ったホームページ制作や、Excel、ACCESS、RPAなどのシステム開発を行っています。
また、Excel、Word、Outlookの研修や、情報セキュリティ研修も行っています。身近なITの相談相手になりたいと思っています。

お気軽にお問い合わせください。

目次

VBAでフィルターを使った行削除の方法と配列を使った行削除のスピード検証

業務効率化を目指しているあなたに、VBAを使ったフィルター機能の活用方法を紹介します。特に、フィルターを使って行を削除する方法も紹介しながら、初心者でも分かりやすく解説したいと思います。これを読めば、データ処理がぐっと楽になりますよ。


フィルターの基本

フィルター機能を使うと、大量のデータから特定の条件にあった行だけを表示することができます。これを利用して、不要な行を削除することができます。

フィルタのかけ方

まずは、VBAではなく、手動でフィルタをかける方法を確認しておきましょう。

データタブ⇒フィルタをクリックすると、選択している行にフィルタをかけることができます。

クリックすると、フィルタがかかり、▼マークをクリックすると条件を指定してフィルタをかけることができます。

より細かな条件設定をすることも可能です。

条件に一致する行を他のシートに貼り付ける時間を検証

では、フィルター機能を使って処理した場合どのくらい時間がかかるか、検証してみましょう!

まずは、テストデータを90万行を作成しました。

配列を使って処理

高速化するといえば、やはり配列ですよね!

Sub CopyRowsToNewSheet()
    Dim wsSource As Worksheet
    Dim wsTarget As Worksheet
    Dim lastRow As Long
    Dim data As Variant
    Dim filteredData() As Variant
    Dim i As Long, targetIndex As Long

    ' タイマーを開始
    StopWatch.StartTimer

    ' 元データのシートを設定
    Set wsSource = ThisWorkbook.Worksheets("売上一覧") 

    ' 最終行を取得
    lastRow = wsSource.Cells(wsSource.Rows.Count, "A").End(xlUp).Row

    ' データを配列に読み込む
    data = wsSource.Range("A1:C" & lastRow).Value

    ' フィルタリング用の配列を初期化
    targetIndex = 0

    ' 条件に一致する行をチェックし、カウント
    For i = 1 To UBound(data, 1)
        If data(i, 3) >= 5000 Then ' C列の金額が5000円以上
            targetIndex = targetIndex + 1
        End If
    Next i

    ' フィルタリング用の配列を実際の行数に基づいて初期化
    If targetIndex > 0 Then
        ReDim filteredData(1 To targetIndex, 1 To 3)
        
        ' 再度ループしてフィルタリング
        targetIndex = 0
        For i = 1 To UBound(data, 1)
            If data(i, 3) >= 2000 Then ' C列の金額が5000円以上
                targetIndex = targetIndex + 1
                filteredData(targetIndex, 1) = data(i, 1) ' A列
                filteredData(targetIndex, 2) = data(i, 2) ' B列
                filteredData(targetIndex, 3) = data(i, 3) ' C列
            End If
        Next i
    Else
        MsgBox "条件に一致する行はありませんでした。"
        Exit Sub
    End If

    ' 新しいシートを作成
    Set wsTarget = ThisWorkbook.Worksheets.Add
    wsTarget.Name = "条件に一致する行"

    ' フィルタリングされたデータを新しいシートに貼り付け
    wsTarget.Range("A1:C" & targetIndex).Value = filteredData

    ' 列幅を自動調整
    wsTarget.Columns("A:C").AutoFit

    ' タイマーを停止
       StopWatch.StopTimer
    
    ' 経過時間を表示
    MsgBox StopWatch.GetFormattedElapsedTime()
End Sub

配列でのフィルタは10.83秒で処理が完了です!

配列はなかなか高速ですね!時間を測るためのストップウォッチの作成方法や、配列を使っての行削除は、下の記事でご紹介しています。

フィルタ使って処理

では、次はフィルタ機能を使って絞り込みした場合は、どれくらい時間がかかるでしょうか。

Option Explicit

Sub CopyFilteredRowsToNewSheet()
    Dim wsSource As Worksheet
    Dim wsTarget As Worksheet
    Dim lastRow As Long
    
    ' タイマーを開始
    StopWatch.StartTimer
    
    ' 元データのシートを設定
    Set wsSource = ThisWorkbook.Worksheets("売上一覧") ' 元データシート名を指定

    ' 最終行を取得
    lastRow = wsSource.Cells(wsSource.Rows.Count, "A").End(xlUp).Row

    ' フィルタを適用
    With wsSource
        .AutoFilterMode = False ' 既存のフィルタを解除
        .Range("A1:C" & lastRow).AutoFilter field:=3, Criteria1:=">=2000" ' C列をフィルタリング
    End With

    ' 新しいシートを作成
    Set wsTarget = ThisWorkbook.Worksheets.Add
    wsTarget.Name = "条件に一致する行"

    ' フィルタリングされたデータをコピー
    wsSource.Range("A1:C" & lastRow).SpecialCells(xlCellTypeVisible).Copy Destination:=wsTarget.Range("A1")

    ' フィルタを解除
    wsSource.AutoFilterMode = False

    ' 列幅を自動調整
    wsTarget.Columns("A:C").AutoFit

    ' タイマーを停止
       StopWatch.StopTimer
       
    ' 経過時間を表示
    MsgBox StopWatch.GetFormattedElapsedTime()
End Sub

148.65秒で処理が終了しました!ながーい!やはり、フィルタ機能は配列と同じぐらい、早く処理ができることがわかりましたね!

まとめ

条件に一致した行を他のシートに貼り付ける
配列:10.83秒
フィルタ:148.65秒

行削除の速度を検証

それでは、行削除だと処理時間の違いがでるのか検証してみましょう!

配列を使った行削除

Sub CopyRowsToSameSheet()
    Dim ws As Worksheet
    Dim lastRow As Long
    Dim data As Variant
    Dim filteredData() As Variant
    Dim i As Long, targetIndex As Long

    ' タイマーを開始
    StopWatch.StartTimer

    ' 元データのシートを設定
    Set ws = ThisWorkbook.Worksheets("売上一覧")

    ' 最終行を取得
    lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row

    ' データを配列に読み込む
    data = ws.Range("A1:C" & lastRow).Value

    ' フィルタリング用の配列を初期化
    targetIndex = 0

    ' 条件に一致する行をチェックし、カウント
    For i = 1 To UBound(data, 1)
        If data(i, 3) >= 2000 Then ' C列の金額が2,000円以上
            targetIndex = targetIndex + 1
        End If
    Next i

    ' フィルタリング用の配列を実際の行数に基づいて初期化
    If targetIndex > 0 Then
        ReDim filteredData(1 To targetIndex, 1 To 3)
        
        ' 再度ループしてフィルタリング
        targetIndex = 0
        For i = 1 To UBound(data, 1)
            If data(i, 3) >= 2000 Then ' C列の金額が2,000円以上
                targetIndex = targetIndex + 1
                filteredData(targetIndex, 1) = data(i, 1) ' A列
                filteredData(targetIndex, 2) = data(i, 2) ' B列
                filteredData(targetIndex, 3) = data(i, 3) ' C列
            End If
        Next i
    Else
        MsgBox "条件に一致する行はありませんでした。"
        Exit Sub
    End If

    ' 元のシートにフィルタリングされたデータを貼り付ける
    ws.Range("A1:C" & targetIndex).Value = filteredData
    
    ' 残りの行を削除
    If targetIndex < lastRow Then
        ws.Rows(targetIndex + 1 & ":" & lastRow).Delete
    End If

    ' 列幅を自動調整
    ws.Columns("A:C").AutoFit

    ' タイマーを停止
    StopWatch.StopTimer
    
    ' 経過時間を表示
    MsgBox StopWatch.GetFormattedElapsedTime()
End Sub

配列を使った行削除の結果は、10.95秒でした!

フィルタを使った行削除

それでは、フィルタ機能使って条件に一致する行をまとめて削除してみましょう!

コード

Option Explicit

Sub CopyFilteredRowsToNewSheet()
    Dim wsSource As Worksheet
    Dim wsTarget As Worksheet
    Dim lastRow As Long
    
    ' タイマーを開始
    StopWatch.StartTimer
    
    ' 元データのシートを設定
    Set wsSource = ThisWorkbook.Worksheets("売上一覧") ' 元データシート名を指定

    ' 最終行を取得
    lastRow = wsSource.Cells(wsSource.Rows.Count, "A").End(xlUp).Row

    ' フィルタを適用
    With wsSource
        .AutoFilterMode = False ' 既存のフィルタを解除
        .Range("A1:C" & lastRow).AutoFilter field:=3, Criteria1:=">=2000" ' C列をフィルタリング
    End With

    ' 新しいシートを作成
    Set wsTarget = ThisWorkbook.Worksheets.Add
    wsTarget.Name = "条件に一致する行"

    ' フィルタリングされたデータをコピー
    wsSource.Range("A1:C" & lastRow).SpecialCells(xlCellTypeVisible).Copy Destination:=wsTarget.Range("A1")

    ' フィルタを解除
    wsSource.AutoFilterMode = False

    ' 列幅を自動調整
    wsTarget.Columns("A:C").AutoFit

    ' タイマーを停止
       StopWatch.StopTimer
       
    ' 経過時間を表示
    MsgBox StopWatch.GetFormattedElapsedTime()
End Sub

実行してみましょう!

AutoFilter field:=3, Criteria1:=">=2000"

で、フィルタが実行されます。

    If Not targetRange Is Nothing Then
        targetRange.Delete shift:=xlUp
    End If

ここで、対象の行が削除されます。

実行結果

フィルタでの行削除の処理時間は、160.26秒でした!行削除は、時間がかかる処理ですが遅いですね!配列はやっぱり高速です。

Row.Deleteで一行毎削除


ちなみにRow.Deleteで実行した場合も計測してみましょう!

Option Explicit

Sub DeleteRowsAbove5000()
    Dim i As Long
    Dim lastRow As Long
    
    ' タイマーを開始
    StopWatch.StartTimer
    
    ' 最終行を取得
    lastRow = Cells(Rows.Count, 3).End(xlUp).Row ' C列の最終行を取得
    
    ' 最終行から1行目に向かってループ
    For i = lastRow To 1 Step -1
        ' C列の値が2,000円以上の場合
        If Cells(i, 3).Value >= 2000 Then
            Rows(i).Delete ' 行を削除
        End If
    Next i
    
    ' タイマーを停止
    StopWatch.StopTimer
       
    ' 経過時間を表示
    MsgBox StopWatch.GetFormattedElapsedTime()
End Sub

30分以上放置しましたが、終了しなかったため検証は終了しました。止めた時には、まだ40万行を処理していました💦

条件に一致した行を他のシートに貼り付ける
配列:10.95秒
フィルタ:160.26秒
1行毎:30分以上(途中終了)

まとめ

やはり配列での処理が一番早い結果となりました!フィルタも使うことはありますが、やはり配列が高速だということが再認識できましたね!

記事を書いた人

稲垣

  • Excel、ACCESSでのシステム開発が得意
  • ITスキルを共有し実践的に学びながら成長する人を見るのが幸せ
  • 自家焙煎するほどのコーヒー好き
  • 使用言語 VBA、Python、Javascript、Java、HTML、CSS etc.
  • 保有資格 Kintoneアソシエイト、日商簿記検定2級、マンション管理士、管理業務主任者、情報セキュリティマネジメント、ExcelVBA etc.
  • 業務フロー図の作成や業務時間分析を通して、効率化ポイントを探る人
  • お客様にとって本当に良いことかを第一に考える人
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次