Excel VBAを使ってデータを振り分けるとき、「条件に一致した行だけを別シートへコピーしたい」という処理はよくあります。
しかし、実務では単純に決められた位置へコピーするだけでは不十分なことがあります。
すでに転記先シートにデータが保存されている場合、既存データを残したまま、その最終行の次へ新しいデータを追加しなければなりません。コピー先を固定してしまうと、前回のデータを上書きしたり、同じ位置へ何度も書き込んだりする原因になります。
そこで重要になるのが、転記先の最終行を取得し、その1行下へ条件に一致したデータを追記する処理です。
この記事では、元データから条件に一致する行を判定し、別シートに保存されているデータの末尾へ順番に追加する方法を解説します。
単純な「別シートへのコピー」ではなく、日々増えていくデータを蓄積することを想定した実務向けの処理として見ていきましょう。
✅ VBAで条件に一致する行を別シートの最終行へ追加する仕組み
条件に一致した行を別シートへ転記するだけであれば、それほど複雑な処理ではありません。しかし実務で注意したいのは、転記先にすでにデータが存在しているケースです。コピー先を2行目などに固定すると、既存データを上書きしてしまう可能性があります。
逆に最終行の考え方を理解せずにコードを組むと、空白行が大量にできたり、見出し行まで書き換えたりすることがあります。特に、毎日・毎月同じマクロを実行してデータを蓄積する仕組みでは、この違いが大きな問題になります。そのため「条件判定」と「最終行の取得」は別々の役割として考えることが重要です。まずは、どのような流れでデータを追記するのか整理しておきましょう。
・条件判定と最終行への追記を分けて考える
今回作成する処理は、大きく分けると次のような流れになります。
- 元データが保存されているシートを指定する
- 転記先となる別シートを指定する
- 元データ側の最終行を取得する
- 各行の値を確認して条件に一致するか判定する
- 転記先シートの現在の最終行を確認する
- 最終行の1行下へデータを追加する
- 次の行について同じ判定を繰り返す
ポイントは、元データの最終行と転記先の最終行は役割が違うことです。
元データ側の最終行は「どこまで検索するか」を決めるために使用します。一方、転記先側の最終行は「次のデータをどこへ書き込むか」を決めるために使用します。
たとえば元データが「データ」シート、転記先が「対象データ」シートだったとします。
「データ」シートには100件のデータがあり、「対象データ」シートには過去に転記した20件が保存されているとします。
この場合、新しく条件に一致したデータは「対象データ」シートの既存20件を消すのではなく、その下へ21件目、22件目……という形で追加していきます。
この追記型の転記が今回の記事で扱う中心的な処理です。
条件に一致する行を別シートへコピーする基本的な流れから確認したい場合は、「条件に一致する行を別シートへコピーする方法」もあわせて確認しておくと、条件判定と転記処理の役割を整理しやすくなります。
・固定セルへのコピーでは既存データを上書きする
たとえば、コピー先を次のように固定する方法があります。
wsDestination.Range("A2:D2").Value = wsSource.Range("A2:D2").Value
このコードでは、コピー先が常にA2:D2です。
一度だけ実行する処理であれば問題ない場合もありますが、繰り返し実行するとA2:D2へ新しいデータが上書きされます。
日々の処理結果を残したい場合には適していません。
そこで、転記先の最終行を調べ、
destinationRow = wsDestination.Cells(wsDestination.Rows.Count, "A").End(xlUp).Row + 1
のように、現在の最終行+1を次の書き込み位置として取得します。
たとえばA列の最終データが20行目なら、destinationRowには21が入ります。
次にデータを追加するとA列の最終データは21行目になるため、その次は22行目が書き込み先になります。
この仕組みによって、既存データを残したまま下方向へデータを蓄積できます。
✅ VBAで条件に一致した行を最終行の次へコピーする基本コード
最終行を取得できても、取得するタイミングや基準列を間違えると、意図しない位置へデータが書き込まれることがあります。特に注意したいのが、元データと転記先で同じ最終行変数を使ってしまうケースです。コード量を減らすために変数を使い回すと、「検索範囲」と「書き込み位置」の区別が分かりにくくなります。
また、CellsやRows.Countの所属シートを省略すると、アクティブシートによって結果が変わるコードにもなりかねません。実務では短さよりも、どのシートを参照しているのか明確なコードの方が安全です。ここでは、後から条件や転記範囲を変更しやすい形で基本コードを組み立てます。
・条件一致した行を追記する実務向けコード
例として、「データ」シートのB列にあるステータスが「完了」の行だけを、「完了データ」シートの最終行の次へ追加します。
A~D列を転記対象とします。
Sub AppendMatchedRows()
Dim wsSource As Worksheet
Dim wsDestination As Worksheet
Dim sourceLastRow As Long
Dim destinationRow As Long
Dim currentRow As Long
'元データと転記先のシートを設定
Set wsSource = ThisWorkbook.Worksheets("データ")
Set wsDestination = ThisWorkbook.Worksheets("完了データ")
'元データの最終行を取得
sourceLastRow = wsSource.Cells(wsSource.Rows.Count, "B").End(xlUp).Row
'2行目から最終行まで条件を確認
For currentRow = 2 To sourceLastRow
'B列が「完了」の行だけを転記
If wsSource.Cells(currentRow, "B").Value = "完了" Then
'転記先A列の最終行の1行下を取得
destinationRow = _
wsDestination.Cells(wsDestination.Rows.Count, "A").End(xlUp).Row + 1
'A~D列の値を転記
wsDestination.Cells(destinationRow, "A").Resize(1, 4).Value = _
wsSource.Cells(currentRow, "A").Resize(1, 4).Value
End If
Next currentRow
End Sub
このコードでは、単純にCopyメソッドを実行するのではなく、転記元と転記先の範囲に対してValueを使っています。
今回のようにセルの値を蓄積することが目的で、書式や数式をそのまま複製する必要がない場合は、クリップボードを使用しない方法の方が処理内容を把握しやすくなります。
また、SelectやActivateも使用していません。
シートを画面上で選択しなくても、
wsSource.Cells(...)
wsDestination.Cells(...)
のように対象シートを明示すれば処理できます。
これにより、ユーザーが別シートを表示している状態でも処理対象が変わりにくくなり、後からコードを確認したときにも「どのシートからどのシートへ転記しているのか」が分かりやすくなります。
条件に一致したデータを別シートへ振り分ける処理そのものを詳しく確認したい場合は、「条件に一致する行を別シートへ抽出する方法」で、条件判定から自動転記までの流れを解説しています。
・最終行を転記先から取得する理由
今回のコードで特に重要なのが、次の部分です。
destinationRow = _
wsDestination.Cells(wsDestination.Rows.Count, "A").End(xlUp).Row + 1
ここでは、転記先である「完了データ」シートのA列を基準として最終行を取得しています。
元データ側の最終行をそのままコピー先として使用してはいけません。
たとえば元データが500行あったとしても、転記先に保存されているデータが50行しかなければ、次に追加すべき場所は基本的に51行目付近です。
元データの500という行番号を使用すると、転記先の51~499行目が空白になってしまいます。
そのため、
- 元データの最終行 → 条件を調べる範囲
- 転記先の最終行 → データを追加する位置
というように役割を明確に分けます。
変数についてもlastRowだけで使い回すのではなく、sourceLastRowとdestinationRowのように目的が分かる名前にしておくと、後から処理を修正するときの読み間違いを減らせます。
✅ VBAで最終行へ追加するときに基準列を正しく選ぶ
「最終行はA列から取得すればよい」と覚えてしまうと、データ構成が変わったときに思わぬ不具合につながります。実務データでは、すべての列に必ず値が入っているとは限りません。A列が任意入力で途中に空白がある一方、管理番号が入るB列は必須というケースもあります。こうした表でA列だけを基準にすると、本来のデータ末尾とは異なる位置を取得する可能性があります。
コード自体がエラーにならないため、間違った位置へ追記されて初めて気付くこともあります。最終行を取得するときは「何列目か」ではなく、「どの列ならデータの存在を確実に判断できるか」で考えることが重要です。
・必ず値が入る列を最終行の基準にする
たとえば次のようなデータを管理しているとします。
| 管理番号 | ステータス | 担当者 | 備考 |
|---|---|---|---|
| 1001 | 完了 | 田中 | 確認済み |
| 1002 | 処理中 | 佐藤 | |
| 1003 | 完了 | 鈴木 |
この場合、「備考」は空白になる可能性があります。
備考列を基準に最終行を取得すると、実際には3件のデータが存在しているにもかかわらず、空白の状態によって正しい末尾を判断できないことがあります。
一方、「管理番号」がすべてのデータに必ず付与される仕様なら、管理番号の列を最終行判定に使う方が安全です。
つまり、
wsDestination.Cells(wsDestination.Rows.Count, "A").End(xlUp).Row
の "A" は単なる決まりではありません。
転記先で必ず値が存在する列を選ぶことが大切です。
この考え方を押さえておくと、列構成が変更された場合でも「どこを直す必要があるか」を判断しやすくなります。
✅ VBAで転記先が空の場合も最終行へ安全に追加する
最終行を取得してデータを追記する処理では、転記先にすでにデータがあるケースだけを考えると不具合につながることがあります。特に新しく作成したシートでは、見出しだけがある場合と、完全に何も入力されていない場合で最終行の考え方が変わります。End(xlUp)は便利ですが、取得した行番号にそのまま1を加えれば必ず正しいとは限りません。
また、見出しを1行目に残したい処理では、データをどの行から開始するのかをあらかじめ決めておく必要があります。この部分を曖昧にすると、見出しの直下ではなく意図しない位置からデータが追加されることがあります。実務では「データがある場合」だけでなく、「初回実行時にどう動くか」まで確認しておくことが重要です。
・見出しがあるシートでは2行目からデータを追加する
転記先シートの1行目に、
- 管理番号
- ステータス
- 担当者
- 備考
などの見出しが設定されているのであれば、最初のデータは2行目から追加します。
前半で使用した次の処理では、
destinationRow = _
wsDestination.Cells(wsDestination.Rows.Count, "A").End(xlUp).Row + 1
A1に見出しが入っていれば、データがまだ1件も存在しない状態でもdestinationRowは2になります。
そのため、1行目を見出し、2行目以降をデータ領域として統一しておけば、その後も同じ仕組みで追記できます。
ただし、転記先シートを完全な空白状態で使用する場合は注意が必要です。
空白シートでもEnd(xlUp).Rowによって1行目が基準になるため、そのまま+ 1すると2行目から書き込まれます。
「見出しなしなら1行目からデータを保存したい」という仕様では、この動作は適していません。
実務では処理を複雑にするより、転記先の1行目を見出しとして固定し、データは2行目以降へ保存するなど、シート側の仕様を統一しておく方が保守しやすくなります。
✅ VBAで同じ条件一致データを重複して追加しないよう注意する
最終行へ追記するマクロで特に注意したいのが、同じ処理を繰り返し実行した場合です。コードが正常終了したからといって、データの内容まで正しいとは限りません。
たとえば「完了」の行をすべて転記するマクロを午前と午後に実行すると、午前中に転記したデータまで午後に再び条件へ一致します。その結果、転記先には同じデータが2件ずつ保存される可能性があります。これはVBAのエラーではなく、処理仕様によって発生する重複です。定期的に実行する追記型マクロでは、「何を追加するか」だけでなく「すでに追加済みか」を考える必要があります。特に履歴管理や集計用シートでは、重複データが後工程の集計結果にも影響するため注意しましょう。
・追記するだけでは再実行時に同じ行が追加される
前半のコードでは、B列が「完了」であれば転記する仕組みにしています。
そのため、
- 1回目の実行で「完了」のデータを転記する
- 元データの「完了」がそのまま残る
- マクロをもう一度実行する
- 同じ行が再び条件に一致する
- 転記先の最終行へ同じデータが追加される
という動作になります。
これはコードの間違いではありません。
「条件に一致した行を、その時点の最終行へ追加する」という指示どおりに動いているためです。
実務で重複を防ぐ場合は、たとえば次のような設計が考えられます。
- 転記後に元データへ「転記済み」のフラグを付ける
- 管理番号を転記先から検索し、存在しないデータだけ追加する
- 処理日時を保存して未処理データだけを対象にする
- 転記対象となる期間を限定する
どの方法が適しているかは、元データを変更してよいか、管理番号が一意なのか、履歴をどのように管理するのかによって変わります。
たとえば、元データに「転記済み」という列を追加できる業務なら、処理後にフラグを設定する方法は比較的分かりやすい設計です。
一方で元データを変更してはいけない場合は、転記先に管理番号が存在するか確認してから追加する方法が候補になります。
重要なのは、最終行への追記と重複防止は別の処理として考えることです。
今回の基本コードへ無理にすべての機能を詰め込むより、業務仕様に応じて必要な重複対策を追加した方が、後から修正しやすいコードになります。
✅ VBAで条件一致行を最終行へ追加する処理を実務向けに変更する
実際の業務では、「B列が完了ならA~D列を転記する」という条件だけで処理が終わるとは限りません。転記する列が増えたり、判定条件が変更されたり、転記先のレイアウトが変わったりすることがあります。このような変更を想定せずにセル番地をコードの各所へ直接書いていると、修正箇所が増えて保守が難しくなります。
また、処理速度だけを優先して複雑なコードにすると、数か月後に別の担当者が改修するときに処理内容を追いにくくなります。実務用VBAでは、現在動くことだけでなく、変更箇所を判断しやすいことも重要です。条件判定・最終行取得・転記範囲の役割を意識してコードを構成しておきましょう。
・条件を変更するときは判定部分を明確にする
たとえば「完了」ではなく「承認済み」のデータを転記したい場合は、
If wsSource.Cells(currentRow, "B").Value = "承認済み" Then
のように変更できます。
複数の値を対象にするのであれば、
If wsSource.Cells(currentRow, "B").Value = "完了" Or _
wsSource.Cells(currentRow, "B").Value = "承認済み" Then
といった判定も可能です。
ただし、条件が増えるほど1つのIf文にすべてを書き込む方法は読みにくくなります。
複数条件、部分一致、日付による判定などが必要になった場合は、条件判定そのものを整理してから追記処理と組み合わせる方が安全です。
今回の記事では、あくまで条件に一致した後、その行を転記先の最終行へ追加する仕組みを中心に考えます。
「完了かつ担当者が田中」のように複数の条件を組み合わせて転記対象を絞り込みたい場合は、「複数条件に一致する行を抽出する方法」でAND・OR条件の組み立て方も確認できます。
・転記範囲を変更するときはResizeの列数を合わせる
前半では次のコードを使用しました。
wsDestination.Cells(destinationRow, "A").Resize(1, 4).Value = _
wsSource.Cells(currentRow, "A").Resize(1, 4).Value
Resize(1, 4)は、「1行×4列」の範囲を表しています。
つまりA~D列をまとめて転記しています。
A~F列まで転記するのであれば、
Resize(1, 6)
に変更できます。
この方法なら、
Cells(currentRow, "A")
Cells(currentRow, "B")
Cells(currentRow, "C")
のようにセルを1つずつコピーする必要がありません。
連続した複数列を扱う場合は、範囲単位で値を渡した方がコードも整理しやすくなります。
ただし、A列・C列・F列だけを転記するような非連続の列構成では、単純なResizeでは対応できません。
転記先のレイアウトによって、範囲をまとめるのか、必要な項目を個別に書き込むのかを使い分けましょう。
完全一致ではなく、「完了予定」や「処理完了」のように特定の文字を含む行を対象にしたい場合は、「特定の文字を含む行を抽出する方法」でInStrを使った部分一致の判定方法を確認できます。
✅ VBAで最終行へ追記するときはCopyとValueを使い分ける
「行をコピーする」という言葉から、必ずCopyメソッドを使う必要があると思われることがあります。しかし、値だけを別シートへ蓄積する処理と、書式・数式を含めて行全体を複製する処理では目的が異なります。目的を整理せずにCopyを使用すると、不要な数式や書式まで転記してしまうことがあります。
反対に、帳票の見た目まで引き継ぐ必要がある処理でValueだけを使用すると必要な情報が失われます。どちらが優れているという話ではなく、「何を別シートへ残したいのか」で判断することが重要です。追記型のデータ保存では、後から集計や検索に利用することも考えて転記方法を決めましょう。
・値を蓄積するならValueによる転記が分かりやすい
今回のコードでは、
wsDestination.Cells(destinationRow, "A").Resize(1, 4).Value = _
wsSource.Cells(currentRow, "A").Resize(1, 4).Value
としています。
この方法では、セルの値を転記先へ渡します。
たとえば処理済みデータを履歴シートへ蓄積したり、後から集計するための一覧表を作ったりする場合に利用しやすい方法です。
一方、セルの背景色や罫線、数式なども含めてコピーしたい場合には、Copyメソッドを使用する方法があります。
実務では「コピー」という操作名だけでコードを決めるのではなく、
値をデータとして保存したいのか、それともセルそのものを複製したいのか
を先に整理することが大切です。
また、大量データを扱う処理では、1行ずつセルへ書き込む方法だけでなく、対象データを配列へ格納してまとめて出力する設計も候補になります。
数百行程度の単純な処理なら分かりやすさを優先したコードでも十分な場合がありますが、数万行規模になると処理方法によって実行時間に差が出ることがあります。
まずは今回の基本的な追記処理を理解し、データ量や実行頻度に応じて処理方法を選択するとよいでしょう。
行ではなく、条件に一致した「列」を別シートへ追加したい場合は、「条件に一致する列をコピーして別シートの最終行へ追加する方法」で、列方向の判定と転記方法を確認できます。
✅ まとめ:VBAで条件一致した行を別シートの最終行へ安全に追加しよう
条件に一致する行を別シートへ転記する処理では、単にコピーできるだけでは実務上不十分なケースがあります。特に既存データを残しながら継続的に情報を蓄積する場合は、転記先の最終行を正しく取得することが重要です。
また、追記型の処理ではマクロを再実行したときの重複にも注意しなければなりません。元データの最終行と転記先の最終行を混同しないことも、安定した処理を作るポイントです。さらに、最終行を取得する基準列は、必ず値が入る項目を選ぶ必要があります。こうした設計を最初に整理しておけば、条件や転記範囲が変更された場合にも対応しやすくなります。
- 元データの最終行は「どこまで条件判定するか」の判断に使用する
- 転記先の最終行は「次にどこへ追加するか」の判断に使用する
- 転記先の最終行に1を加えることで既存データの下へ追記できる
- 最終行の基準には管理番号など必ず値が入る列を使用する
- 再実行する処理では同じデータが重複して追加されないか確認する
- 値だけを蓄積する場合は
Valueによる転記が分かりやすい - 書式や数式も必要な場合は
Copyなど別の方法を検討する - 条件判定・最終行取得・転記処理の役割を分けると保守しやすくなる
条件に一致したデータを別シートへ保存するだけなら、比較的シンプルなVBAでも実現できます。しかし、既存データを残したまま最終行へ追加する仕組みを取り入れることで、日次データの蓄積、処理済みデータの履歴管理、部署別データの振り分けなど、より実務的な自動化へ発展させられます。
まずは「条件に一致するかを判定する処理」と「転記先の次の空き行を決める処理」を分けて考え、業務に合わせて重複防止や複数条件などを追加していきましょう。