Excel VBAでデータを整理するとき、「途中に空白セルがある列をまとめて削除したい」というケースがあります。
たとえば、1行目には見出しが入力されていても、2行目以降のデータに1つでも空白があれば、その列を不要な項目として削除したい場合です。
ここで注意したいのが、「空白セルを含む列」と「列全体が空白」の違いです。
列内に1つでも空白セルがあれば削除する処理と、データがまったく入力されていない列だけを削除する処理では、判定方法も削除される結果も大きく異なります。
さらに、列を左から順番に削除すると列位置が変わり、判定していない列を飛ばしてしまうこともあります。
この記事では、空白セルを含む列を判定して安全に削除する方法を、削除方向や判定範囲など実務で注意したいポイントとあわせて解説します。
✅ VBAで空白セルを含む列を削除する前に判定範囲を決める
空白セルを条件に列を削除するとき、最初に決めるべきなのは「どこからどこまでを空白判定の対象にするか」です。シート全体を対象にすると、実際のデータより下にある未使用セルまで空白として扱われるため、ほぼすべての列が条件に一致してしまいます。
また、1行目の見出しまで判定対象に含めるかどうかでも結果が変わります。実務では、見出しは存在しているものの、データ部分に欠損がある列だけを削除したいケースもあります。空白判定ではコードを書く前に、対象となる表の構造を明確にすることが重要です。「空白がある」という条件だけではなく、どの範囲に空白があったら削除するのかまで決めておきましょう。
・見出しではなくデータ部分だけを空白判定する
たとえば、次のようなデータがあるとします。
| 商品名 | 売上 | 原価 | 担当者 |
|---|---|---|---|
| 商品A | 100000 | 60000 | 佐藤 |
| 商品B | 120000 | 鈴木 | |
| 商品C | 90000 | 50000 | 田中 |
この場合、「原価」列には商品Bの行に空白セルがあります。
今回の記事では、
データ部分に1つでも空白セルが存在する列を削除する
というルールを基本とします。
そのため、見出しがある1行目は空白判定の対象から外し、2行目からデータの最終行までを確認します。
処理の流れは次のとおりです。
- データの最終行を取得する
- 見出しがある最終列を取得する
- 各列について2行目から最終行までを確認する
- 範囲内に空白セルがあるか判定する
- 空白セルが1つでもあれば、その列全体を削除する
このように対象範囲を限定すれば、ワークシートの未使用セルまで空白判定へ含めることを防げます。
特に「空白セルを含む列」という条件では、表の外側にある空白ではなく、表のデータ範囲内にある空白を探すことが重要です。
・最終行はどの列を基準にするか注意する
空白セルを探すときに、
lastRow = targetSheet.Cells(targetSheet.Rows.Count, currentColumn) _
.End(xlUp).Rowのように、現在調べている列ごとに最終行を取得すると問題が起こる場合があります。
たとえば、本来100行目までデータがある表で、D列だけ80行目以降がすべて空白だったとします。
D列自身から最終行を求めると79行目などが最終行として取得され、80~100行目の空白部分を判定できません。
そのため、表全体で必ずデータが入る基準列がある場合は、その列から最終行を取得する方法が安全です。
たとえばA列の商品番号が必須項目なら、
lastRow = targetSheet.Cells(targetSheet.Rows.Count, "A") _
.End(xlUp).Rowとします。
これにより、A列を基準として表全体のデータ範囲を決め、その範囲内で各列の空白を確認できます。
「最終行を取得する」という処理自体は簡単でも、どの列を基準にするかによって空白判定の結果が変わる点には注意が必要です。
✅ VBAで空白セルを含む列と空白列を区別する
「空白の列を削除する」という表現は似ていますが、「1つでも空白セルがある列」と「すべてのセルが空白の列」はまったく異なる条件です。この違いを理解せずにコードを使用すると、必要なデータが入っている列まで削除する危険があります。たとえば100件中99件に値が入っていて、1件だけ未入力の列も「空白セルを含む列」です。
一方、空白列は対象範囲のすべてが未入力の状態を意味します。業務データでは1件の入力漏れだけで列全体を削除してよいとは限らないため、削除条件は特に慎重に決める必要があります。ここでは2つの条件の違いをコードで確認します。
・1つでも空白セルがあれば削除対象にする判定
指定範囲に空白セルが存在するか確認する方法の1つとして、CountBlankを利用できます。
If Application.WorksheetFunction.CountBlank(checkRange) > 0 Then
'空白セルが1つ以上存在する
End Ifたとえば2行目から100行目までの範囲に1つでも空白セルがあれば、CountBlankの結果は1以上になります。
したがって、
Set checkRange = targetSheet.Range( _
targetSheet.Cells(2, currentColumn), _
targetSheet.Cells(lastRow, currentColumn) _
)
If Application.WorksheetFunction.CountBlank(checkRange) > 0 Then
targetSheet.Columns(currentColumn).Delete
End Ifとすれば、データ範囲内に空白セルが存在する列を削除できます。
ただし、この条件はかなり強力です。
1000行ある列のうち999行に正常なデータが入っていても、1セルだけ空白なら列全体が削除されます。
そのため、実務では「空白が存在する」という事実だけで列削除してよいのかを十分確認してから使用する必要があります。
・列全体が空白の場合だけ削除する判定
一方、対象範囲のすべてが空白である列だけを削除したい場合は、空白セル数と範囲内のセル数を比較できます。
If Application.WorksheetFunction.CountBlank(checkRange) = _
checkRange.Cells.Count Then
'対象範囲がすべて空白
End Ifたとえば対象範囲が99セルなら、99セルすべてが空白の場合だけ条件成立となります。
この違いを整理すると、
CountBlank(checkRange) > 0は空白セルを1つ以上含む列、
CountBlank(checkRange) = checkRange.Cells.Countは対象範囲がすべて空白の列
を意味します。
コード上ではわずかな違いですが、削除結果は大きく異なります。
実務で列を削除するときは、処理を実行する前に「一部の空白を検出したいのか」「完全な空白列だけを検出したいのか」を明確にしておきましょう。
列ではなく、行の中に空白セルがあるかを判定して対象行を削除したい場合は、「空白セルを含む行を削除する方法」で、空白セルを含む行と空白行の違い、安全に削除するための判定方法を解説しています。
✅ VBAで空白セルを含む列を安全に削除する方法
空白判定が正しくても、列を削除する順番を間違えると期待どおりに処理できません。たとえばB列を削除すると、それまでC列だった列がB列へ移動します。そのまま次の列番号へ進むと、移動してきた列を確認せずに飛ばしてしまうことがあります。
これは行削除でも起こる典型的な問題ですが、列削除では横方向に列位置が変化します。処理後の列番号を意識せず左から削除していくと、連続して条件に一致する列がある場合に削除漏れが発生しやすくなります。そのため、削除を伴うループでは検索方向そのものが重要です。ここでは、右端から左方向へ判定する方法を使用します。
・右端から左へ列を確認して削除する
列削除では、次のようにStep -1を使用して右端から処理します。
For currentColumn = lastColumn To 1 Step -1
'空白セルの判定と列削除
Next currentColumnたとえばD列を削除しても、その左側にあるA~C列の列番号は変わりません。
そのため、これから確認する列への影響を抑えられます。
逆に、
For currentColumn = 1 To lastColumnとして左から削除すると、削除後に右側の列が左へ移動するため、次に確認すべき列を飛ばす可能性があります。
削除処理では、単に条件判定が正しいだけでは不十分です。
削除によってセル・行・列の位置が変わることまで考えてループ方向を決める必要があります。
・空白セルを含む列を削除する実務コード
次のコードでは、A列を基準に最終行を取得し、2行目から最終行までに空白セルが1つでも存在する列を削除します。
Sub DeleteColumnsContainingBlankCells()
Dim targetSheet As Worksheet
Dim checkRange As Range
Dim lastRow As Long
Dim lastColumn As Long
Dim currentColumn As Long
Dim blankCount As Long
'処理対象となるシートを明示する
Set targetSheet = ThisWorkbook.Worksheets("元データ")
'A列を基準にデータの最終行を取得する
lastRow = targetSheet.Cells(targetSheet.Rows.Count, "A") _
.End(xlUp).Row
'1行目を基準に最終列を取得する
lastColumn = targetSheet.Cells(1, targetSheet.Columns.Count) _
.End(xlToLeft).Column
'列位置のずれを防ぐため右端から確認する
For currentColumn = lastColumn To 1 Step -1
'見出しを除いたデータ部分を判定範囲にする
Set checkRange = targetSheet.Range( _
targetSheet.Cells(2, currentColumn), _
targetSheet.Cells(lastRow, currentColumn) _
)
'対象範囲内の空白セル数を取得する
blankCount = Application.WorksheetFunction.CountBlank(checkRange)
'空白セルが1つでも存在する場合は列を削除する
If blankCount > 0 Then
targetSheet.Columns(currentColumn).Delete
End If
Next currentColumn
End Subこのコードで特に重要なのは、CountBlankそのものよりも、判定範囲と削除方向を明確にしていることです。
1行目は見出しなので対象から除外し、A列を基準に表全体の最終行を決めています。
さらに、右端から左方向へ処理することで、削除による列番号のずれで未判定の列を飛ばすことを防いでいます。
ただし、このコードにはもう1つ重要な注意点があります。
A列も削除判定の対象になっているため、A列に空白があれば基準列そのものが削除されます。A列が商品番号や管理番号などの必須列で、絶対に削除してはいけない場合は、削除対象となる開始列を制限する設計が必要です。
「空白なら削除」という処理を作るだけではなく、業務上削除してはいけない列をどう保護するかまで考えることで、より安全なマクロになります。
空白の有無ではなく、見出しに指定した値が含まれているかを判定して不要な列を削除したい場合は、「特定の値を含む列を削除する方法」で、見出しを基準に対象列を判定して安全に削除する方法を解説しています。
✅ VBAで必須列を空白判定による削除対象から除外する
前半のコードでは、1行目の最終列からA列までをすべて空白判定の対象にしています。しかし実務では、商品番号や社員番号、日付など、空白が含まれていても自動削除してはいけない列が存在することがあります。
特に最終行を取得する基準列まで削除対象にすると、後続処理を追加した際にデータ範囲を正しく判断できなくなる可能性があります。「条件に一致したから削除する」だけではなく、「そもそも削除してよい列なのか」を分けて考えることが重要です。対象範囲が決まっている場合は、ループする列そのものを限定する方がコードも分かりやすくなります。業務上の必須項目を保護しながら、不要な列だけを処理できる構成にしておきましょう。
・削除対象となる開始列を指定する
たとえばA列が商品番号であり、絶対に削除したくない場合は、B列以降だけを処理できます。
For currentColumn = lastColumn To 2 Step -1
Set checkRange = targetSheet.Range( _
targetSheet.Cells(2, currentColumn), _
targetSheet.Cells(lastRow, currentColumn) _
)
blankCount = Application.WorksheetFunction.CountBlank(checkRange)
If blankCount > 0 Then
targetSheet.Columns(currentColumn).Delete
End If
Next currentColumnTo 2としているため、B列まで処理したところでループが終了し、A列は判定されません。
A列とB列を残してC列以降だけを対象にするなら、
For currentColumn = lastColumn To 3 Step -1とできます。
このように削除してよい範囲が明確なら、判定条件の中で毎回、
If currentColumn <> 1 Thenのように除外するよりも、最初からループ範囲を限定した方が処理意図を把握しやすくなります。
ただし、「A列とD列だけは残す」のように保護対象が離れている場合は、列番号や見出しを確認して除外する方法も検討します。
列番号だけに依存すると、後から列が追加されたときに対象がずれる可能性があります。
固定レイアウトなら列番号、レイアウト変更が想定されるなら見出し名など、データ構造に合わせて保護方法を選ぶことが大切です。
✅ VBAでは数式で空文字になるセルも空白判定に注意する
見た目が空白だからといって、セルの状態まで完全に同じとは限りません。特にExcelでは、セルに数式が入力されていて、その計算結果として""が返されているケースがあります。画面上では何も表示されないため、利用者から見れば通常の空白セルとほとんど区別できません。
しかし、VBAで空白を判定する方法によっては、数式が存在するセルと本当に何も入力されていないセルで結果が変わります。ここを理解せずに列削除へ利用すると、想定以上の列が対象になったり、逆に削除したかった列が残ったりする可能性があります。「見た目が空白」を探すのか、「セル自体が未入力」を探すのかを明確にしておきましょう。
・CountBlankでは数式による空文字も意識する
たとえばセルに次の数式が設定されているとします。
=IF(A2="","",A2*10)A2が空白なら、このセルの表示結果も空文字になります。
CountBlankを利用すると、このように数式の結果が空文字列""になるセルも空白としてカウントされる点に注意が必要です。
そのため、
blankCount = Application.WorksheetFunction.CountBlank(checkRange)による判定は、
「何も入力されていないセルだけを探す」
というより、
「データとして空白相当になっているセルを探す」
用途で考えた方が分かりやすくなります。
帳票上で値が表示されていないセルを欠損として扱うのであれば、この判定が適しているケースがあります。
一方、数式が設定されているセルは処理済みとして扱い、完全な未入力セルだけを問題にしたい場合は、別の判定が必要です。
・完全な未入力セルだけを判定する場合
セルに数式や定数が存在するかを1セルずつ確認する方法もあります。
たとえば次のように、対象列をループしてIsEmptyで判定できます。
Dim targetCell As Range
Dim hasEmptyCell As Boolean
hasEmptyCell = False
For Each targetCell In checkRange.Cells
If IsEmpty(targetCell.Value) Then
hasEmptyCell = True
Exit For
End If
Next targetCellIsEmptyは、変数やセルに値が設定されていない状態を確認するときに利用できます。
条件に一致するセルが1つ見つかった時点で、
Exit Forとしているのもポイントです。
今回必要なのは「空白セルが何個あるか」ではなく、「1つでも存在するか」です。
1件見つかった後も最終行まで調べ続ける必要はありません。
ただし、CountBlankとIsEmptyのどちらが優れているという話ではありません。
- 表示上の空白も含めて欠損と考える →
CountBlank - 数式が入っているセルと未入力セルを区別したい → セルの状態を個別に判定
というように、業務上の「空白」の定義によって判定方法を選ぶことが重要です。
✅ VBAで列を削除する前に対象を確認できるようにする
列削除はデータそのものを変更するため、抽出や非表示よりも慎重な処理が必要です。特に「空白セルが1つでもある」という条件は対象範囲が広く、入力漏れが1件あるだけで列全体を失う可能性があります。コードが正しく動作した結果として必要な列まで削除されるケースは、単純な実行エラーより発見が遅れることがあります。
また、マクロを実行した本人が削除条件を完全に理解しているとは限りません。業務ファイルへ導入する場合は、いきなり削除するだけでなく、どの列が対象になったのか確認できる仕組みも検討しましょう。削除処理ほど「正常に動くこと」と「安全に使えること」を分けて考える必要があります。
・削除対象の見出しを確認してから処理する
削除前に見出しを確認したい場合は、対象列名を文字列へ記録する方法があります。
たとえば、
Dim deletedColumnNames As Stringを用意し、削除する直前に、
deletedColumnNames = deletedColumnNames & _
targetSheet.Cells(1, currentColumn).Value & vbCrLfと記録します。
削除後に、
If Len(deletedColumnNames) > 0 Then
MsgBox "次の列を削除しました。" & vbCrLf & vbCrLf & _
deletedColumnNames, vbInformation
Else
MsgBox "削除対象となる列はありませんでした。", vbInformation
End Ifとすれば、どの列が条件に一致したのか確認できます。
より慎重に運用するなら、実際に削除する前に対象列を一覧化し、利用者の確認後に削除する設計も考えられます。
ただし、確認ダイアログを列ごとに何度も表示すると、対象列が多い場合に操作が煩雑になります。
そのため実務では、対象をまとめて確認する → 必要なら一括処理するという流れの方が扱いやすいケースがあります。
✅ VBAで元データを直接削除しない方法も検討する
不要な列を取り除きたいからといって、必ず元データから削除する必要があるとは限りません。削除処理は一度実行すると元の列構成を変更するため、その後の別マクロや数式が列位置を前提としている場合に影響する可能性があります。
特に複数の処理で共有する元データでは、1つのマクロだけを見て削除してよいか判断するのは危険です。目的が「必要な列だけを使った別データを作ること」であれば、削除ではなく別シートへの抽出の方が安全な場合もあります。また、一時的に見えなくしたいだけなら非表示という選択肢もあります。実務では削除そのものを目的にせず、その後の利用方法から処理を選びましょう。
・削除・非表示・別シート抽出を目的で使い分ける
列を整理する方法は、大きく次のように使い分けられます。
| 目的 | 処理方法 |
|---|---|
| 不要な列をデータから完全に取り除く | 列を削除 |
| 一時的に表示させたくない | 列を非表示 |
| 元データを残して必要な項目だけ使いたい | 別シートへ抽出 |
たとえば外部システムから毎回取得する一時データであれば、不要列を削除して加工する方法が適していることがあります。
一方、マスターデータや他のマクロも利用するシートなら、元データを直接変更しない方が安全です。
また、元データの列番号を使って後続処理をしているマクロがある場合、途中の列を削除すると列番号が変わります。
空白セルを含む列の削除自体は正常に終了しても、その後のマクロへ影響する可能性があります。
そのため、
- 元データを直接変更してよいか確認する
- 空白の定義を決める
- 判定するデータ範囲を決める
- 削除してはいけない列を除外する
- 右端から列を削除する
- 必要に応じて削除結果を確認する
という順番で設計すると、安全性を高められます。
単純にColumns.Deleteを実行するだけでなく、削除した後にどの処理が影響を受けるのかまで考えることが実務では重要です。
元データを変更せずに処理したい場合は、目的に応じて方法を使い分けましょう。一時的に対象列を隠したい場合は「条件に一致する列を非表示にする方法」、必要な列だけを別のデータとして残したい場合は「条件に一致する列だけを別シートへ抽出する方法」で、それぞれの処理を詳しく解説しています。
✅ まとめ:VBAで空白セルを含む列を安全に判定して削除しよう
VBAを使えば、指定したデータ範囲を確認し、空白セルを含む列を自動的に削除できます。
今回のポイントをまとめると、次のとおりです。
- 空白判定はシート全体ではなく、実際のデータ範囲に限定する
- 見出しを除外する場合は2行目以降を判定対象にする
- 最終行はデータが必ず入る基準列から取得する
CountBlank > 0なら空白セルを1つ以上含む列を判定できる- 空白セルを含む列と、すべてのデータが空白の列は区別する
- 列を削除するときは右端から左へ処理して列番号のずれを防ぐ
- 商品番号などの必須列は削除対象から除外する
- 数式で
""を返すセルを空白として扱うか事前に決める - 必要に応じて削除対象の見出しを記録して結果を確認する
- 元データを残す必要がある場合は、削除ではなく非表示や別シートへの抽出も検討する
空白セルを含む列の削除で特に注意したいのは、「空白を見つけること」よりも「その空白を理由に列全体を削除してよいか」を判断することです。
1件の未入力があるだけで列全体を削除する仕様なら、CountBlankによる判定はシンプルに実装できます。しかし、数式による空文字や必須列まで同じ条件で処理すると、必要なデータを失う可能性があります。
まずは空白の定義と判定範囲を明確にし、削除してはいけない列を保護したうえで処理することで、実務でも扱いやすい列削除マクロにできます。