【Power Query】年月ごとにCSVの最新版だけを採用する

「作成日時つきCSVから年月ごとに最新版だけ採用する」というタイトルと、Table.Groupで年月ごとの最大の作成日時を求めるPower Queryのコードを示した画像 Power Query

毎月届くCSVを、上書きせずに作成日時つきで同じフォルダへ溜めていく運用があります。訂正版が出ても古い版が残るので、履歴をたどれます。

ところが、このフォルダをPower Queryでまとめて読むと、同じ年月の行が版の数だけ重なって、合計が膨らみます。

この記事では、同じ年月に複数の版があるとき、作成日時が最新の版だけを残す書き方を紹介します。フォルダ内のCSVを読み込む基本は、【Power Query】フォルダ内データを自動取込みする設定で扱っています。

前提:CSVの形と溜まり方

CSVは5列(Person・Item・Amount・YearMonth・CreatedAt)です。作成日時(CreatedAt)はCSVの列にも入れ、ファイル名にも同じ日時を入れます。検証用に、次の5ファイルを同じフォルダに置きました。

年月作成日時行数Amount合計
2026072026-08-03 10:00:00190
2026082026-09-01 09:00:003600
2026082026-09-03 12:00:001105
2026082026-09-05 17:30:004660
2026092026-10-02 09:00:002330

全部を読むと11行で、Amountの合計は1,785です。残したいのは、各年月で作成日時が最新の3ファイル(7行、合計1,080)です。202608には3つの版があり、最新は2026-09-05 17:30:00の版です。

完成コード:年月ごとに最新版だけを残す

let
    FolderPath = "C:\Data\csv_archive",
    Files = Table.SelectRows(Folder.Files(FolderPath), each Text.Lower([Extension]) = ".csv"),
    LoadOne = (content as binary, fileName as text) as table =>
        let
            t = Table.PromoteHeaders(
                Csv.Document(content, [Delimiter = ",", Encoding = 65001, QuoteStyle = QuoteStyle.Csv]),
                [PromoteAllScalars = true]),
            u = Table.AddColumn(t, "SourceFile", each fileName, type text)
        in
            u,
    Combined = Table.Combine(List.Transform(Table.ToRecords(Files), each LoadOne([Content], [Name]))),
    Typed = Table.TransformColumns(Combined, {
        {"CreatedAt", each DateTime.FromText(_, [Format = "yyyy-MM-dd HH:mm:ss", Culture = "en-US"]), type datetime},
        {"Amount", each Number.FromText(_, "en-US"), Int64.Type}}),
    Latest = Table.Group(Typed, {"YearMonth"}, {{"MaxCreated", each List.Max([CreatedAt]), type datetime}}),
    Joined = Table.NestedJoin(Typed, {"YearMonth", "CreatedAt"}, Latest, {"YearMonth", "MaxCreated"}, "J", JoinKind.Inner),
    Result = Table.RemoveColumns(Joined, {"J"})
in
    Result

FolderPathだけを自分のフォルダに書き換えれば動きます。

解説

1. フォルダ内のCSVを1つにまとめる

Folder.Filesでフォルダ内のファイルを一覧にし、拡張子が.csvのものだけを残します。検証用フォルダにはreadme.txtも置きましたが、6ファイルのうちCSVの5ファイルだけが読まれました。拡張子で絞っておくと、CSV以外のファイルが混ざっても止まりません。

CSVはCsv.Documentで1つずつ読み、SourceFile列にファイル名を足してからTable.Combineで縦に連結します。SourceFileは、あとで「どのファイルが残ったか」を確かめるために使います。

Encoding = 65001はUTF-8です。Shift_JISのCSVなら932にします。文字コードが混ざるときの判定は、【Power Query】CSVの文字コードを置換文字で判定するで扱っています。

2. 作成日時はFormatとCultureを指定して日時型にする

CSVから読んだ列は、すべてテキストです。最大値を求める前に、作成日時を日時型にします。

DateTime.FromTextは、Formatを省略するとテキストを推測して読みます。Cultureを省略すると、現在のロケールが使われます。引数の仕様は、Microsoft LearnのDateTime.FromTextにあります。

ロケールが日本語(日本)のPCで、Cultureを省略したときと、Cultureを指定したときを比べました。

読ませた文字列省略ja-JPen-USen-GB
10/02/2026 09:00:002026-10-022026-10-022026-10-022026-02-10
02/10/2026 09:00:002026-02-102026-02-102026-02-102026-10-02

2026-10-02 09:00:00、2026/10/02 09:00:00、2026-10-02T09:00:00、2026/10/2 9:00:00は、どの場合も2026-10-02 09:00:00になりました。20261002090000は、どの場合もエラーでした。

スラッシュ区切りで年が最後の形は、日本語(日本)のPCでも月が先に読まれます。en-GBを指定すると、日と月が入れ替わります。しかも、エラーにならずに別の日付になります。

Formatを指定すると、様子が変わります。Formatをyyyy-MM-dd HH:mm:ssにして同じ7つの文字列を読ませると、一致する形の1つだけが通り、ほかの6つはエラーになりました。10/02/2026のような文字列が、別の日付として読まれることはありません。エラーで止まります。

3. 文字列のまま最大を取ると、最新版を取り違える

作成日時をテキストのままList.Maxにかけると、文字の並びで大小が決まります。0埋めのない2026/10/9 9:00:00と2026/10/15 9:00:00では、文字として大きいのは9日のほうでした。日時型にしてから比べると、15日が最大になります。

2026-10-09 09:00:00のように0埋めした形式なら、テキストのままでも結果は同じでした。ただ、CSVを出力する側の書式が変わっても壊れないように、日時型にしてから比べます。

4. 年月でGroupして最大を出し、Inner結合で最新版だけにする

Table.Groupで、年月ごとの最大の作成日時を出します。この表と元のテーブルを、年月と作成日時の2列をキーにしてJoinKind.Innerで結合します。作成日時が最大と一致する行だけが残ります。結合でできるJ列は使わないので、展開せずに削除します。

Latestは年月ごとに1行なので、結合しても元の行は増えません。行が増える結合は、【Power Query】結合で行が増える原因と重複の検知方法で扱っています。

202608は、版が3つあったので8行ありましたが、最新版の4行だけになりました。Satoの金額は、100(2026-09-01)、105(2026-09-03)、110(2026-09-05)の3つがあり、110だけが残りました。Itoは最新版にだけある行で、残りました。

左に全部のCSVを読んだ11行の表、右に年月ごとに作成日時が最新の版だけを残した7行の表を並べたExcelの画面
再現用の最小コードの実行結果

結合キーの型は、左右でそろえます。作成日時が左はテキスト、右は日時型だと、一致する行がなく0行になりました。型の違いによる結合の失敗は、【Power Query】キーの型がバラバラで結合できない時の対処法で扱っています。

Table.Joinでも同じ結合ができます。左右のキー列が同じ名前なら、1列にまとまります。今回の環境では、左右に同じ名前のYearMonth列があるままでも7行で結合できました。ただし、キー以外の列名が左右で重なると、「結合処理で、重複する列名を含むテーブルを生成することはできません」というエラーになります。この記事では、結果を入れ子の列Jに入れるTable.NestedJoinを使い、Jを削除しています。

同じ作成日時のファイルが2つあるとき

同じ年月に、作成日時がまったく同じファイルが2つあると、最新が2つになります。検証では、2つのファイルとも残り、2行になりました。

SourceFileの種類が2つ以上ある年月があれば、エラーで止めます。集計に使う取込なら、行が増えたまま先へ進めないほうが安全です。次のCheckを、Resultの結果に通します。同じ年月に最新のファイルが2つある場合は、エラー「DuplicateVersion」で止まり、詳細にその年月(検証では202610)が出ました。通常の7行のときは、そのまま通ります。

Check = (t as table) as table =>
    let
        g = Table.Group(t, {"YearMonth"}, {{"Files", each List.Count(List.Distinct([SourceFile])), Int64.Type}}),
        d = Table.SelectRows(g, each [Files] > 1)
    in
        if Table.RowCount(d) > 0
        then error Error.Record("DuplicateVersion", "multiple files share the latest CreatedAt", Text.Combine(d[YearMonth], ","))
        else t

まとめ

  • 溜めたCSVは、全部読んでから、年月ごとの最大の作成日時で絞ります
  • 作成日時は、FormatとCultureを指定して日時型にしてから比べます
  • 年月でGroupして最大を出し、年月と作成日時の2列でInner結合すると、最新版の行だけが残ります
  • 同じ年月に同じ作成日時のファイルが2つあると、両方残ります。SourceFileで検知して止めます

直近の数か月だけを比べる処理は、【Power Query】Table.Pivotの月の列は固定リストで渡すで扱っています。

ここでの結果は、Windowsの日本語環境(コードページ932)のExcel 16.0で、Power Queryのロケールが日本語(日本)の状態で確認したものです。Cultureを省略した結果は、ロケールで変わります。

コメント

タイトルとURLをコピーしました