安全な一括インポート — プレビュー、検証、コミットのパターン
安全な一括インポートは不正データや予想外の変更を防ぎます。プレビュー、検証、行ごとのエラー処理、ロールバック対応のあるコミットパターンを使いましょう。

なぜ一括変更は失敗するのか(ユーザーは何を期待しているか)
一括変更が失敗するのは現実的で地味な理由によることが多いです。ファイルはほぼ正しいが列名が違う。必須フィールドが一部の行で空になっている。IDがデータベースのものと一致しない(誰かが先週エクスポートしていて、その間にレコードが変わった)などです。あるいはデータ自体は有効でも、誤ったフィールドにマッピングされていて、電話番号がノート欄に入ってしまうこともあります。
怖いのは速度です。ひとつの想定ミスで何百〜何千件ものレコードに瞬時に影響が出てしまいます。安全な一括インポートは単なるバックエンドの問題ではなく、信頼の問題です。
ユーザーが期待する単純なことはひとつ:実行する前に何が起きるか見せてほしい。最も信頼できるパターンは「プレビュー」「検証」「コミット」の順です。
- プレビュー: 何が変わるかの明確なサマリと実際の変更サンプルを表示します。
- 検証: 必須項目の欠落、フォーマット不備、参照の不一致などを検出するルールを実行します。
- コミット: ユーザーが確認した後、リスクに合わせた方法で変更を適用します。
ユーザーはまた、2種類の失敗から守られることを期待しています。
個別に修正できる問題は行単位で扱うべきです。12行でメール形式が不正、またはZIPコードが抜けているような場合、ユーザーはその行だけを修正(レポートをダウンロードして編集、インラインで修正、あるいは再アップロード)して、残りを保持したいと考えます。
全体を止めるべき問題は全体を停止させるべきです。マッピングが間違っている、重要フィールドを上書きする可能性がある、あるいはファイルが間違ったワークスペースや顧客用である場合は、明確な説明付きで強制停止するのが最良の体験です。
ユーザーはまた、実行履歴を求めます:ランID、タイムスタンプ、誰が実行したか、どのファイルが使われたか、何が変わり何が失敗したか。これがサポートを早くし、問題発生時の対応を可能にします。
平易な言葉でのプレビュー→検証→コミットの流れ
一括変更は「ワンクリックで何千件にも影響する」ためリスクが高く感じられます。リスクを下げる最も簡単な方法は作業を三段階に分け、それぞれの段階でアウトプットを出すことです。
フェーズ1:プレビュー(バッチ準備)
入力(CSV、貼り付けた行、選択したレコード)を準備済みバッチに変換します。ここでの仕事は、実際に何が起きるとシステムが考えているかを、何も変更する前に示すことです。
良いプレビューは次の3つの質問に答えます:何が変更されるのか、何件が影響を受けるのか、どこが怪しいのか。
最低限、件数(総行数、マッチしたレコード、新規レコード、スキップされた行)、実際の行の小さなサンプル、リスクのある項目(必須フィールドの欠落、あいまいな一致、異常な値)についての明確な警告を含めてください。マッチングルール(例:「メールで一致」や「外部IDで一致」)を明示し、バッチに名前、タイムスタンプ、一意のバッチIDを付与して識別できるようにします。
フェーズ2:検証(ドライラン)
ドライランはデータベースへの書き込みを伴いません。実際の更新時に使うのと同じチェックを走らせ、結果はレポートとして出力します。
検証は行単位のルール(この行は有効か?)と行間ルール(これらの行は互いに矛盾していないか?)の両方をカバーするべきです。出力は曖昧な合格/不合格ではなく、サマリと特定の行に紐づく問題一覧にして、ユーザーが推測せずに修正できるようにします。
フェーズ3:コミット(変更の適用)
コミットは取り返しのつかないポイントなので、成功したドライランの後にのみ利用可能にしてください。ユーザーは「ファイル」を確認するのではなく、プレビュー・検証された特定の準備済みバッチを確認していることを明確にします。
この決定ポイントは重要です。ファイルが変わったり、マッピングが変わったり、データが再アップロードされた場合は新しいバッチを作り、再度確認を求めます。
例:5,000件の顧客をインポートする。プレビューは4,920件がメールでマッチ、60件が新規、20件がメール欠落でスキップと表示。ドライランで12行が電話形式不正とフラグされる。これら12行が修正されて初めて、そのバッチIDに対する「バッチをコミット」が有効になります。
入力、マッピング、レコードの識別方法
多くの一括ジョブは検証に入る前に失敗します。入力が乱れている、列がフィールドに合わない、行が既存を作成すべきか更新すべきか判別できないなどです。
一括処理は通常、CSVエクスポート、貼り付けたスプレッドシート行、アプリ内の選択レコード(一括更新)、またはAPIでトリガーされたバッチジョブから始まります。ソースが何であれ、ユーザーの持っているものとシステムが保持するものの間に明確なマッピングが必要です。
マッピングは列→フィールドの対応、小さな変換(空白のトリム、日付の解析、電話番号の正規化)、欠損値に対するデフォルトを含むべきです。空の列がどう扱われるかを隠してはいけません。空セルで既存値を保持するのか、値をクリアするのか、デフォルトを適用するのかをユーザーは知る必要があります。
次に重要なのは識別方法です:各行を既存レコードにどうマッチさせるか?
安定した識別子を優先し、マッチがない場合や複数ヒットする場合の挙動を明示してください。一般的な選択肢は内部ID(ユーザーがエクスポートできるなら最良)、外部システムID(統合に便利)、メール(有用だが重複と大文字小文字に注意)です。場合によっては account_id + invoice_number のような複合キーが適切です。あるいは「作成のみ」モードを提供し、常に新規作成する選択肢もあります。
最後に、権限ルールを一括スケールで適用してください。1件編集できるユーザーが自動的に何千件もの全フィールドを更新できるべきではありません。どのロールがインポートを実行できるか、どのフィールドの変更を許可するか、追加承認が必要なときはいつかを決めてください。
信頼を築くプレビューの設計
プレビューはユーザーが「コミット」を押して安全だと感じるかどうかを決める場所です。プレビューが曖昧だと、ユーザーはシステムが推測していると考えます。良いプレビューはレシートのように読めるべきです:何が変わるか、システムの確信度、更新を阻む要因。
まず簡潔なサマリを表示します。ほとんどのユーザーは総行数、スキップ数、作成/更新(削除を許すなら削除も)、警告行数とエラー行数、使用したマッチングルール(例:「メールで一致」)程度の数字があれば十分です。可能なら、最も一般的な警告カテゴリをグループ化して、パターンを素早く見せます。
次に実データをスポットチェックできるようにします。少数のスクロール可能なサンプルを表示し、更新には変更前/変更後ビューを含めます。「古い値 → 新しい値」を見ることで、電話番号が空セルで上書きされるといった驚きを防げます。実用的なUIパターンは、10〜50行を表示して検索やフィルタ(例:「警告のみ」)を使えるようにし、ファイル全体はバックグラウンドで処理する方法です。
不確実性は可視化してください。ある行が複数の既存レコードにマッチする可能性があるなら、その候補を表示します。必須フィールドが空なら正確なセルを指摘します。インポートが重複を作るなら短い理由(例:「同じメールがファイル内に2回出現」)を示してください。システムが分からないことを認めると、ユーザーの信頼は高まります。
次のアクションも明確にしましょう。ユーザーは行番号付きのエラーレポートをダウンロードして修正し、マッピングを再構築せずに再アップロードできるべきです。変更せずにキャンセルするか、リスクが低く権限があれば進めることができるようにします。
問題を早期に発見する検証ルール
良い検証があってこそ一括インポートは安心して使えます。目標は何も変更する前に問題を見つけ、それをユーザーが修正できる形で説明することです。
検証を明確な種類に分ける
一つの巨大な「無効」メッセージは混乱を招きます。チェックを別々のバケツに分けると、それぞれに適した修正が示せます。
フォーマットチェックは型、日付形式、数値範囲、電話/メールのパターンなどを扱います。必須フィールドチェックは欠損値や空文字、0と空の違いのような曖昧なケースを捕まえます。参照チェックはIDが存在するか、ステータスが許可されているかを確認します。ビジネスルールは実際の制約(与信枠、権限、「未処理のアイテムがある限り注文を閉じられない」など)を適用します。
重要なルール:検証はコミット時に使うのと同じロジックで行うこと。プレビューとコミットで別のルールだとユーザーの信頼はすぐに失われます。同じバリデータ、同じデータ参照、同じ権限チェックを端から端まで再利用してください。
検証を速く予測可能にする
大きなファイルは時間がかかるので、検証は応答性を感じさせるべきです。チャンクで検証し(例:500〜2,000行ごと)、進捗と推定時間を表示し、繰り返し使う参照データはキャッシュして同じIDリストを何度もフェッチしないようにします。
行間ルール(ファイル内の重複や競合)はファイル全体を見ないと判定できません。解析中に軽量のインデックスを作り、両方の関係行をフラグしてユーザーがどちらを残すか選べるようにします。
行レベルのエラー:恐怖ではなく行動可能にする
行レベルのエラーは信頼を失うか勝ち取るかの分かれ目です。赤いテキストの壁はユーザーを止めてしまいます。明確で修正可能な項目はユーザーを前進させます。
まず重大度を分けます。ブロッキングエラーはそのままでは適用できない(必須項目欠落、無効フォーマット、レコードが見つからない)ことを意味します。警告は適用可能だが選択が必要(値がトリムされる、デフォルトが使われる、重複の可能性がある)というものです。
良い行レベルのフィードバックは具体的で再現可能です。各問題は行識別子(ファイルの行番号とメールや外部IDのような安定キー)、フィールド名(列と宛先フィールド)、平易なメッセージ(「電話はE.164形式である必要があります」など)と推奨修正(例:サンプル値や許容範囲)を含めます。重大度タグは一貫させてください。
部分成功は意図的なオプションであるべきで、偶発的に起こるべきではありません。行が独立していて壊れた状態を生まない場合にのみ許可します。顧客のタグ更新は部分的に成功しても問題ないことが多いですが、請求書とその明細の更新などは通常そうではありません。
リトライをUXの一部として計画してください。ユーザーはソースファイルを修正してマッピングをやり直さずに再実行できるべきです。実用的なパターンは「import run」レコードを保持してマッピング選択と行レベル結果を保存し、次回実行時に「まだ失敗している」行と「今は修正済み」行をハイライトすることです。
コミットパターン:原子性、部分適用、冪等性
コミットステップで一括インポートは信頼を得るか壊すかが決まります。ユーザーはプレビューを見て問題を修正しています。今、システムは検証したものを正確に適用することが期待されています。
コミットモードを選びルールを明示する
よく使われるコミットモードは2つで、どちらもルールが明確なら有効です。
原子(All-or-nothing)は、任意の行が失敗したら何も書き込まれない方式です。お金、在庫、権限など一貫性が重要な場面に最適です。部分コミット(ベストエフォート)は有効な行を適用し、無効な行はスキップして報告します。CRMの更新やプロフィールの補完のように、一部でも進む方が良い場合に向きます。ハイブリッドで閾値を設けるチームもあり(例:失敗が2%を超えたら停止)ます。
何を選ぶにせよ、コミット画面と最終サマリで可視化してください。
コミットを正確に検証済みバッチに紐づける
プレビュー時に作成したインポートジョブID(バッチID)を使ってください。コミットリクエストはそのIDを参照し、データを再アップロードさせないようにします。
これにより「あるファイルでプレビューしておいて、別のファイルをアップしてからコミットしてしまう」ような間違いを防げます。また複数管理者が同時に作業する場合にも有用です。
冪等性:二重適用から守る
人はダブルクリックします。ブラウザはリトライします。コミットは二度実行しても安全であるべきです。
最も単純な手法は冪等キーをジョブごと(必要なら行ごとも)発行し、可能ならアップサートを使い、ジョブ状態をValidated → Committing → Committedのように一度しか遷移しないようロックすることです。
結果を受領書のように残す
コミット後は簡潔なサマリを表示し、ユーザーが結果をダウンロードまたはコピーできるようにします。作成、更新、スキップ、失敗の件数と短い理由を含めれば、怖い一括変更でも検証と説明が可能になります。
実務で使えるロールバック計画
ロールバック計画があれば、一括インポートは「うまくいくことを祈る」作業ではなくなります。結果が間違っていた場合、変更前の状態に戻せるべきです。
適切なアプローチはバッチの大きさ、処理時間、外部システムを触るかどうかによって変わります(メールや支払いは巻き戻せないことが多い)。
実用的なロールバック手法3つ
小さいバッチで短時間に終わるなら、単一のデータベーストランザクションが最も簡単な安全策です。すべての変更を適用し、途中で失敗したらデータベースがすべて破棄します。PostgreSQL内の数百〜数千行の更新に向いています。
大きなインポートではステージングを先に行う方法が安全です。ファイルをステージテーブルにロードし、そこで検証してから本番テーブルにプロモートします。見た目に問題があればステージを破棄して本番に影響を与えません。ステージを残しておけばマッピングやルールを調整して再実行するのも簡単です。
真のロールバックが不可能な場合は補償アクションを計画します。メールや支払いなど取り消せない外部副作用がある場合は、「レコードをキャンセルする」「返金する」「訂正のメッセージを送る」などの手順を前もって定義します。
簡単な選び方の目安:
- バッチが小さく自分のDBだけ触るなら単一トランザクションを使う。
- バッチが大きく遅いかリスクが高いならステージングとプロモーションを使う。
- 外部副作用を伴うなら補償アクションを用意する。
- 同じ入力が重複適用されないよう、再実行可能な計画を常に用意する。
監査ログがあればロールバックは現実的になる
ロールバックは何が起きたかを正確に知ることに依存します。誰がいつジョブを実行したか、ソースファイルやジョブID、どのレコードが変わったか(前後の値、または少なくとも変更サマリ)を記録してください。
具体例:サポート責任者が5,000件の顧客ステータスを一括更新する。ステージングで200件の不一致を事前に発見している。もしリリース後にマッピングが逆だったと気づいたとしても、監査ログがあれば影響範囲だけをターゲットにしたリバートを実行でき、システム全体を巻き戻す必要はありません。
避けるべき一般的なミスとトラップ
一括ジョブは予測可能な形で失敗します。多くの問題は「データが悪い」ではなく期待の不一致です:ユーザーが起きることをこう想定していて、システムが別のことをしてしまうケースです。
大きな落とし穴は、検証で使うルールとコミットで使うルールが違うことです。プレビューが簡易チェックを使い、コミット経路が追加の制約や別のデフォルトを持っていると、ユーザーは「問題なし」と見ていたのに実際のジョブが失敗する、または異なる結果で成功してしまうことがあります。解析器、ルールセット、マッチングロジックは端から端まで共通にしてください。
あいまいなマッチングロジックも典型的な失敗です。「メールで一致」は単純に聞こえますが、重複や大文字小文字の違い、ユーザーがメールを変更しているケースにぶつかります。UIはマッチングの詳細と複数ヒットや非ヒット時の挙動を明示してください。例:営業管理者が2,000件の連絡先を更新するつもりでインポートしたが、システムがメールだけでマッチしており、ファイルの半分が電話番号ベースだったため新規作成されてしまった、という事故はよくある話です。
「親切な」自動修正にも注意してください。無言の切り捨て、無言のトリム、日付形式の推測はデータ損失を隠すことがあります。正規化するならプレビューで(古い値 → 新しい値)を示し、危険な変換は警告にしてください。フィールドが長さ制限で切られるなら、それを明示する警告を出します。
結果を失わせないでください。タブを閉じて報告が消えるとサポート案件になります。各インポート実行を状態と結果ファイル、明確なサマリを持つオブジェクトとして保存してください。
スケールも計画に入れてください。バッチ処理がないとタイムアウトや部分書き込みが大量データで発生します。チャンク処理と進捗表示、レートリミットとバックオフ、冪等性キー、部分成功の扱い、失敗行の再実行オプションを用意してシステムを守ってください。
シンプルなチェックリストと次のステップ
一括変更は、何が起きるか、何が悪くなる可能性があるか、そして問題を迅速に見つける方法がわかれば安全に感じられます。
コミット前の簡単な事前確認(誰かがCommitを押す前に)
UIだけでなくデータの現実チェックを少数の行で行ってください。
- 代表的なケースとエッジケースを含めて小さなサンプル(例:20行)をスポットチェックする。
- フィールドマッピングがソース列に合っていることを確認(空セルが意図した動作をするか含む)。
- マッチキー(メール、SKU、外部ID)が十分にユニークで存在するか確認する。
- 合計を比較:何行が作成、更新、スキップされるか。
- 警告を声に出して読み、受け入れ可能か合意する。
人による判断を挟んでください。顧客、請求、在庫に影響するなら所有者の承認を得ます。営業マネージャーが1,200件の連絡先が更新されると期待しているのにプレビューが12,000件を示すなら、その理由が分かるまで進めないでください。
コミット後のチェック(問題を放置しないために)
コミット後は現実を再確認しますが、焦点を絞って行います。
- 更新されたレコードの小さなセットを開いて主要フィールドが正しく変わっているか確認する。
- 行ごとのステータス、作成されたID、エラーを含む結果レポートをエクスポートする。
- 実行した人、実行時間、使用したファイル/バージョン、サマリ件数を記録する。
- エラーが発生したら迅速に判断:失敗行を修正して再試行するか、ロールバックするか。
ノーコードプラットフォームでこのワークフローを作る場合、インポートを単なる管理スクリプトではなく製品機能として扱うと便利です。例えば AppMaster (appmaster.io) では、多くのチームがPostgreSQLにImport Runレコードをモデル化し、Business Process Editorでドライランとコミットロジックを実装して、監査証跡を保ちながら繰り返し可能でサポートしやすい一括更新フローを作っています。
よくある質問
プレビュー、検証、コミットの三段階フローを使ってください。プレビューは何が変わるかを示し、検証はコミットと同じルールでドライランを行い、コミットはその特定のバッチが検証に合格したときだけ有効にします。
プレビューは、誤ったマッピング、期待と異なる作成/更新の数、またはデータを上書きしてしまう空白など、書き込み前に明らかなミスを見つけられるようにします。合計値と少数の「変更前→変更後」サンプルを表示し、影響を目で確認できるようにしましょう。
検証のドライランは、実際の更新と同じ解析、マッチング、権限チェック、ビジネスルールを適用しますが、データベースには書き込みません。出力は分かりやすいサマリと、行ごとの問題一覧で、ユーザーが推測せずに修正できるようにします。
ジョブ全体が安全でない場合(間違ったワークスペース、危険なマッピング、主要フィールドを上書きしてしまうようなケース)は全体を停止します。一方で、電話番号形式が一部の行で不正など、修正可能な問題は行ごとに修正して残りを進められるようにします。
マッチキーを明示し、マッチしなかった場合や複数ヒットした場合の挙動を示してください。安定した内部IDが最も良く、外部IDは統合に便利、メールは使えるが重複と大文字小文字の扱いに注意が必要です。複合キー(例: account_id + invoice_number)が適切な場合もあります。
各フィールドごとにルールを決め、隠さないでください。更新時に空セルが既存値を残すのか、値をクリアするのか、デフォルトを入れるのかを定義し、プレビューで表示しましょう。ユーザーがサプライズを受けないことが重要です。
行番号とメールや外部IDなどの安定した識別子を表示し、列名と宛先フィールドを示して、具体的なメッセージで修正案を出します。ユーザーがソースファイルを素早く修正して再実行できることを目標にしてください。
一貫性が重要な場合(お金、在庫、権限など)は原子的なコミット(All-or-nothing)が適しています。独立した更新(連絡先の情報補完など)では部分コミット(有効な行だけ適用)でもよいでしょう。UIでどちらを選ぶかを明示してください。
検証済みバッチに結びついた冪等キーを使い、ジョブ状態をロックして一度だけコミットが進むようにします。これによりダブルクリックやブラウザのリトライで同じ変更が二重適用されるのを防げます。
PostgreSQLにImport Runレコードをモデル化し、バッチID、マッピングの選択、検証結果、最終結果を保存します。ドライランとコミットのロジックをBusiness Processフローで実装すれば、監査証跡のある繰り返し可能なプロセスになります。


