如何设计数据库导入、去重与对账
导入不是批量 insert,而是外部事实进入系统前的一道验收门。
这页只讲一件事:同一份银行流水导入两次,系统怎样不重、不漏、不误合并。
| 事故 | 根因 | 设计动作 |
| 同一文件上传两次,月报翻倍。 | 没有批次和文件指纹。 | 建 import_batches,保存来源和 file_hash。 |
| CSV 有错行,但页面显示成功。 | 原始行没有入库,失败原因丢了。 | 建 import_rows,每行都有状态。 |
| 同日同额两笔消费被合并。 | 弱指纹被当成强唯一键。 | 强匹配可自动去重,弱匹配只能出候选。 |
| 手动账和导入账重复。 | 导入直接写事实表。 | 先匹配候选,再决定创建、合并或忽略。 |
| 想撤销导入却不知道删哪几条。 | 事实行没有来源链接。 | 事实表和原始行之间保留链接。 |
判断:事实表只接收已经解析、去重、确认过的数据;导入过程本身也必须可追溯。
| 问题 | 最低设计 |
| 这次导入是谁发起、来自哪个文件或接口? | import_batches 保存来源、文件 hash、状态、统计。 |
| 每一行原始输入去哪了? | import_rows 保存原始内容、解析结果、错误原因。 |
| 什么算重复? | 外部交易号是强匹配;日期金额商户是弱候选。 |
| 手动账和导入账怎么合并? | 候选匹配要保留用户分类、备注和确认记录。 |
| 导入完成怎么证明? | 行数、金额、错误、重复、来源链接都要对得上。 |
import_batches:
id, ledger_id, source_type, file_hash, status
row_count, success_count, duplicate_count, error_count
import_rows:
id, batch_id, row_no, raw_payload
normalized_payload, row_status, error_message
candidate_entry_id, applied_entry_id
entry_import_links:
ledger_entry_id, import_batch_id, import_row_id
external_txn_id, match_type
| 样本 | 通过标准 |
| 同一文件重复上传。 | 第二次不创建新事实行。 |
| 同一交易出现在不同文件。 | 按外部交易号去重。 |
| 没有外部交易号。 | 只能进入候选,不能静默合并。 |
| 解析失败行。 | 有错误原因,批次不能假成功。 |
| 导入到一半失败。 | 重跑幂等,不扩大影响范围。 |
记住:导入设计的核心不是快,而是证据链。先保留批次和原始行,再谈写入事实表。