銀行のCSVエクスポートには、毎回同じ種類の厄介な点があります。その一覧を知っておけば、わけのわからないファイルも5分の作業になります。以下の問題はどれも銀行側の書式の判断であり、ファイルが壊れているわけではありません。
- 見出し行の前の前置き行。 口座名、支店コード(sort code)、期間、そして本来の見出し行の前の空行。ファイルを読み込むパーサーは、このうち最初の行を列名として扱います。
- 金額が2列に分かれている。 符号付きの1列ではなく
DebitとCreditの2列になっている場合や、符号付きでも符号の向きが想定と逆の場合があります。 - あいまいな日付。
03/04/2026は英国では4月3日、米国では3月4日で、ファイルにはどちらか書かれていません。 - 説明列に5つの項目が詰め込まれている。 店名、場所、カード番号、参照番号、銀行独自の取引種別です。
- 金額が文字列になっている。 桁区切り、通貨記号、
(45.00)という形のマイナス表記などです。
この5つを直せば、あとは普通のデータです。
まずファイルの形を確認する
何よりも先に、ファイルを開いて先頭の10行を見ます。
head -n 12 statement.csv
前置き行の数を数え、本当の見出し行がどの行かを確認します。そのうえで読み飛ばします。
tail -n +5 statement.csv > clean.csv
表計算ソフトで開く場合も、ほかの作業の前にそれらの行を削除してください。残したまま並べ替えやフィルターをかけると、口座の概要が取引の途中に紛れ込みます。見つけにくく、合計にうっかり含めてしまいがちです。
日付という落とし穴
これは、はっきり壊れるのではなく、間違った答えを出すエラーです。米国形式のパーサーが英国の明細を読むと、12日までの日付はすべて、4月3日が3月4日に黙って変換されます。13日以降はそのままです。結果は一見まったく普通で、月ごとの合計はすべて間違っています。
確実な順に、2つの確かめ方があります。
12より大きい日を探す。 日付列で 25/12/2026 のような値を探します。最初の数字が12を超えていれば、日が先の形式です。10秒で済み、確実です。
覚えている取引と照合する。 記憶から日付を言える取引が1件あれば、並び順はすぐにわかります。
そのうえで、何かに推測させず、明示的に変換します。
duckdb -c "SELECT strptime(\"Transaction Date\", '%d/%m/%Y') d, * FROM read_csv('clean.csv', all_varchar=true)"
大事なのは all_varchar=true のほうです。これで読み込み時の型推測が完全に止まり、すべての変換を自分で決められます。参照番号や口座の数字が浮動小数点数として読み込まれるのも防げます。
金額をそろえる
目指すのは、符号付きの数値が入った1列です。変換元の形は3つあります。
Debit と Credit が別々の列。 出金をマイナスにして結合します。
COALESCE(-TRY_CAST(replace("Debit", ',', '') AS DOUBLE), TRY_CAST(replace("Credit", ',', '') AS DOUBLE), 0)
区切りや記号の入った文字列。 変換の前に、数字、マイナス、小数点以外をすべて取り除きます。£1,234.56 も 1 234,56 もこの処理が必要で、後者は小数点のカンマの扱いも必要です。
会計式のマイナス。 (45.00) は −45.00 を意味します。デフォルトで正しく変換してくれるものはないので、先にかっこを置き換えます。
合計を信用する前に、変換を検証してください。NULLでない金額の数が、取引の行数と一致している必要があります。TRY_CAST が失敗すると静かにNULLになり、SUM の中のNULLは単に無視されます。こうして、はっきり壊れるのではなく、もっともらしく間違った合計ができあがります。
説明列を分割する
役に立つ情報は説明列に隠れていて、1つの文字列として届きます。
CARD PAYMENT TO TESCO STORES 3421 ON 02-04-2026 GBP 43.19
本格的なパーサーが必要になることはまれです。次の2つの操作で、たいていの用は足ります。
店名の列。 気になる店名のリストと照合して作ります。CASE WHEN description LIKE '%TESCO%' THEN 'Tesco' のような句を20個書けば、個人の明細の大半は分類でき、残りの細かい分は自動化する価値がありません。
取引種別の列。 先頭の語から作ります。CARD PAYMENT、DIRECT DEBIT、FASTER PAYMENT、ATM などです。同じエクスポート内なら銀行の表記は一貫していて、種別でグループ化するほうが、店名でグループ化するより役立つことも多くあります。
やらないほうがいいこと。説明文の中から金額や日付を取り出そうとすることです。どちらもすでに列としてあり、文中に埋め込まれたものは書式が違ううえ、食い違うこともあります。
そして本当の問いを立てる
日付、符号付きの金額、店名がそろえば、問いはどれも1行で済みます。
SELECT strftime(d, '%Y-%m') month, sum(amount) FROM tx GROUP BY month ORDER BY month
SELECT merchant, count(*) n, sum(amount) FROM tx WHERE amount < 0 GROUP BY merchant ORDER BY sum(amount) LIMIT 20
SELECT * FROM tx WHERE amount < -100 ORDER BY amount
月ごとの収支、店別の支出の大きい順、100を超えるもの全部。明細を開く目的の大半は、この3つで答えが出ます。
SQLもビジュアルのフィルターエディターも、ファイルがある場所でそのまま実行されます。どこにもアップロードしたくない銀行のエクスポートなら、そこが大事な点です。グラフも同じクエリから作られるので、月ごとの合計はそのまま1ステップで棒グラフになります。
できること最後の点ははっきり言っておく価値があります。銀行の明細は、ほとんどの人が扱うファイルの中でも特に機密性が高いものです。すべての店、すべての場所、毎月の支払い、そして多くの場合は口座番号の一部が含まれます。無料のオンラインCSVビューアーや「明細を変換」するサイトは、ファイル全体を、何も知らないサーバーにアップロードします。このファイルに限っては、ローカルで動くツールを使うのは神経質なのではなく、当然の初期設定です。
誰かに渡すために整える
明細を開く理由は、会計士、大家、経費精算など、誰かがその一部を必要としているからということがよくあります。作業は引き算です。
- 必要な列だけ残します。日付、説明、金額です。残高の推移や口座の数字を含め、ほかは削除します。
- 実際に求められた期間やカテゴリで絞り込みます。
- 相手から聞かれそうなら、カテゴリ列を追加します。
- エクスポートして、出力の合計が絞り込んだ結果の合計と一致することを確認します。
残高の列は、面倒だからと残さず、意識して伏せてください。取引そのものよりずっと多くの情報が漏れるからです。
列の削除、期間での絞り込み、計算によるカテゴリ列の追加は、どれもここで直接編集できます。数式列は結果をCSVに保存するので、お渡しするのは普通のCSVで、相手がどのツールを使っていても正しく開けます。
できること複数の月、複数の口座
銀行は1回に1期間ずつエクスポートするため、1年にわたる問いには複数のファイルが必要です。結合する前にやることが2つあります。
見出しが一致しているか確認する。 銀行は年ごとにエクスポート形式を変えることがあり、列の位置が動いていると、何も確認せずに連結した場合、それ以降の列がすべてずれます。先に見出し行を比べてください。CSVファイルを結合するガイドで、この問題に強い列名ベースの結合を紹介しています。
口座の列を追加する。口座の異なるファイルを結合する前に行います。ないと、自分の口座間の振替が、出金と入金の2回、二重に現れます。それを考慮しない合計はどれも、振替額の2倍だけずれます。出どころの印を付け、そのうえで内部振替を明示的に除外する。それが、意味のある世帯の合計を出す唯一の方法です。