最終行まで処理したのに、3件足りない

LWP | 最終行まで処理したのに、3件足りない

最終行まで処理したのに、3件足りない

「どこまで」と「何件」を、オイカワ部長たちと整理する

Copyright © 2026 LWP 山中 一弘

本資料は、出典を明記いただければ、商用・非商用を問わず、ご自由に複製・改変・再配布していただけます。なお、著作権表示は改変せず、そのまま記載してご利用くださいますようお願いいたします。

ストーリー

月次の明細を出力し終えたところで、フナさんが照合表を持ってきました。「9件あるはずなのに、出力は6件です」。オイカワ部長とVBA担当のシャケモンが、一緒に処理の終点を確かめます。

この会話と数値は架空の説明例です。基本の表は1行目が見出し、2〜10行目が9件の明細。A列のIDは全件に入り、任意入力のB列の備考は7行目を最後に空欄になっています。フィルターや非表示行は使っていません。

1 全部処理したのに、3件足りない

処理がエラーなく終わっても、必要な明細がそろったとは限りません。まず、元の件数と出力の件数を照合します。

図1。オイカワ部長とフナさんが、元の明細9件に対して出力6件、差3件を確かめる。

図1|「全部」の確認は、完了メッセージだけでは終わらない。

2 調べていたのは、備考列

シャケモンが確認すると、処理はB列を下からたどっていました。B7には備考があり、B8〜B10は空欄です。そこで7行目までを処理したため、見出しを除く6件で終わっていました。

図2。フナさんとシャケモンが備考列を確認し、B7を終点とすると8〜10行目の3件が処理から漏れることを示す。

図2|任意入力の列の末尾と、明細全体の末尾は一致しない。

3 同じ表でも、終点が違う

A列なら10行目、B列なら7行目。同じシートでも、指定する列によって答えが変わります。列を指定して求めた行番号を、そのまま「表全体の最終行」と呼ぶと、前提が抜け落ちます。

図3。A列のIDの終点は10行目、B列の備考の終点は7行目であり、その差の8〜10行目に3件の明細がある。

図3|最終行の話には「どの列を見たか」が必要。

4 1件の基準を、先に決める

フナさんの業務では、明細にIDが必須で、備考は任意です。その条件を確認してから、処理の基準列を選びます。必須のIDが欠けた行を見つけた場合も、黙って捨てず、照合が必要な行として扱います。

図4。オイカワ部長とフナさんが、IDは必須、備考は任意という業務上の条件から1件の基準を決める。

図4|図中の表は、必須項目と任意項目を示す模式例。

5 空欄に見えても、式がある

ここで別の条件を試して考えます。A20に空文字を返す数式 `=""` が入っていると、表示が空欄でもセルには式が存在します。今回の下からたどる方法では、A20が終点になります。これは20件あるという意味ではありません。

図5。A20に空文字を返す数式があるため、見た目は空欄でも下から調べた終点が20行目になる例。

図5|「空に見える」と「値も式もない」は違う。

6 行番号を、そのまま件数にしない

最終行が10行目でも、明細が10件とは限りません。今回の基本例で9件になるのは、1行目だけが見出しで、2〜10行目に明細が連続し、全行が対象だからです。途中に空行や対象外の行がある表では、行番号の差だけでは件数を求められません。

図6。オイカワ部長とフナさんが、最終行10から見出し1行を引いて9件になる条件と、条件が崩れる例を整理する。

図6|「10 − 1 = 9件」は、この表の条件があってこその計算。

7 境界の3ケースも確かめる

列が完全に空でも、この方法は行番号1を返します。見出しだけの場合も行番号1ですが、データ件数は0件です。さらに、シートの最下行そのものに値がある場合は、そこを起点に領域の端へ移動するため、単純な「最後の非空セル探し」としては扱えません。

図7。空列、見出しだけ、シート最下行に値がある場合の3つについて、Endによる移動とデータ件数の違いを確認する。

図7|通常の表で便利な方法にも、確かめるべき境界がある。

8 「全部」の条件がそろった

オイカワ部長とフナさんは、「IDを持つ明細を対象とし、元の明細と出力を照合する」と確認しました。処理の終点を直すだけでなく、何をもって完了とするかも共有できました。

図8。オイカワ部長とフナさんが、IDを基準にすること、行番号と件数を分けること、元明細と出力を照合することを共有する。

図8|「全部できた」と言う前に、対象と照合条件をそろえる。

まとめ

最終行は、調べる列と調べ方によって変わります。まず業務上の1件を決め、必須項目の欠落や数式のある空欄も考慮して終点を選びます。そのうえで、行番号とは別に対象件数と出力内容を照合します。

今回の仕掛け

TakiLibの `rngLastRow` は、指定列をシートの最下行から `End(xlUp)` でたどったセルの行番号を返します。引数を省略すると対象は `ThisWorkbook.ActiveSheet`、列は1列目ですが、この記事の例では誤解を避けるため両方を明示します。

Microsoftの仕様では、`Range.End` は起点を含む領域の端のセルを返す操作です。「業務上の明細を数える関数」ではありません。空文字数式や最下行の扱いが重要になるのは、この移動操作を利用しているためです。

今回のソースコードのサンプル

入力・概要

次のコードは、TakiLibを導入済みのブックに置く、単独で実行できる確認用マクロです。公開安定版3.5.4の通常CP932版 `taki3lib.bas` をVBEからインポートし、「明細」というワークシートを用意します。読解用の `taki3libUTF8.bas` をインポートする手順ではありません。

「明細」のA1を「ID」、B1を「備考」とします。A2〜A10に9件分のIDを連続して入れ、B7に「確認済」を入れてください。B8以降、A11以降には値や式を入れません。B2〜B6の入力有無はこの例の終点には影響しません。出力はVBEのイミディエイトウィンドウで確認します。

サンプルコード 列を指定して最終行を比べる

01

Option Explicit

02

03

Public Sub Test_Main()

04

    Dim ws As Worksheet

05

    Set ws = ThisWorkbook.Worksheets("明細")

06

07

    Debug.Print "ID列の終点", taki3lib.rngLastRow(ws, 1)

08

    Debug.Print "備考列の終点", taki3lib.rngLastRow(ws, 2)

09

End Sub

コードの説明

同じワークシートを渡し、列番号だけを1と2に変えてTakiLibを直接呼び出しています。戻り値はどちらも行番号です。このマクロ自体は明細の出力や件数の集計を行いません。

サンプルコード 直接呼び出す関数の原典抜粋

01

Public Function rngLastRow(Optional ByVal a_sheet, Optional ByVal a_col_num)

02

    rngLastRow = rngLastRowRange(a_sheet, a_col_num).Row

03

End Function

この3行はTakiLibの読解用抜粋です。すでに導入したライブラリと重複するため、ブックへ追加するコードではありません。内部の `rngLastRowRange` は、シートと列番号を補ったうえで、`a_sheet.Cells(a_sheet.Rows.Count, a_col_num).End(xlUp)` を返します。

結果

上記の入力条件では、ID列の終点は10、備考列の終点は7になると、ソースと公式仕様から読み取れます。A20に `=""` を入れた別条件では、ID列の終点は20になります。空列・見出しだけの場合はいずれも行番号1です。

掲載コードは今回Excelで実行検証していません。ここに示した値は、確認したソースと入力条件に基づく期待値です。実際の表に適用するときは、必須IDの欠落、空文字数式、対象外の行など、業務の条件を加えて確認してください。

出典メモ

  • 元ネタ:TK-074「最終行は『何をデータと呼ぶか』で変わる」(2026年9月7日整理)。

  • TakiLib 3.5.4、通常CP932版 `taki3lib.bas` の `rngLastRow` と `rngLastRowRange`。2026年9月27日確認。

  • Microsoft Learn「Range.End property (Excel)」。2026年9月27日確認。