- 技術 💻
突合とは|読み方・意味と照合との違い、データ突合のやり方(SQL・Python)
結論
突合(とつごう)とは、2つ以上のデータや書類を突き合わせて、合っているか・どこが食い違うかを確かめることです。データの突合は、結果を「両方にある」「片方だけにある」「値が違う」の3つに分けて出します。
この記事でわかること
- 読み方と意味、照合・消込・マッチングとの違い
- SQL・pandas・コマンドで突合した実行結果
- キーの揺れ・NULL・重複キーで外れる例と直し方
「突合しておいて」と頼まれて、読み方も手順も分からず検索した、という場面に向けた記事です。前半で言葉の意味を辞書で確かめ、後半で架空の請求データと入金データを実際に突合して、出力をそのまま載せます。この記事で実際に動かしたものは、PostgreSQL と SQLite の SQL、pandas、comm・diff・join の各コマンドです。
flowchart TD
A[一覧A 請求] --> K[キーをそろえる]
B[一覧B 入金] --> K
K --> M{キーで突き合わせる}
M -->|Aだけ| X[片方だけ]
M -->|Bだけ| X
M -->|両方にある| V{値は同じ?}
V -->|はい| OK[一致]
V -->|いいえ| NG[値が違う]
よく調べられることの答えを先にまとめます。
| 知りたいこと | 答え | 詳しい節 |
|---|---|---|
| 突合の読み方 | 「とつごう」。辞書の見出しには「つきあわせ」もある | 突合とは |
| 突合の意味 | 資料やデータを並べて比べ、合っているかを確かめること | 突合とは |
| 突合と照合の違い | ほぼ同じ意味。突合は「2つの一覧を全件比べる」場面でよく使う | 照合などとの違い |
| 突合と消込の違い | 消込は、突合で一致した分を「済み」にしていく後の処理 | 照合などとの違い |
| SQL で突合するには | FULL OUTER JOIN で3つの結果を1回で出す | SQLでデータ突合 |
| Python で突合するには | pandas の merge(indicator=True) | pandasで突合 |
| コマンドで突合するには | sort してから comm・diff・join | コマンドで突合 |
| 件数は合うのに不安 | 件数と合計が同じでも中身が違う例がある。チェックサムも見る | 件数・合計・チェックサム |
| 合うはずなのに合わない | 全角半角・空白・ゼロ埋め・大文字小文字・NULL・重複キーを疑う | キーの揺れ |
確認環境:Ubuntu 24.04.5 LTS の上で、PostgreSQL 16.15 を作業用のフォルダーに initdb し、127.0.0.1 のポートだけで起動して SQL を実行しました。SQLite は Python 3.13.18 の sqlite3 モジュール(SQLite 3.53.1)から同じ SQL を実行しています。Python は pandas 3.0.5・NumPy 2.5.3、コマンドは GNU coreutils 9.4(sort・comm・join・sha256sum・md5sum)と GNU diffutils 3.10 です。データはすべてこの記事のために作った架空のもので、実在の会社・取引とは関係がありません。10万行の計測は2コアのマシンで1回ずつ測った値です。言葉の読みと意味は、コトバンクに載っている精選版 日本国語大辞典と、Weblio に載っている実用日本語表現辞典で確かめました。SQL と pandas の決まりは、PostgreSQL・SQLite・pandas の公式ドキュメントで確かめています。表計算ソフトでの突合は扱っていません。図は mermaid-cli 11.16.0 で描画を確かめています(確認日 2026年11月11日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「突合とは|読み方・意味と照合との違い、データ突合のやり方(SQL・Python)」
突合とは(読み方は「とつごう」・意味)
この節では、突合の読み方と意味を、辞書の記載とあわせて確かめられます。
突合は「とつごう」と読み、2つ以上の資料やデータを並べて比べ、合っているかどうかを確かめることを指します。 実用日本語表現辞典(Weblio)は、読み方を「とつごう・つきあわせ」とし、意味を「資料などを並べて比較対照し、異同や整合性などを確認すること」と説明しています。
一方、精選版 日本国語大辞典(コトバンク)では、「突合」は「つきあわせ」の見出しで載っていて、意味の1つに「両方を照らし合わせること」が挙げられています。つまり、もともとの和語は「突き合わせ(つきあわせ)」で、それを漢字2文字で書いた「突合」を音読みしたものが「とつごう」です。仕事の会話や資料では「とつごう」と読むのが一般的で、今回確かめた2つの辞書に「とっこう」「とつあい」という読みは載っていませんでした。
| 項目 | 内容 |
|---|---|
| 読み方 | とつごう(辞書の見出しには「つきあわせ」もある) |
| 意味 | 資料・データを並べて比べ、同じか違うか、つじつまが合うかを確かめること |
| 動詞の形 | 突合する(とつごうする)/突き合わせる |
| よく一緒に使う語 | 突合作業・突合結果・突合キー・不一致・差異 |
| 英語 | reconciliation、matching と訳されることが多い |
ふだんの事務の言葉としての突合と、IT・データの仕事での突合は、やっていることは同じですが、対象と道具が違います。
| 事務・経理での突合 | IT・データでの突合 | |
|---|---|---|
| 比べるもの | 請求書と入金の記録、発注書と納品書と請求書 | 2つの表・CSV・ログ、移行前と移行後のデータ |
| 手がかり | 請求番号・取引先名・金額 | キー(主キー・ID・コード) |
| 道具 | 目で見比べる、表計算ソフト | SQL・Python・コマンド |
| 出したい結果 | 合わないものの一覧 | 両方にある/片方だけ/値が違う の3つ |
1読みは「とつごう」
辞書の見出しは「つきあわせ」。会話では「とつごう」が通じやすい。
2目的は食い違いを見つけること
合っている分を確かめるだけでなく、合わない分を一覧にして初めて終わる。
3キーが要る
どの行とどの行を比べるかを決める列(請求番号など)がなければ、突合は始められない。
突合と照合・突き合わせ・消込・マッチング・検証の違い
この節では、突合と似た言葉をどう使い分けるかが、1つの表で分かります。
突合と照合は、ほぼ同じ意味で使われます。 辞書の説明でも、突合は「照らし合わせること」と書かれていて、はっきりした線引きはありません。そのうえで、仕事でよく見る使い分けを整理すると次のようになります。会社やチームで言い方が決まっているときは、そちらに合わせるのが確実です。
| 言葉 | 読み | よく使う場面 | 突合との関係 |
|---|---|---|---|
| 突合 | とつごう | 2つの一覧を全件比べ、食い違いを洗い出す | ― |
| 突き合わせ | つきあわせ | 突合と同じ。話し言葉でやわらかい | 同じ意味の和語 |
| 照合 | しょうごう | 基準になるものと比べて確かめる(本人確認・指紋・パスワードなど) | ほぼ同じ。1件ずつ確かめる場面に多い |
| 消込 | けしこみ | 入金と請求を突き合わせ、一致した請求を「済み」にする | 突合のあとに行う処理 |
| マッチング | ― | 条件に合う相手どうしを結び付ける。あいまいな一致も含む | 突合の「結び付ける」部分 |
| 検証 | けんしょう | 正しいかどうかを確かめること全般 | 突合は検証の方法の1つ |
突合と言うことが多い
請求の一覧と入金の一覧を全件比べる
移行前と移行後のデータを比べる
発注書・納品書・請求書の3つを比べる(三点突合)
照合と言うことが多い
入力されたパスワードを登録済みの値と比べる
届いた荷物を伝票と比べる
データベースの文字の比べ方の決まり(照合順序)
三点突合は、発注書・納品書・請求書の3つの書類で、品目・数量・金額が同じかを確かめる作業の呼び名です。2つの一覧の突合を、組み合わせを変えて行うと考えると分かりやすくなります。
突合を使う場面(ビジネスとIT)と例文
この節では、突合という言葉がどんな仕事で出てくるのかと、そのまま使える例文が分かります。
「同じはずの2つ」があるところには、突合があります。 片方は自分たちの記録、もう片方は別の仕組みや相手から来た記録、という組み合わせが典型です。
| 場面 | 突き合わせるもの | キーの例 | 見つけたいもの |
|---|---|---|---|
| 請求と入金 | 請求の一覧と、銀行の入金の一覧 | 請求番号・金額 | 未入金・金額の違い・身に覚えのない入金 |
| 在庫 | 帳簿の在庫数と、数えた在庫数 | 品目コード | 数の差 |
| データ移行 | 移行前の表と、移行後の表 | 主キー | 移っていない行・値が変わった行 |
| ログ | 送った側のログと、受けた側のログ | 処理番号・時刻 | 届いていない処理・二重の処理 |
| マスタの件数 | 本番と控え、システムAとシステムB | コード | 登録もれ・件数の差 |
例文です。
- 請求データと入金データを突合し、差異を一覧にしました。
- 移行前後で件数を突合したところ、3件の不一致が見つかりました。
- 商品マスタと注文ログを突合して、マスタに登録のないコードを洗い出します。
- 突合の結果、片方にしかないデータが2件ありました。原因を調べています。
- 突合キーは請求番号とし、金額が1円でも違えば不一致として扱います。
例文の最後のように、「何をキーにするか」「どこまで同じなら一致とするか」を先に決めて書いておくと、あとで結果の読み方がぶれません。
データ突合のやり方の全体像(3つの結果と突合キー)
この節では、道具が何であっても共通する、データ突合の手順と出すべき結果が分かります。
データ突合の結果は、「両方にあって値も同じ」「両方にあるが値が違う」「片方だけにある」に分かれます。 片方だけは、どちらにあるかで2つに分かれるので、数えると4通りです。この記事では、次の架空のデータを最後まで使います。請求が6件、入金が5件です。
seikyu(請求) nyukin(入金)
invoice_no customer amount invoice_no amount
INV-001 青葉商事 120000 INV-001 120000
INV-002 北斗工業 88000 INV-002 80000 ← 金額が違う
INV-003 みなと物産 45000 INV-003 45000
INV-004 山吹電機 230000 (なし) ← 請求だけ
INV-005 東雲システム 66000 INV-005 66000
INV-006 若葉運輸 15000 (なし) ← 請求だけ
(なし) INV-007 30000 ← 入金だけ
どの道具でも、この「3・1・2・1」が出れば正しく突合できています。手順は次のとおりです。
突合キーを決める
行を1つに決められる列を選ぶ。ここでは請求番号(invoice_no)。1つの列で決まらなければ、複数の列を組み合わせる。
キーが重複していないか数える
同じキーが2行あると、突き合わせた結果の行が増える。先に数えて、あれば集計してから比べる。
キーの書き方をそろえる
全角と半角、前後の空白、ゼロ埋め、大文字と小文字をそろえる。そろえる前の値も残しておく。
件数と合計を比べる
全体が合っているかを最初に見る。合わなければ、どのくらい違うかの見当がつく。
キーで突き合わせ、3つの結果に分ける
一致・値が違う・片方だけを出し、それぞれの件数を数える。件数の合計が、元の行数と合うかも確かめる。
合わなかった分の原因を調べる
本当の食い違いか、書き方の揺れかを分ける。揺れなら手順3に戻って決まりを足す。
SQLでデータ突合:FULL OUTER JOIN(SQLiteとPostgreSQL)
この節では、2つの表を SQL で突合して、3つの結果を1回の問い合わせで出す書き方が分かります。
FULL OUTER JOIN(完全外部結合)は、両方の表の行をすべて残して結び付けるので、突合にそのまま使えます。 相手がいない側の列は NULL(値がない印)になるため、NULL かどうかで「片方だけ」を見分けます。
SELECT COALESCE(s.invoice_no, n.invoice_no) AS invoice_no,
s.amount AS seikyu, n.amount AS nyukin,
CASE WHEN n.invoice_no IS NULL THEN '請求だけ'
WHEN s.invoice_no IS NULL THEN '入金だけ'
WHEN s.amount <> n.amount THEN '値が違う'
ELSE '一致' END AS kekka
FROM seikyu s FULL OUTER JOIN nyukin n ON s.invoice_no = n.invoice_no
ORDER BY 1;
PostgreSQL 16.15 と SQLite 3.53.1 で同じ SQL を実行し、同じ結果になりました。
invoice_no|seikyu|nyukin|kekka
INV-001|120000|120000|一致
INV-002|88000|80000|値が違う
INV-003|45000|45000|一致
INV-004|230000||請求だけ
INV-005|66000|66000|一致
INV-006|15000||請求だけ
INV-007||30000|入金だけ
(7 rows)
結果ごとの件数は、外側を GROUP BY で包むと出せます。
SELECT kekka, COUNT(*) AS kensu FROM (
SELECT CASE WHEN n.invoice_no IS NULL THEN '請求だけ'
WHEN s.invoice_no IS NULL THEN '入金だけ'
WHEN s.amount <> n.amount THEN '値が違う'
ELSE '一致' END AS kekka
FROM seikyu s FULL OUTER JOIN nyukin n ON s.invoice_no = n.invoice_no) t
GROUP BY kekka ORDER BY kekka;
kekka|kensu
一致|3
値が違う|1
入金だけ|1
請求だけ|2
- CASE の順番が大切です。 先に「相手がいない」を判定し、そのあとで値を比べます。逆にすると、NULL との比較になって判定がずれます。
COALESCE(s.invoice_no, n.invoice_no)は、左から見て最初の NULL でない値を返します。入金だけの行でも請求番号を表示するために使っています。- SQLite が FULL OUTER JOIN に対応したのは 3.39.0(2022年6月25日)です。それより前の版の SQLite や、FULL OUTER JOIN のないデータベースでは、LEFT JOIN を2回書いて UNION でつなぐ形にします。
結合の種類ごとの違いは関連記事「SQLのJOINの違い|INNER JOINとLEFT JOINの使い分け」で、FULL OUTER JOIN の使えないデータベースでの書き方は関連記事「SQL CROSS JOIN・FULL OUTER JOINとは|例とMySQL代替」で扱っています。
EXCEPTとNOT EXISTSで「片方だけ」を出す
この節では、「こちらにあって、あちらにない行」だけを出したいときの2つの書き方と、結果の違いが分かります。
EXCEPT は「上の結果から、下の結果にある行を除く」演算です。 行のすべての列を比べるので、キーが同じでも金額が違えば残ります。
SELECT invoice_no, amount FROM seikyu
EXCEPT
SELECT invoice_no, amount FROM nyukin
ORDER BY 1;
invoice_no|amount
INV-002|88000
INV-004|230000
INV-006|15000
上下を入れ替えると、入金の側から見た結果になります。EXCEPT は片方向なので、突合では必ず両方向を実行します。
invoice_no|amount
INV-002|80000
INV-007|30000
「キーが相手にない行」だけを、ほかの列も付けて出したいときは NOT EXISTS を使います。
SELECT s.invoice_no, s.customer, s.amount
FROM seikyu s
WHERE NOT EXISTS (SELECT 1 FROM nyukin n WHERE n.invoice_no = s.invoice_no)
ORDER BY 1;
invoice_no|customer|amount
INV-004|山吹電機|230000
INV-006|若葉運輸|15000
| 書き方 | 出るもの | 向いている場面 |
|---|---|---|
| FULL OUTER JOIN | 一致・値が違う・片方だけを1回で | 突合の結果を一覧にしたい |
| EXCEPT(両方向) | 行の中身が1列でも違う行 | 列が多く、全列が同じかを確かめたい |
| NOT EXISTS | キーが相手にない行 | 未入金・登録もれだけを出したい |
SQLite と PostgreSQL の書き方の違い
同じ SQL を2つのデータベースで実行して、違いが出たところです。
| 書き方 | PostgreSQL 16.15 | SQLite 3.53.1 |
|---|---|---|
| FULL OUTER JOIN | 使える | 使える(3.39.0 から) |
| EXCEPT | 使える | 使える |
| EXCEPT ALL | 使える | near "ALL": syntax error |
a IS DISTINCT FROM b | 使える | 使える(3.39.0 から) |
a IS NOT b(値どうし) | syntax error at or near "b" | 使える |
| 文字の列 = 整数の列 | operator does not exist: text = integer | エラーにならず、00123 と 123 が一致した |
EXCEPT は重複した行を1つにまとめてから引きます。同じ行が2回あることも見分けたいときは、PostgreSQL では EXCEPT ALL が使えます。SQLite では、行ごとの件数を GROUP BY で数えてから比べます。
Python(pandas)で突合:merge(indicator=True)とcompare
この節では、CSV などの2つの表を pandas で突合する書き方と、出力の読み方が分かります。
pandas の merge に how="outer" と indicator=True を付けると、行がどちらにあったかを示す _merge 列が付きます。 値は both(両方)・left_only(左だけ)・right_only(右だけ)の3つです。
import pandas as pd
s = pd.read_csv("seikyu.csv", dtype={"invoice_no": "string"})
n = pd.read_csv("nyukin.csv", dtype={"invoice_no": "string"})
m = s.merge(n, on="invoice_no", how="outer", suffixes=("_seikyu", "_nyukin"),
indicator=True, validate="one_to_one")
print(m[["invoice_no", "amount_seikyu", "amount_nyukin", "_merge"]])
print()
print(m["_merge"].value_counts())
both = m[m["_merge"] == "both"]
print()
print(both[both["amount_seikyu"] != both["amount_nyukin"]][["invoice_no", "amount_seikyu", "amount_nyukin"]])
invoice_no amount_seikyu amount_nyukin _merge
0 INV-001 120000.0 120000.0 both
1 INV-002 88000.0 80000.0 both
2 INV-003 45000.0 45000.0 both
3 INV-004 230000.0 NaN left_only
4 INV-005 66000.0 66000.0 both
5 INV-006 15000.0 NaN left_only
6 INV-007 NaN 30000.0 right_only
_merge
both 4
left_only 2
right_only 1
Name: count, dtype: int64
invoice_no amount_seikyu amount_nyukin
1 INV-002 88000.0 80000.0
bothの4件のうち、金額が違うのは1件でした。一致3・値が違う1・請求だけ2・入金だけ1で、SQL の結果と同じです。- 相手のいない行に NaN(値がない印)が入るため、金額の列が小数(120000.0)に変わっています。整数のまま扱いたいときは、読み込むときに
dtype={"amount": "Int64"}を指定します。 validate="one_to_one"は、キーが両方の表で重複していないことを確かめる指定です。重複があるとエラーで止まるので、突合では付けておくと安心です。- 請求番号を
dtype="string"で読んでいるのは、00123のような値が数値に変わって先頭のゼロが消えるのを防ぐためです。
同じ形の2つの表は compare で比べる
DataFrame.compare は、行と列の並びが同じ2つの表を比べて、違うセルだけを出します。
import pandas as pd
old = pd.DataFrame({"code": ["A1", "A2", "A3"], "name": ["ねじA", "ねじB", "ねじC"], "price": [100, 250, 80]}).set_index("code")
new = pd.DataFrame({"code": ["A1", "A2", "A3"], "name": ["ねじA", "ねじB", "ねじC"], "price": [100, 250, 85]}).set_index("code")
print(old.compare(new, result_names=("old", "new")))
print()
new2 = new.drop(index="A3")
try:
old.compare(new2)
except ValueError as e:
print("ValueError:", e)
name price
old new old new
code
A2 ねじB ねじB NaN NaN
A3 NaN NaN 80.0 85.0
ValueError: Can only compare identically-labeled (both index and columns) DataFrame objects
A2 は名前の「B」が全角に変わったこと、A3 は価格が 80 から 85 に変わったことが分かります。同じところは NaN で表示されます。下のエラーは、片方の表から1行を除いて比べたときのものです。compare は行や列が増減した表には使えないので、先に merge で「片方だけ」を洗い出し、両方にある行どうしを compare で比べます。
10万行を突合したときの時間
10万行の表と、そこから1%の行を抜き、0.5%の値を変え、500行を足した表(99,511行)を突合しました。結果はどの方法でも「片方だけ 989行と500行、値が違う 485行」で一致しています。
SQLite で、キーの列に索引を作らずに同じ FULL OUTER JOIN を実行すると、25秒待っても終わらなかったため打ち切りました。実行計画は両方の表の全件読み(SCAN)でした。SQLite で大きな表を突合するときは、先にキーの列に索引を作ります。
コマンドで突合:sort+comm・diff・join
この節では、データベースや Python を使わずに、Linux や Mac のコマンドだけでテキストファイルを突合する方法が分かります。
comm は、並べ替え済みの2つのファイルを1行ずつ比べて、「1つ目だけ」「2つ目だけ」「両方」の3列に分けるコマンドです。 -1・-2・-3 は、その列を出さない指定です。まず、CSV からキーの列だけを取り出して並べ替えます。
export LC_ALL=C
tail -n +2 seikyu.csv | cut -d, -f1 | sort > s_keys.txt
tail -n +2 nyukin.csv | cut -d, -f1 | sort > n_keys.txt
$ comm -12 s_keys.txt n_keys.txt # 両方にある
INV-001
INV-002
INV-003
INV-005
$ comm -23 s_keys.txt n_keys.txt # 請求だけ
INV-004
INV-006
$ comm -13 s_keys.txt n_keys.txt # 入金だけ
INV-007
値の違いまで見るには、キーと金額を並べた2つのファイルを diff で比べます。
$ diff -u s_kv.txt n_kv.txt
--- s_kv.txt
+++ n_kv.txt
@@ -1,6 +1,5 @@
INV-001,120000
-INV-002,88000
+INV-002,80000
INV-003,45000
-INV-004,230000
INV-005,66000
-INV-006,15000
+INV-007,30000
diff は行の単位で比べるので、「値が違う」のか「片方だけ」なのかは、出力を見て自分で読み分けることになります。キーで結び付けて横に並べたいときは join を使います。-a1 -a2 は相手のいない行も出す指定、-e は空の欄に入れる文字、-o は出す列の指定です。
$ join -t, -a1 -a2 -e '-' -o 0,1.2,2.2 s_kv.txt n_kv.txt
INV-001,120000,120000
INV-002,88000,80000
INV-003,45000,45000
INV-004,230000,-
INV-005,66000,66000
INV-006,15000,-
INV-007,-,30000
この出力に awk で判定の列を足すと、SQL と同じ一覧になりました。
$ join -t, -a1 -a2 -e '-' -o 0,1.2,2.2 s_kv.txt n_kv.txt \
| awk -F, '{r=($2=="-")?"入金だけ":($3=="-")?"請求だけ":($2==$3)?"一致":"値が違う"; print $0","r}'
INV-001,120000,120000,一致
INV-002,88000,80000,値が違う
INV-003,45000,45000,一致
INV-004,230000,-,請求だけ
INV-005,66000,66000,一致
INV-006,15000,-,請求だけ
INV-007,-,30000,入金だけ
comm と join は、並べ替えていないファイルを渡すとエラーになります。 並べ替えを逆順にしたファイルで試したときの文言です。
$ comm -12 s_keys.txt n_rev.txt
comm: file 2 is not in sorted order
comm: input is not in sorted order
$ join -t, s_kv.txt n_kv_rev.txt
join: n_kv_rev.txt:2: is not sorted: INV-005,66000
join: input is not in sorted order
sort の並び順は言語の設定(ロケール)で変わるため、sort・comm・join を同じ LC_ALL=C の下で実行すると順序の食い違いを防げます。diff の出力の読み方は関連記事「diffコマンドの使い方|-uの読み方・patch適用・オプション一覧」で扱っています。
件数・合計・チェックサムで突合する
この節では、明細を1行ずつ比べる前に全体が合っているかを確かめる方法と、その限界が分かります。
件数と合計の突合は速くて分かりやすい反面、「合っていれば中身も同じ」とは言えません。 請求と入金の例では、件数も合計も違うので、食い違いがあることがすぐ分かります。
SELECT 'seikyu' AS hyo, COUNT(*) AS kensu, SUM(amount) AS gokei FROM seikyu
UNION ALL
SELECT 'nyukin', COUNT(*), SUM(amount) FROM nyukin;
hyo|kensu|gokei
seikyu|6|564000
nyukin|5|341000
次は、件数も合計も同じなのに中身が違う例です。移行前(old.csv)の ID-1 が 500 多く、ID-2 が 500 少なく移ったと考えてください。
old.csv new.csv
ID-1,1000 ID-1,1500
ID-2,2000 ID-2,1500
ID-3,3000 ID-3,3000
old.csv 件数=3 合計=6000
new.csv 件数=3 合計=6000
$ sha256sum old.csv new.csv
15c6063c090af1c189bf7b02db25e66999cf85ef1a0d0071074b65455f15d3ae old.csv
fda37535c0dff1b20d763899c58489f23728f3a1d2553db4670154ad98fc240e new.csv
件数も合計も 3件・6000 で一致しましたが、チェックサム(中身から計算した短い値。1文字でも違えば変わる)は別の値になりました。増えた分と減った分が打ち消し合うと、合計の突合では見つけられません。
ただし、チェックサムは中身がまったく同じときにしか一致しません。行の順番や改行の種類が違うだけでも変わります。
| 比べたもの | チェックサム | 読み取れること |
|---|---|---|
| old.csv と、行の順番だけ入れ替えたファイル | 一致しない | 順番が違うだけで変わる |
| 両方を sort してから計算 | 一致した | 並べ替えてから比べる |
| old.csv と、改行を CRLF にしたファイル | 一致しない | 改行の種類でも変わる |
| CRLF のファイルから CR を除いて計算 | 一致した | 改行をそろえてから比べる |
データベースの中身とファイルを比べるときも、同じ形の文字列にそろえれば突合できます。PostgreSQL で請求の表を「請求番号,金額」の行にして順に並べ、md5 を計算した値は、CSV から同じ形を作って md5sum で計算した値と一致しました。
-- PostgreSQL
SELECT COUNT(*), SUM(amount),
md5(string_agg(invoice_no || ',' || amount, E'\n' ORDER BY invoice_no))
FROM seikyu;
6|564000|cbeac45c00176541421ae556e37c2e03
$ tail -n +2 seikyu.csv | cut -d, -f1,3 | sort | head -c -1 | md5sum
cbeac45c00176541421ae556e37c2e03 -
- 1件数を比べる行の増減が分かる。いちばん速い
- 2合計を比べる数値の列のずれが分かる。打ち消し合うと見えない
- 3チェックサムを比べる同じか違うかが確実に分かる。どこが違うかは分からない
- 4キーで明細を突合するどの行のどの値が違うかまで分かる
上から順に進め、合わなかった段階で下の段に進むと、手間を小さくできます。移行のときに件数を確かめる SQL の書き方は関連記事「データ移行のマッピング定義書の書き方|項目テンプレートと件数検証SQL」で扱っています。
キーの揺れで突合が外れる例と直し方(全角半角・空白・ゼロ埋め)
この節では、同じはずのキーが一致しない原因と、書き方をそろえて一致させる手順が分かります。
突合で「合うはずなのに合わない」ときは、データの食い違いではなく、キーの書き方の違いであることがよくあります。 入金の側の請求番号を、わざと5通りに崩して突合しました。
| 入金の側のキー | 崩し方 | そのまま突合 | そろえたあと |
|---|---|---|---|
inv-001 | 小文字 | 一致しない | INV-001 |
␣INV-003␣ | 前後に半角の空白 | 一致しない | INV-003 |
INV-005+全角の空白 | 後ろに全角の空白 | 一致しない | INV-005 |
INV-004 | 全角の英数字と記号 | 一致しない | INV-004 |
INV-2 | ゼロ埋めなし | 一致しない | INV-002 |
import unicodedata, re
def norm(key):
k = unicodedata.normalize("NFKC", key).strip().upper() # 全角→半角、前後の空白、大文字
m = re.fullmatch(r"INV-(\d+)", k)
return f"INV-{int(m.group(1)):03d}" if m else k # 数字を3桁にゼロ埋め
n3["invoice_no"] = n3["invoice_no"].map(norm)
'inv-001' -> 'INV-001'
' INV-003 ' -> 'INV-003'
'INV-005\u3000' -> 'INV-005'
'INV-004' -> 'INV-004'
'INV-2' -> 'INV-002'
そのまま: 0 件が一致 / 正規化後: 5 件が一致
そのままでは5件とも一致せず、そろえたあとは5件とも一致しました。NFKC は Unicode の正規化の1つで、全角の英数字や記号、全角の空白を半角にそろえます。
SQL でそろえる場合は、UPPER(TRIM(列)) が手軽ですが、効く範囲に限りがあります。同じデータを PostgreSQL と SQLite で試すと、どちらも次の結果でした。
| 結合の条件 | 一致した件数 | 残ったもの |
|---|---|---|
s.invoice_no = n.invoice_no | 0件 | すべて |
s.invoice_no = UPPER(TRIM(n.invoice_no)) | 2件 | 全角の空白・ゼロ埋めなし |
TRIM は、PostgreSQL でも SQLite でも全角の空白を取りませんでした。 TRIM('INV-005 ') の長さは8文字のままです。全角が混ざるデータは、取り込む前に Python などでそろえるか、取り除く文字を明示して指定します。
ゼロ埋めと型の違い
移行前はコードが文字(00123)、移行後は整数(123)という表を突合しました。
-- PostgreSQL
ERROR: operator does not exist: text = integer
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
-- SQLite(エラーにならず一致した)
old_code|new_code
00123|123
00456|456
PostgreSQL は型が違うと止まるので、CAST(o.code AS INTEGER) = n.code のように、どちらにそろえるかを自分で書きます。SQLite は自動で数値として比べたため一致しました。便利ですが、00123 と 123 を別のコードとして扱いたいデータでは、意図しない一致になります。
キーをそろえるとき
そろえた決まり(何を何に変えたか)も結果と一緒に残す
名前で突合したくなったとき
番号がないデータは、先に番号を付ける決まりを作る
NULLや重複キーがあるときの突合
この節では、値のない欄(NULL)と、同じキーが複数ある表で、突合の結果がどう狂うかと直し方が分かります。
NULL があると「値が違う」を見落とす
SQL では、NULL との比較の結果は真でも偽でもなく NULL になります。 PostgreSQL の公式ドキュメントも、ふつうの比較演算子はどちらかの入力が NULL のとき NULL を返す、と説明しています。そのため a.v <> b.v だけで比べると、片方が NULL の行が「一致」に入ってしまいます。
SELECT a.k, a.v AS a_v, b.v AS b_v,
CASE WHEN a.v <> b.v THEN '値が違う' ELSE '一致' END AS futsu,
CASE WHEN a.v IS DISTINCT FROM b.v THEN '値が違う' ELSE '一致' END AS null_taio
FROM a JOIN b ON a.k = b.k ORDER BY 1;
k|a_v|b_v|futsu|null_taio
K1|100|100|一致|一致
K2||200|一致|値が違う
K3|||一致|一致
K4|300||一致|値が違う
K2 と K4 は片方にしか値がないのに、<> では「一致」になりました。IS DISTINCT FROM は NULL を「比べられる値」として扱う比較で、片方だけ NULL なら違う、両方 NULL なら同じ、と判定します。PostgreSQL と SQLite(3.39.0 から)の両方で同じ結果でした。EXCEPT も NULL どうしを同じとみなすので、同じデータで K2 と K4 の2行が出ました。
pandas は向きが逆です。値がない印の NaN どうしを != で比べると「違う」になるため、今度は両方とも空の行が不一致に入ります。
k v_a v_b ne chigau
0 K1 100.0 100.0 False False
1 K2 NaN 200.0 True True
2 K3 NaN NaN True False
3 K4 300.0 NaN True True
ne は v_a != v_b、chigau は「等しい、または両方とも空」を除いた判定です。K3 の扱いが違います。また、pandas の公式ドキュメントは、キーの列が両方とも空の行どうしは結び付けられ、これは SQL の結合と違う動きだ、と注意しています。手元でも、キーが None の行どうしが結び付きました。キーが空の行は、突合の前に取り除いて別に数えます。 SQL の NULL の決まりは関連記事「SQLのNULLの扱い|COALESCE・NULLIFと落とし穴12選」で扱っています。
重複キーがあると行が増える
請求 INV-001(120000)に対して、入金が 60000 ずつ2回に分かれて届いた、という表で突合しました。
invoice_no|seikyu|nyukin|kekka
INV-001|120000|60000|値が違う
INV-001|120000|60000|値が違う
INV-002|88000|88000|一致
INV-003|45000|45000|一致
請求は3件なのに結果は4行になり、INV-001 は2行とも「値が違う」と出ました。まず重複を数え、キーごとに合計してから突合します。
SELECT invoice_no, COUNT(*) AS kensu FROM nyukin2 GROUP BY invoice_no HAVING COUNT(*) > 1;
SELECT s.invoice_no, s.amount AS seikyu, n.gokei AS nyukin, n.kensu,
CASE WHEN s.amount <> n.gokei THEN '値が違う' ELSE '一致' END AS kekka
FROM seikyu s
JOIN (SELECT invoice_no, SUM(amount) AS gokei, COUNT(*) AS kensu
FROM nyukin2 GROUP BY invoice_no) n ON s.invoice_no = n.invoice_no
ORDER BY 1;
invoice_no|kensu
INV-001|2
invoice_no|seikyu|nyukin|kensu|kekka
INV-001|120000|120000|2|一致
INV-002|88000|88000|1|一致
INV-003|45000|45000|1|一致
pandas では、validate="one_to_one" を付けておくと、重複があった時点で次のエラーで止まります。気づかないまま行が増えるのを防げます。
MergeError: Merge keys are not unique in right dataset; not a one-to-one merge
Duplicates in right:
invoice_no
INV-001 ...
重複した行の見つけ方と、1件だけ残す書き方は関連記事「SQLで重複データを抽出・削除する方法|1件だけ残す書き方」で扱っています。
突合の方法の選び方(判断表)とつまずき
この節では、手元のデータに合った突合の方法の選び方と、うまくいかないときの原因と直し方が分かります。
データがどこにあるかで選ぶのが、いちばん手間がかかりません。 データベースの中にあるなら SQL、ファイルなら pandas かコマンドです。
| 手元の状況 | 向いている方法 | 理由 |
|---|---|---|
| 2つの表が同じデータベースにある | FULL OUTER JOIN | 取り出さずに3つの結果を出せる |
| 列が多く、全部同じかを確かめたい | EXCEPT を両方向 | 列を1つずつ書かずに済む |
| CSV や表計算ソフトから書き出したファイル | pandas の merge | キーをそろえる処理と突合を1つのスクリプトに書ける |
| キーだけのテキスト、サーバーの上でさっと確かめたい | sort と comm | 追加の道具がいらない |
| 別々のデータベース、別々のサーバーにある | 件数・合計・チェックサム | データを運ばずに全体を比べられる |
| 行と列の並びが同じ2つの表 | pandas の compare | 違うセルだけが出る |
| 文章や設定ファイルの違いを見たい | diff | 行の単位で前後が分かる |
つまずきやすい点です。文言は、この記事で実際に出したものです。
| 症状・文言 | 原因 | 直し方 |
|---|---|---|
| 合うはずの行が「片方だけ」に出る | キーの全角半角・空白・ゼロ埋め・大文字小文字の違い | そろえた値を別の列に作って突合する |
| 値が違うのに「一致」になる | NULL を <> で比べている | IS DISTINCT FROM を使う |
| 結果の行数が元より多い | キーが重複している | 重複を数え、キーごとに集計してから突合する |
operator does not exist: text = integer | PostgreSQL で型の違う列を比べた | CAST でどちらかにそろえる |
near "ALL": syntax error | SQLite で EXCEPT ALL を書いた | GROUP BY で件数を数えてから比べる |
comm: file 2 is not in sorted order | 並べ替えていない | 同じロケールで sort してから渡す |
MergeError: Merge keys are not unique | validate を付けた merge でキーが重複 | 重複を集計するか、指定を見直す |
Can only compare identically-labeled | compare に行や列の違う表を渡した | 先に merge で両方にある行だけにする |
| 金額が 120000.0 と表示される | NaN が入って列が小数になった | Int64 で読み込む |
突合を終える前の確認です。
- 1突合キーと「一致」の決まりを書き残した何の列をキーにし、どこまで同じなら一致としたか
- 2キーの重複と空の行を数えた重複は集計し、空の行は別に数える
- 3結果の件数を足すと元の行数に合う請求6件=一致3+値が違う1+請求だけ2
- 4両方向で確かめたEXCEPT や NOT EXISTS は片方向。入れ替えてもう一度実行する
- 5合わなかった分の一覧を残した件数だけでなく、どの行かが分かる形で
よくある質問
Q. 突合の読み方は「とつごう」ですか、「つきあわせ」ですか? A. 仕事の会話では「とつごう」が一般的です。実用日本語表現辞典は読みを「とつごう・つきあわせ」としていて、精選版 日本国語大辞典は「つきあわせ」の見出しで載せています。どちらも誤りではありません。
Q. 突合とは、簡単に言うと何ですか? A. 2つ以上のデータや書類を突き合わせて、合っているか、どこが食い違うかを確かめることです。請求の一覧と入金の一覧を比べる、移行前と移行後のデータを比べる、といった作業を指します。
Q. 突合と照合の違いは何ですか? A. ほぼ同じ意味で、はっきりした線引きはありません。2つの一覧を全件比べて食い違いを洗い出す場面では突合、基準になるものと比べて1件ずつ確かめる場面では照合と言うことが多いです。
Q. 突合と消込の違いは何ですか? A. 消込は、請求と入金を突き合わせて、一致した請求を「済み」にしていく処理です。突合で一致を確かめ、そのあとに消込を行う、という順番になります。
Q. 「突合する」は正しい言い方ですか? A. 業務の用語として広く使われています。相手になじみがなさそうなときは、「突き合わせる」「照らし合わせる」と言い換えると伝わりやすくなります。
Q. SQL で2つのテーブルを突合するには、どう書きますか? A. FULL OUTER JOIN でキーを結び付け、相手の側が NULL なら「片方だけ」、両方にあれば値を比べます。この記事では PostgreSQL と SQLite で同じ SQL を実行し、同じ結果になりました。片方だけを出すなら NOT EXISTS、全列の違いを出すなら EXCEPT が使えます。
Q. pandas で突合するには、どう書きますか?
A. merge(how="outer", indicator=True) を使います。_merge 列の both・left_only・right_only で、どちらにあった行かが分かります。both の行について値の列を比べると、値の違いが出せます。
Q. 件数が合っていれば、突合は終わりにしてよいですか? A. 件数と合計が同じでも中身が違う例を、この記事で実際に作りました。件数と合計は最初の確認として使い、大事なデータはチェックサムかキーごとの突合まで進めるのが確実です。
Q. 合うはずのデータが一致しません。何を見ればよいですか? A. キーの全角と半角、前後の空白、ゼロ埋め、大文字と小文字を確かめます。次に、キーの重複と、空の値(NULL)を数えます。この記事の例では、書き方をそろえるだけで 0件から5件の一致に変わりました。
Q. 三点突合とは何ですか? A. 発注書・納品書・請求書の3つの書類で、品目・数量・金額が同じかを確かめる作業です。考え方は2つの一覧の突合と同じで、組み合わせを変えて比べます。
Q. 突合は英語で何と言いますか? A. reconciliation や matching と訳されることが多いです。会計の文脈では reconciliation、データの行を結び付ける意味では matching がよく使われます。
まとめ
- 突合は「とつごう」と読み、2つ以上のデータや書類を突き合わせて、合っているか・どこが食い違うかを確かめること。辞書の見出しは「つきあわせ」。
- 照合とはほぼ同じ意味。消込は突合のあとの処理、マッチングは結び付ける部分、検証は突合を含む広い言葉。
- データ突合の結果は「一致・値が違う・片方だけ」。SQL は FULL OUTER JOIN、pandas は
merge(indicator=True)、コマンドは sort と comm・join で、同じ「3・1・2・1」が出た。 - 件数と合計が同じでも中身が違うことがある。チェックサムは同じか違うかだけが分かり、順番や改行の違いでも変わる。
- 合わないときは、キーの揺れ・NULL・重複キーを疑う。NULL は
IS DISTINCT FROM、重複は集計、揺れはそろえた値を別の列に作って突合する。
次に読むとよい記事です。結合の基本は関連記事「SQLのJOINの違い|INNER JOINとLEFT JOINの使い分け」で、移行のときの件数の確かめ方は関連記事「データ移行のマッピング定義書の書き方|項目テンプレートと件数検証SQL」で扱っています。
参考・出典
確認日はいずれも 2026年11月11日です。
- コトバンク「突合」(精選版 日本国語大辞典):https://kotobank.jp/word/%E7%AA%81%E5%90%88-342352
- Weblio 辞書「突合」(実用日本語表現辞典):https://www.weblio.jp/content/%E7%AA%81%E5%90%88
- PostgreSQL 16 Documentation「9.2. Comparison Functions and Operators」(IS DISTINCT FROM と NULL の比較):https://www.postgresql.org/docs/16/functions-comparison.html
- PostgreSQL 16 Documentation「7.2. Table Expressions」(FULL OUTER JOIN):https://www.postgresql.org/docs/16/queries-table-expressions.html
- PostgreSQL 16 Documentation「7.4. Combining Queries」(EXCEPT・EXCEPT ALL):https://www.postgresql.org/docs/16/queries-union.html
- SQLite「Release 3.39.0」(FULL OUTER JOIN と IS DISTINCT FROM の追加):https://www.sqlite.org/releaselog/3_39_0.html
- SQLite「Datatypes In SQLite」(型の違う値の比較):https://www.sqlite.org/datatype3.html
- pandas「pandas.DataFrame.merge」(indicator・validate・空のキーの注意):https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.merge.html
- pandas「pandas.DataFrame.compare」:https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.compare.html
- GNU Coreutils「comm」「join」(手元の
comm --help・join --helpの表示でも確認):https://www.gnu.org/software/coreutils/manual/coreutils.html - Python「unicodedata」(NFKC の正規化):https://docs.python.org/ja/3/library/unicodedata.html
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「突合とは|読み方・意味と照合との違い、データ突合のやり方(SQL・Python)」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。