ゴールのイメージを先にそろえる
今回のテーマは「フォルダに入っている複数の CSV を、Power Query で一括取込して、毎回[更新]ボタンだけで最新状態に保てる“実務テンプレ”を作ること」です。
毎日や毎月、同じ形式の CSV が何個も出てきて、それを手作業でコピペしている状況を、丸ごと自動化するイメージです。
キーワードは「Folder.Files」と「Csv.Document」と「ヘッダーの扱い」です。
ここを押さえれば、あとはパターン化してどの現場にも持ち込めるようになります。
フォルダ一括取込の全体像
フォルダを“データベース”として扱う発想
まず発想として、フォルダを「CSV がどんどん貯まっていく箱」ではなく、「CSV テーブルがたくさん入ったデータベース」として見る、というイメージを持ってください。
Power Query には Folder.Files という関数があり、これを使うと「フォルダ内のファイル一覧」をテーブルとして取得できます。
このテーブルには、ファイル名、拡張子、更新日時、フルパス、そして一番重要な「Content(バイナリ)」が入っています。
この Content 列が、実際の CSV データ本体です。ここに対して Csv.Document をかけることで、各ファイルをテーブルに変換していきます。
もっとも基本的なテンプレの流れ
Folder.Files でファイル一覧を取る
まずはフォルダを指定して、ファイル一覧をテーブルとして取得します。M コードの最小例は次のようになります。
let
Source = Folder.Files("C:\Data\IncomingCsv")
in
Source
これを実行すると、Name、Extension、Date accessed、Date modified、Attributes、Folder Path、Content などの列を持つテーブルが返ってきます。
ここで重要なのは、拡張子で絞り込むことと、Content 列を後で使うことです。
拡張子で絞り込む例は次のようになります。
Filtered = Table.SelectRows(Source, each [Extension] = ".csv")
これで「CSV だけ」に対象を絞れます。
各ファイルの Content を Csv.Document でテーブル化する
次に、各行(各ファイル)の Content 列に対して Csv.Document を適用し、テーブルに変換します。
Power Query の UI から「結合」ウィザードを使うと自動生成されますが、仕組みを理解するために素直な形で書いてみます。
AddedTables =
Table.AddColumn(
Filtered,
"CsvTable",
each
Csv.Document(
[Content],
[
Delimiter = ",",
Encoding = 65001,
QuoteStyle = QuoteStyle.Csv
]
)
)
ここでやっていることは、「Filtered テーブルに CsvTable という新しい列を追加し、その中に各ファイルの CSV をテーブルとして格納する」という処理です。[Content] は、その行の Content 列(バイナリ)を指しています。
ヘッダー昇格と列名の統一
多くの場合、CSV の 1 行目はヘッダー(列名)になっています。
その場合、Table.PromoteHeaders を使って、先頭行をヘッダーに昇格させます。
AddedTablesWithHeader =
Table.TransformColumns(
AddedTables,
{
"CsvTable",
each
Table.PromoteHeaders(
_,
[PromoteAllScalars = true]
)
}
)
ここでは、CsvTable 列に入っている各テーブルに対して、ヘッダー昇格を一括で適用しています。
この時点で、すべての CSV が「同じ列名・同じ構造」であることが前提になります。
もし列名がバラバラだと、後で結合するときに列がズレたり、Null が増えたりします。
すべてのテーブルを縦に結合する
最後に、CsvTable 列を展開して、全ファイル分のテーブルを縦に結合します。
代表として 1 つ目のテーブルから列名を取得し、それを展開に使うのが定番パターンです。
SampleTable = AddedTablesWithHeader[CsvTable]{0},
ColumnNames = Table.ColumnNames(SampleTable),
Expanded =
Table.ExpandTableColumn(
AddedTablesWithHeader,
"CsvTable",
ColumnNames,
ColumnNames
)
これで、フォルダ内のすべての CSV が、1 つの大きなテーブルとして扱えるようになります。
あとはこの Expanded をクエリの出力にすれば、「フォルダ内 CSV 一括取込」の基本テンプレが完成です。
重要ポイントの深掘り
なぜ「Content → Csv.Document」という二段階なのか
Folder.Files が返す Content 列は、「ファイルの中身そのもの(バイナリ)」です。
このままでは中身は読めないので、「これは CSV ですよ」と教えてあげる必要があります。
その役割を果たすのが Csv.Document です。
Csv.Document は、バイナリを「区切り文字」「文字コード」「引用ルール」などの情報をもとに解析し、テーブルに変換します。
ここで Delimiter や Encoding をきちんと指定しておくことが、文字化けや列ズレを防ぐうえで非常に重要です。
特に Encoding は、UTF-8 なら 65001、Shift-JIS なら 932 など、環境に合わせて明示的に指定するのが実務的です。
「自動判定」に任せると、ファイルによって結果が変わることがあり、トラブルの元になります。
ヘッダー行が各ファイルに含まれている場合の扱い
フォルダ一括取込でよくあるのが、「各 CSV にヘッダー行が含まれている」パターンです。
この場合、単純に縦結合すると、途中に何度もヘッダー行が混ざることになります。
対処としては二段階あります。
一つ目は、各ファイルをテーブル化した直後に Table.PromoteHeaders を行い、「ヘッダー行を列名として吸収してしまう」こと。
二つ目は、結合後のテーブルから「ヘッダーと同じ値を持つ行」を削除することです。
たとえば、日付列の列名が「日付」で、データ部分には「日付」という値が出てこない前提なら、次のように書けます。
Cleaned =
Table.SelectRows(
Expanded,
each [日付] <> "日付"
)
これで、途中に紛れ込んだヘッダー行を一括で取り除けます。
より厳密にやりたい場合は、複数列を組み合わせて「日付=’日付’ かつ 金額=’金額’ かつ …」のように条件を書くこともできます。
ファイル名や更新日を列として持たせる意味
実務では、「この行はどのファイルから来たのか」「どの年月のデータなのか」を後で使いたくなることが非常に多いです。
そのため、結合前にファイル名や更新日を列として追加しておくのがおすすめです。
たとえば、Filtered の段階で次のように列を追加します。
WithMeta =
Table.AddColumn(
Filtered,
"ファイル名",
each [Name],
type text
)
あるいは、ファイル名に年月が含まれているなら、そこから「対象年月」列を作ることもできます。
WithMonth =
Table.AddColumn(
WithMeta,
"対象年月",
each Text.Middle([Name], 6, 7),
type text
)
こうしておけば、結合後のテーブルでも「どの月のデータか」「どのファイル由来か」を簡単に判別できます。
ピボットテーブルや Power BI で集計するときにも、そのまま軸として使えます。
実務での運用イメージ
毎日 CSV が増えていくケース
たとえば、システムから毎日 sales_2024-10-01.csv のようなファイルが出力され、それを C:\Data\DailySales に貯めていくとします。
一度、今日のテンプレでクエリを作っておけば、あとは次の運用だけで済みます。
新しい CSV をフォルダにコピーする。
Excel ファイルを開いて[すべて更新]を押す。
これだけで、過去分+新しい日のデータがすべて反映されたテーブルが手に入ります。
「昨日分をコピペ」「列の幅を調整」「フィルタをかけ直す」といった作業は完全に不要になります。
フォルダを分けるかどうかの判断
データ量が増えてくると、「何年分も同じフォルダに入れ続ける」のが重くなってくることがあります。
その場合は、年ごとにフォルダを分ける、古い年はアーカイブ用フォルダに移す、などの運用も検討できます。
Power Query 側では、複数フォルダを別クエリで読み込んでから Append する、という構成も取れます。
大事なのは、「フォルダ構成とファイル名ルールを最初に決めておく」ことです。
ここが安定していれば、M コードはほとんど触らずに長く使い続けられます。
まとめと「あなたの現場用」にするための一歩
ここまでで、フォルダ内 CSV 一括取込の基本テンプレとして、
Folder.Files でファイル一覧を取ること。
Content 列に対して Csv.Document を適用してテーブル化すること。
ヘッダー昇格と途中ヘッダー削除の考え方。
ファイル名や年月を列として持たせる実務的な工夫。
を一通り押さえました。
