
データ不整合に気づく仕組み|件数突合チェックを毎朝自動で回す
「先月の売上、管理画面と会計の数字が合わないんですけど……」。 経理の人からそう声をかけられて、慌ててDBを開く。調べてみると、ある日の連携処理が途中で止まっていて、数十件だけ相手側に渡っていなかった——。そんな経験、ありませんか。
データの食い違い(不整合)は、エラー画面のように派手には出てきません。処理は「正常終了」と言っているのに、中身だけが静かにずれていく。だから、誰かが数字を見比べたときに初めて見つかることが多いのです。それは見落としではなく、気づく仕組みがまだ無かっただけです。
この記事では、お客さまや他部署より先に自分が気づけるように、件数や合計を突き合わせる小さなチェック(突合)を毎朝自動で回すやり方を、一人運用の目線で一緒に組み立てます。大きな監視ツールは要りません。SQLが1本書ければ始められます。
結論:データ不整合は「数えて・比べて・ずれたときだけ知らせる」チェックで早く気づけます。やることは3つ。①「本来一致するはずの2つの数」を1組決める、②その2つを数えるSQLを書き、差が0でないときだけ通知する、③cronなどで毎朝自動で回す。まずは「いちばん問い合わせが来やすい数字」1組から始めます。
SQLの書き方や関数名は、使っているDB(MySQL・PostgreSQL など)やバージョンで少しずつ違います。ここでの例は考え方の見本として、自分の環境で SELECT だけ試してから使ってください。
何が起きているのか:データ不整合が「静かに」生まれる理由
不整合は、たいてい2つの場所に同じ事実を持っているところで生まれます。たとえば次のような組み合わせです。
- 親と子のテーブル:注文(親)と注文明細(子)。明細だけ残って親が消えている、親があるのに明細が0件。
- システムとシステム:自社の受注データと、会計・在庫・配送など連携先に渡したデータ。
- 元データと集計データ:日々の明細と、夜間バッチで作る月次集計テーブル。
ずれる原因は、だいたい次のどれかです。
- 連携やバッチが途中で止まったのに、エラーとして気づかれなかった。
- 画面や処理の不具合で、片方だけ更新・削除された。
- 手作業のデータ修正で、関連するテーブルを直し忘れた。
- 二重実行で、同じデータが2回入った。
どれも「処理は終わっている」ように見えるのが厄介なところです。cronやバッチの失敗検知を入れていても、成功扱いで中身だけずれるケースは拾えません。そこで、結果のデータそのものを数えて確かめる、というのが突合チェックの考え方です。
件数突合チェックの作り方(数える・比べる・知らせる)

突合チェックは、3つの部品でできています。順番に作っていきましょう。
① 数える:「本来一致するはずの2つの数」を決める
最初に決めるのは、「この2つは必ず同じになるはず」と言える数の組です。全部を一度にやろうとせず、1組だけ選びます。選び方の目安は、過去に問い合わせや「合わない」が起きた数字です。
よく使う組み合わせの例です。
- 親子の整合:親のない明細の件数(本来は0件)
- 連携の整合:昨日分の受注件数と、連携先に送った件数(本来は同じ)
- 集計の整合:明細を合計した金額と、集計テーブルの金額(本来は同じ)
- 重複の検出:同じ注文番号が2行以上ある件数(本来は0件)
「本来は0件」になるものは特に作りやすいので、最初の1本におすすめです。
② 比べる:差が出たらすぐ分かるSQLにする
数え方は、SELECT だけで書きます。チェックではデータを書き換えないのが大前提です。
親のない明細を数える例(テーブル名・列名は自分の環境に読み替えてください):
-- 親(orders)が存在しない明細(order_items)の件数。本来は 0
SELECT COUNT(*) AS orphan_count
FROM order_items oi
LEFT JOIN orders o ON o.id = oi.order_id
WHERE o.id IS NULL;
同じ注文番号の重複を数える例:
-- 同じ注文番号が2行以上ある番号の数。本来は 0
SELECT COUNT(*) AS dup_count
FROM (
SELECT order_no
FROM orders
GROUP BY order_no
HAVING COUNT(*) > 1
) d;
2つの数を比べるものは、どちらも「昨日分」のように期間をそろえるのがコツです。期間がずれていると、本当は合っているのに「差あり」になってしまいます。とくに日付の境目(タイムゾーンや締め時刻)は食い違いやすいところなので、日付・タイムゾーンのずれもあわせて確かめておくと安心です。
③ 知らせる:ずれたときだけ通知する
チェックの結果は、差が0なら黙っている、0でないときだけ知らせるようにします。毎朝「異常なし」が届くと、すぐに読まなくなってしまうからです(監視アラートの鳴らしすぎと同じ理屈です)。
シェルで組む場合の考え方の例です(接続方法や通知の送り先は環境に合わせてください)。
#!/bin/sh
# 毎朝の突合チェック:差が 0 でなければ通知する
COUNT=$(mysql --defaults-extra-file=/path/to/readonly.cnf -N -e "
SELECT COUNT(*) FROM order_items oi
LEFT JOIN orders o ON o.id = oi.order_id
WHERE o.id IS NULL;" mydb)
if [ "$COUNT" != "0" ]; then
echo "親のない明細が ${COUNT} 件あります(order_items)" | mail -s "[突合] 不整合あり" ops@example.com
fi
ここで押さえたいのは2点です。
- 接続には読み取り専用のユーザーを使う:チェック用のスクリプトが、うっかりデータを書き換えられない形にしておきます。パスワードはスクリプトに直書きせず、権限を絞った設定ファイルに分けます(パスワード・APIキーの平文保管を見直す)。
- チェック自体が動かなかったことにも気づけるようにする:DBにつながらずにエラーになったとき、何も通知されないと「異常なし」と区別がつきません。接続エラーのときも知らせるか、実行ログを残しておきます。
毎朝自動で回す
できたスクリプトは、cronなどで朝、業務が始まる前に回します。夜間バッチが終わったあとの時間にすると、その日の処理結果まで含めて確かめられます。
# 毎朝 7:30 に突合チェック(夜間バッチ完了後の時間に合わせる)
30 7 * * * /opt/check/tsugo-check.sh >> /var/log/tsugo-check.log 2>&1
重いテーブルを全件数えるSQLは、本番の負荷になることがあります。最初は期間を「昨日分」に絞る、アクセスの少ない時間に回す、レプリカ(読み取り用の複製)があればそちらで数える、のどれかで負荷を抑えましょう。
具体例:こんなふうに気づけるようになる
実際の現場で、突合チェックが役に立つのはこんな場面です。
- 連携の取りこぼし:「昨日の受注 128件/会計に送った件数 120件」と朝に通知が来る。経理から言われる前に、止まっていた8件を再送できた。
- 二重登録:決済画面での二重クリックで、同じ注文番号が2行できていた。重複チェックで翌朝に気づき、出荷前に片方を止められた。
- 手作業修正の直し忘れ:先週SQLで注文を消したとき、明細を消し忘れていた。親のない明細チェックに出てきて、まとめて片付けられた(データを直すときは本番データをSQLで直すときの安全手順の順番で)。
どれも、チェックが「直してくれる」わけではありません。でも、ずれに気づくのが1か月後から翌朝に変わるだけで、直す量も、説明の重さも、ぐっと小さくなります。
影響:先に気づけると、何が変わるか
- 問い合わせが「報告」に変わる:「数字が合わない」と言われる前に、「昨日の8件、再送しておきました」と自分から伝えられる。
- 原因が追いやすい:ずれが起きた日が分かるので、その日のログやリリースを見ればよい。1か月分さかのぼる調査がなくなる。
- 直す範囲が小さい:数件のうちに直せるので、データ修正も小さく安全に済む。
- 「たぶん合っている」が「合っていると確かめた」になる:月次の締めや監査のときに、自信を持って答えられる。
明日やること:いちばん問い合わせが来る数字を1組だけ
全部のテーブルを見張る必要はありません。明日できる、いちばん小さな一歩はこれです。
- 過去に「数字が合わない」と言われたことがある場所を1つ思い出す(なければ、親子関係のあるテーブルを1組)。
- その数字について、「本来は0件」または「本来は同じ」になるSQLを1本、
SELECTだけで書く。 - 本番(できれば読み取り専用ユーザー)で1回だけ手で実行し、今の結果を見てみる。すでにずれていたら、それは今日見つけられた収穫です。
- 結果が0でないときだけ通知するスクリプトにして、毎朝のcronに登録する。
- どのチェックを、何時に、何を見ているかを、運用ドキュメントに一行メモしておく。
最初の1本ができれば、2本目からは同じ形をコピーして足していくだけです。
「件数突合チェック」チェックリスト
突合チェックを作るとき・見直すときに当ててみてください。
- 「本来一致するはずの2つの数」を、少なくとも1組決めているか
- チェックのSQLは
SELECTだけで、データを書き換えないか - 比べる2つの数の期間(昨日分・締め時刻など)がそろっているか
- 差が0のときは黙り、ずれたときだけ通知する形になっているか
- チェック自体が失敗したとき(接続エラーなど)にも気づけるか
- 接続には読み取り専用のユーザーを使い、パスワードを直書きしていないか
- 本番の負荷にならない時間・範囲で回しているか
- 通知が来たときに「まず何を見るか」を一行でもメモしているか
全部に○が付かなくても大丈夫です。まずは1組の数字を毎朝数えるだけで、気づくのはお客さまより先になります。
よければ、こちらも
突合チェックは、バッチ・データ修正・監視とつながっています。あわせて読むと、気づいてから直すまでがなめらかになります。
- cronバッチの失敗に気づく仕組み|静かに止まるジョブを見逃さない:処理が「止まった」ことに気づく側の仕組み。突合チェックと組み合わせると、止まった・ずれたの両方を拾えます。
- 本番データをSQLで直すときの安全手順|バックアップと件数確認:ずれが見つかったあと、安全に直すための手順です。
- 監視アラートの鳴らしすぎを減らす|閾値の決め方と運用のコツ:通知を「動くものだけ」に絞る考え方。突合の通知にもそのまま使えます。

データのずれは、誰かが悪いから起きるのではありません。2つの場所に同じ事実を持つ以上、いつかはどこかで少しずつずれるものです。大事なのは、ずれないことより、ずれたときに早く気づけることです。 今日は、いちばん問い合わせが来る数字を1組、SELECT COUNT(*) で数えてみるだけで十分です。その1本が、毎朝あなたの代わりに数字を見張ってくれるようになります。
ほかの実務ヒントは記事一覧からどうぞ。保守運用の小さな備えを、メールでも少しずつお届けしています。