改善・モダナイゼーション

既存サービスのデータ不整合を解消する進め方

July 10, 2026
データベース 既存改善 データ不整合 整合性 データ品質

「管理画面の合計金額と、経理が別で集計した数字が合わない」「同じユーザーのレコードが2件できている」「退会したはずのユーザーにメールが届いた」——こうした報告が月に1回程度なら「たまたま」で片付けられますが、頻度が増えてくると誰も数字を信用できなくなります。営業もサポートも「システムの数字は一応参考程度に」と言い出し、結局Excelで裏取りする二度手間が常態化する、という相談を私たちはよく受けます。

データ不整合は見た目の派手さがないぶん後回しにされがちですが、信頼できないデータは意思決定を鈍らせるという点で、画面の不具合より実害が大きいことも珍しくありません。この記事では、不整合の原因の見極め方から、実際のクレンジング作業、再発防止の制約追加、そして「いきなり全部直そうとしない」優先順位のつけ方までを整理します。

不整合の典型パターンと原因

「データがおかしい」と一括りにされがちですが、症状ごとに原因が異なります。原因を切り分けずに直すと、直したはずの場所からまた同じ不整合が再発します。

1. バリデーション不足による不正データ

アプリケーション層のバリデーションが緩い、あるいは複数の入力経路(通常のフォーム・管理画面・APIから直接・過去の一括インポート)のうち一部にしかバリデーションが効いていないケースです。

  • メールアドレスの重複チェックが新規登録フォームにはあるが、管理画面からの手動作成にはない
  • 必須のはずの外部キーが NULL を許容していて、親レコードが消えても子レコードだけ残る
  • 過去のバージョンでは許可されていた値が、仕様変更後も過去データとして残り続けている(例: 旧ステータス値がコード上は廃止済みなのにDBには残存)

2. トランザクション不備による中途半端な更新

複数テーブルにまたがる更新のうち一部だけが成功し、残りが失敗した状態です。

  • 注文レコードは作られたが、在庫の引き当てレコードは作られなかった(片方の処理で例外が発生し、トランザクションで括られていなかった)
  • 外部API呼び出しを挟む処理で、DB更新は成功したがAPI呼び出しが失敗し、リトライ時に二重処理が起きた
  • 非同期ジョブ(Sidekiq等)がリトライされる際、冪等性(同じジョブを2回実行しても結果が変わらない性質)が担保されておらず、レコードが重複作成される

このパターンはログを見ないと気づけないのが厄介な点です。エラー自体は握りつぶされていたり、リトライで最終的に成功していたりして、中間状態の不整合だけが静かに残ります。

3. 過去のバグが残した爪痕

すでに修正済みのバグが、稼働していた期間に生成した不整合データがそのまま残っているケースです。

  • 「あの時期のタイムゾーン変換バグで、この期間の created_at だけ9時間ズレている」
  • 「N+1対策で書いたキャッシュ更新処理に条件漏れがあり、特定条件のレコードだけ集計値が更新されずに古いまま」

バグ自体はコードレベルで直っていても、過去に生成されたデータは自動的には直りません。「コードは直したのでもう大丈夫」という認識で放置されているケースが実際には多く、調査すると数年前のデータまで影響が残っていることがあります。

4. 非正規化の破綻

集計値や参照先の情報をパフォーマンスのために別カラムへ複製する「非正規化」は、更新の同期漏れがあると簡単に破綻します。

  • orders テーブルに total_amount を非正規化して持っているが、注文明細(order_items)が個別に編集された際に再計算されずキャッシュが腐る
  • ユーザー名を投稿テーブルに複製しているが、ユーザーが改名しても過去の投稿側は更新されない設計で、「どちらが正か」の合意がチーム内で崩れている

非正規化そのものが悪いわけではなく、更新経路が増えるたびに同期ロジックを見直していないことが根本原因です。

調査: 不整合の洗い出し方

原因の当たりをつけたら、実際にどれだけ・どこに不整合があるかを数値で洗い出します。感覚ではなくクエリで確認する工程です。

  • 集計の突合クエリを書く: 「注文明細の合計」と「注文テーブルのtotal_amount」のように、本来一致すべき値をSQLで突き合わせ、乖離しているレコード数を数える
  • 外部キー整合性を確認する: 親が存在しない子レコード、本来1対1のはずが複数存在するレコードをカウントする。PostgreSQLなら NOT EXISTS サブクエリや LEFT JOIN ... WHERE parent.id IS NULL で機械的に洗い出せる
  • 重複レコードの検出: 一意であるべきキー(メールアドレス、外部連携ID等)で GROUP BY ... HAVING COUNT(*) > 1 を実行し、重複件数を把握する
  • 影響範囲と発生時期を特定する: created_at や更新履歴(あれば)を見て、不整合がいつからいつまでの期間に発生したかを絞り込む。特定のデプロイ日を境に急増していれば、原因のバグを特定する重要な手がかりになる

この調査結果を件数と割合(全体の何%が不整合か)で出すと、後の優先順位づけと発注者への説明が格段にしやすくなります。「なんとなく不整合がある」と「1.2%・約3,400件のレコードで金額が乖離している」では、判断のしやすさが全く違います。

修正: クレンジングと再発防止をセットで行う

不整合データを直すだけでは、同じ原因が残っていれば再発します。クレンジング(既存データの是正)と再発防止(制約追加)は必ずセットで進めます。

データクレンジングの進め方

  • バックアップを取ってから実行する: 大量データの一括更新は、実行前に対象テーブルのバックアップかスナップショットを必ず取る
  • まず件数の少ないパターンで小さく試す: 全件一括更新の前に、影響範囲が小さいサンプルで実行し、想定通りの結果になるか確認する
  • 「正」をどちらにするか明文化してから直す: 非正規化の破綻のように複数の値が食い違っている場合、どちらを正とするかの判断はビジネス側の合意が必要になることがある(例: 集計値とトランザクション明細のどちらを信頼するか)。エンジニアだけで機械的に決めない
  • 修正ログを残す: 「いつ・どのレコードを・どう変更したか」を記録しておく。後で「この数字、いつ変わったんですか」と聞かれたときに説明できる状態にする

再発防止: 制約をDB側に寄せる

アプリケーション層のバリデーションだけに頼ると、前述のとおり「入力経路が増えるたびに漏れる」問題が再発します。可能な限りDB自体に制約をかけて、アプリのバグが混入しても不整合が発生しない状態を作ります。

  • 一意性制約(UNIQUE)、外部キー制約(FOREIGN KEY)、NOT NULL 制約をDBレベルで追加する
  • チェック制約(CHECK)で、特定カラムが許容される値の範囲外を取れないようにする
  • 大きなテーブルへの制約追加は本番トラフィックをロックすることがあるため、PostgreSQLでは NOT VALID オプション付きで制約を追加してから VALIDATE CONSTRAINT を別途実行し、ロック時間を最小化する

DB制約を後付けする際は、既存データが制約に違反していると追加自体が失敗します。そのためクレンジングを先に完了させてから制約を追加するという順序が重要です。テーブル構造自体に無理があり、非正規化の破綻が繰り返し起きている場合は、既存プロダクトのDB設計を安全に見直す進め方で扱っている段階的なスキーマ再設計も検討対象になります。

いきなり全部直さない: 優先順位のつけ方

不整合の洗い出しを終えると、直したい箇所が一度に何十個も出てくることがあります。すべてを同時に着手すると、検証範囲が広がりすぎてリスクが高くなります。私たちは次の軸で優先順位をつけます。

  1. 金銭・法務に関わるものを最優先: 請求額、在庫数、契約状態など、誤りが直接的な損失や信頼問題につながる箇所から着手する
  2. ユーザーに見えているものを次に: 管理画面の内部集計より、エンドユーザーの画面に表示される不整合(マイページの残高、注文履歴等)を優先する
  3. 発生頻度・拡大傾向で判断する: 一度きり過去に発生して止まっているものより、今も継続的に発生し件数が増え続けているものを優先する。放置するほど後のクレンジング対象が増える
  4. 調査・修正コストが低いものは並行して片付ける: 影響は小さいが原因も修正も単純な不整合は、優先度の高い作業と並行して数を減らしておく

「全部同時に完璧に直す」のではなく、影響が大きく発生し続けているものから止血し、あとは計画的に減らしていくという進め方が、実務上もっとも現実的です。

外注する場合に確認すべきこと

データ不整合の解消を外部チームに依頼する際は、次の点を発注前に確認しておくと安心です。

  • 原因の切り分けを先にするか: 「とりあえず直します」ではなく、バリデーション不足・トランザクション不備・過去バグ・非正規化破綻のどれに該当するかを調査する工程が含まれているか
  • クレンジングと再発防止をセットで提案してくるか: データを直すだけで終わらせず、DB制約の追加やアプリ側バリデーションの見直しまで含めた提案になっているか
  • バックアップとロールバック手順があるか: 一括更新の前にバックアップを取り、想定外の結果が出た場合に戻せる計画があるか
  • 件数・割合で報告してくるか: 「不整合がありました、直しました」で終わらず、どれだけの件数・割合に影響があったかを数値で報告してくれるか

技術的な修正力だけでなく、こうした進め方の丁寧さが既存プロダクト改善の品質を大きく左右します。私たちが対応する技術スタックの詳細はデータベース関連の技術ページに、進め方全体はモダナイゼーション支援サービスにまとめています。既存プロダクト改善に着手する際の基本的な確認観点は既存プロダクト改善の最初の確認観点もあわせてご覧ください。

まとめ

  • データ不整合は「バリデーション不足」「トランザクション不備」「過去のバグの爪痕」「非正規化の破綻」の4パターンに大別でき、原因ごとに調査・修正の進め方が異なる
  • クレンジング(既存データの是正)と再発防止(DB制約の追加)は必ずセットで行う。制約はデータを直してから追加する順序が重要
  • 不整合をすべて同時に直そうとせず、金銭・法務への影響、ユーザーへの見え方、発生頻度で優先順位をつけて段階的に解消する

「システムの数字が信用できず、いつもExcelで裏取りしている」という段階でも、torcheees はご相談を受け付けています。まずは1〜4週間の「既存プロダクト改善診断」で不整合の実態を洗い出し、継続的な改善支援も対応しています。お問い合わせフォームからお気軽にご連絡ください。

Discuss Your AI × Rails Development

Contact Us
Quick Estimate