TL;DR — 行の削除は構造の変更です。行を空にするのではなく、行そのものを 取り除き、下にあるすべての行を 1 つ上へ詰めます。この 1 つの事実が、VBA で最も よくあるバグの正体です — 行を削除する順方向ループは、削除された行に続いていた行を ことごとく飛ばします。行番号がループの足元でずれていくからです。直し方はひと言、 逆順にループする(
For i = last To first Step -1)だけ。行全体は.EntireRow.Deleteで消し、離れた行が多数あるならUnionに集めて一度で削除 します — その方が速く、ずれの問題も丸ごと回避できます。
' A 列が空の行をすべて削除する。下から上へループする。
Dim ws As Worksheet: Set ws = ThisWorkbook.Worksheets("Data")
Dim lastRow As Long, i As Long
lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
For i = lastRow To 2 Step -1 ' <-- 逆順に。さもないと行を飛ばす
If Trim(ws.Cells(i, "A").Value) = "" Then ws.Rows(i).EntireRow.Delete
Next i
「これらの行を削除して」は、Excel 自動化で最も簡単そうな作業に聞こえますが、誰もが
最初につまずくのがこれです。原因は Delete メソッドではありません — 削除という操作が、
ループ対象そのものを変えてしまうことにあります。進む向きさえ正しければ条件による削除
は他愛もありませんが、間違えるとマクロは該当行の半分を黙って取りこぼします。本記事は
この 1 つの考え方を軸に据え、そこから導かれる実務パターンを順に見ていきます。
この記事で学べること
- 考え方の軸 — 削除は行を取り除き、下をすべて上へ詰める
- 最も大事なルール — 逆順にループする。順方向だと行を飛ばす
- なぜ
.EntireRow.Deleteが安全な対象なのか、セルへの.Deleteは何をするのか - 離れた行を速く消すパターン —
Unionを組み立て、一度で削除する - 空白行の除去と、参照数式を壊さない条件削除
考え方の軸:削除はグリッドを動かす、行を空にするのではない
人が同じ「削除」と呼ぶ、まったく別の操作が 2 つあります。Delete キーを押すと
セルは消去され — 行は残り、値だけが消えます。右クリック ▸ 削除、あるいはコードの
Rows(i).Delete は行を取り除き — 行そのものが存在しなくなり、その下のすべての行が
1 つ上へ詰まって隙間を埋めます。11 行目だったものが 10 行目になります。
この「隙間を埋める」挙動がすべてです。10 行目を削除すると、11 行目のデータは今や 10 行目に座っています。「11 行目にはさっきと同じものが入っているはず」と仮定した コードは、もう間違いです。だから行の削除は、あなたが書くほかのどんなループとも感触が 違うのです。まだ処理していないデータのアドレスが、削除した瞬間に変わる。座標系を 編集しながら、その上を歩いているようなものです。
このイメージ — 削除した行より下は、すべて上へずれる — を頭に置いておけば、以下の ルールはすべてその当然の帰結にすぎません。
最も大事なルール:逆順にループする、さもないと行を飛ばす
これがそのバグです。ほとんどの人が最初に書くとおりの形で示します。
' 間違い。削除対象の行を順方向(上から下)にループしている。
For i = 2 To lastRow
If ws.Cells(i, "A").Value = "DELETE" Then ws.Rows(i).EntireRow.Delete
Next i
5 行目と 6 行目がどちらも "DELETE" だとします。ループが i = 5 に達し、5 行目を削除
すると、6 行目が上へ詰まって新しい 5 行目になります。ところがループは次に i = 6 へ
進みます — それはかつての 7 行目です。古い 6 行目は一度もチェックされません。
隣り合う 2 つの一致のうち、2 つ目が生き残るのです。一致がブロックで並んでいると、
ほぼ 1 つおきに取りこぼし、症状(「一部の行が削除されない」)はランダムに見えます。
直すのに手間はかかりません。下から上へ歩きます。
' 正しい。最終行から先頭へ向かって削除する。
For i = lastRow To 2 Step -1
If ws.Cells(i, "A").Value = "DELETE" Then ws.Rows(i).EntireRow.Delete
Next i
逆順に進むと、6 行目を削除しても下の行が上へずれるだけ — その行はもう処理済みです。
まだ見ていない行はすべて、いま削除した行より上にあり、番号は動きません。ずれは
相変わらず起きますが、それは害を及ぼせない、あなたの背後で起きるのです。行を削除
するループは、必ず Step -1。 これを覚えれば、削除バグの半分は消えます。
データの整合を保つルール:セルではなく行を削除する
Delete はどんな範囲にも使えて、範囲が小さいほど危険です。ws.Cells(i, "A").Delete
と書くと、セルを 1 つ削除し、Excel はA 列だけその下のセルを上へずらします —
B・C・D 列はそのままです。そこから下の各行はずれてしまい、A 列はある 1 件を、B・C 列は
その下の 1 件を表示することになります。エラーは何も出ず、データが静かに混ざるだけです。
行全体を削除すれば、すべての列がそろって動きます。
ws.Rows(i).EntireRow.Delete ' 11 行目がまるごと消え、12・13… が一緒に上へ詰まる
ws.Cells(i, "A").EntireRow.Delete ' 同じ結果。.EntireRow はセルをその行全体に昇格させる
.EntireRow は「このセル」から「このセルが属する行全体」への昇格であり、ほぼ常に
これが欲しいものです。Shift:=xlUp/xlToLeft を付けた素の Range.Delete は、
レコードを取り除くのではなく本当にセルのブロックを動かしたい、まれな場合のために
とっておきましょう。
より速く安全なパターン:Union に集めて一度で削除する
1 行ずつループして削除する方法は、逆順にしさえすれば正しく動きますが、大きなシートでは
遅くなります — .Delete のたびに Excel はグリッドをずらし、再計算を強いられます。もっと
速く、しかもずれの罠を受けないパターンがあります。削除をすべて 1 ステップでやって
しまうのです。取り除きたい行を Union で 1 つの Range に積み上げ、最後にまとめて
削除します。
Dim victims As Range, i As Long
For i = 2 To lastRow ' 方向は問わない。削除は後でまとめて行う
If ws.Cells(i, "A").Value = "DELETE" Then
If victims Is Nothing Then
Set victims = ws.Rows(i)
Else
Set victims = Union(victims, ws.Rows(i))
End If
End If
Next i
If Not victims Is Nothing Then victims.EntireRow.Delete ' 1 回の削除で全行をまとめて消す
ループが終わるまでどの行も取り除かれないので、走査の最中に行番号が動くことは
ありません — だからここでは順方向ループでもまったく安全です。そして複数領域から
なる範囲の .Delete を 1 回呼ぶ方が、個別の削除を何百回も呼ぶより桁違いに速いのです。
目安はこうです。数行なら逆順ループ、数百・数千行なら Union(またはフィルター —
後述)に集めて一度で削除。どちらも Application.ScreenUpdating = False/
Calculation = xlManual で包めば、大量削除は数秒から一瞬になります。
空白行・条件・参照の罠
空白行を削除するには、どんなループにも勝る 1 行があります — 空白を探すのは Excel に任せましょう。
On Error Resume Next ' 空白が 1 つもないと SpecialCells は 1004 エラーを出す
ws.Range("A2:A" & lastRow).SpecialCells(xlCellTypeBlanks).EntireRow.Delete
On Error GoTo 0
SpecialCells(xlCellTypeBlanks) は A 列の空セルすべてを 1 つの複数領域範囲として返し、
.EntireRow.Delete がそれらの行を一撃で取り除きます。注意が 2 つ。1 つの列を基準に
すること(A が空でも B が埋まっている行も消えます)。そして空白が 1 つもないとエラーに
なること — だから On Error で守ります。
大きなデータを条件で削除するなら、プロの一手は AutoFilter です。不要な行だけに 絞り、見えている行を削除し、フィルターを外す。マッチングを Excel がやってくれるので、 これがいちばん速いのです。
With ws.Range("A1").CurrentRegion
.AutoFilter Field:=1, Criteria1:="DELETE"
.Offset(1).SpecialCells(xlCellTypeVisible).EntireRow.Delete ' 見出し行は飛ばす
End With
ws.AutoFilterMode = False
これらすべてを生き延びる罠が 1 つ。行を削除すると参照がずれます。どこか別の場所の
数式が =A20 を指していて 5 行目を削除すると、Excel はそれを =A19 に書き換えます —
たいていは望みどおりです。しかし数式が依存している行そのものを削除すると、その数式は
#REF! になります。生きた数式の足元から行を削除するのは、シートが #REF! エラー
だらけになる典型的な原因です。一括削除の前に、取り除こうとしている行を下流の何かが
参照していないかを把握しておきましょう。データがどこで終わるかを見つけるのは関連
スキルです — VBA 最終行を参照してください。
ExcelMaster の活用
行削除のコードには、静かに間違える道がいくつもあります。行を飛ばす順方向ループ、列を
ずらす単一セル削除、空白がないと落ちる SpecialCells 呼び出し、#REF! を残す一括削除。
どれも分かりやすいエラーを出さず — ただ少しだけ間違ったデータを作ります。
ExcelMaster なら、
結果を説明するだけで済みます。「A 列が空の行をすべて削除して」「ステータスが Cancelled
の行を消して」と言えば、安全な向きでループを書き(大きなデータなら Union/フィルター)、
.EntireRow を対象にし、境界ケースを守り、まずシートをバックアップします。ブックも
コードもあなたの手元に残り、Step -1 の書き忘れがレポートを静かに壊す、あの一幕だけを
飛ばせます。
よくある質問
削除のとき VBA のループが行を飛ばすのはなぜ?
行を削除すると下のすべての行が 1 つ上へ詰まるのに、順方向ループはインデックスを増やし
続けるからです。5 行目を削除すると、古い 6 行目が 5 行目になります — そしてループは
6 行目へ進み、それを飛ばします。代わりに逆順でループしましょう。
For i = lastRow To 2 Step -1。まだチェックしていない行は常に削除位置より上にあるので、
番号が動くことはありません。
VBA で行全体を削除するには?
.EntireRow.Delete を使います — たとえば ws.Rows(11).EntireRow.Delete や
ws.Cells(11, "A").EntireRow.Delete。どちらも行全体を取り除くので、すべての列が
そろったままです。Cells(11, "A").Delete は避けましょう。これは単一セルを削除して
その列だけをずらし、残りを混ぜてしまいます。
VBA でたくさんの行を削除する最速の方法は?
数百・数千行なら、1 つずつ削除してはいけません。不要な行に AutoFilter で絞って見えて
いるセルを 1 ステップで削除するか、ループ中に対象行すべての Union を組み立てて
最後に .EntireRow.Delete を一度だけ呼びます。複数行をまとめて 1 回削除する方が、
個別に何度も削除するよりずっと速く、ScreenUpdating と Calculation を切ればなおさら
です。
VBA で空白行をすべて削除するには?
ws.Range("A2:A" & lastRow).SpecialCells(xlCellTypeBlanks).EntireRow.Delete が、A 列が
空白の行を 1 回の操作ですべて取り除きます。On Error Resume Next/On Error GoTo 0 で
包みましょう。見つかる空白セルが 1 つもないと SpecialCells はエラーを出すからです。
行を削除したあとに #REF! エラーが出るのはなぜ?
行を削除するとセル参照がずれます。削除した行より先を指す数式は自動で番号が振り直され
ますが、削除した行の中のセルを参照していた数式はもう解決できず、#REF! になります。
一括削除の前に、これから取り除く行にほかのセルが依存していないか確かめましょう。
検証環境
動作確認: Excel 365 (Windows 11), VBA 7.1 — 最終確認 2026-08-07。
関連ガイド: VBA Insert Rows and Columns · VBA Hide Columns and Rows · VBA Last Row · VBA For Loop · VBA Range
