收藏
0
0
分享
举报
·2 小时前发布·69次阅读

CSV导入前:重复记录到底怎么处理?

独立开发商业增长付费用户

假设一个小型客户管理工具支持导入 CSV。文件有一百行,页面显示“导入成功”。用户打开列表却发现客户备注没了,或者只有九十七条新增记录。问题未必出在 CSV 解析上,也可能是产品从未明确“重复”的含义。

下面是一份设计推演,不是某个真实产品的事故复盘。我建议先把重复处理写成用户能理解的约定,再决定数据库语句。

不要让“覆盖”代替业务规则

假设客户表包含内部 ID、邮箱、姓名、备注。文件里某行邮箱已经存在,但姓名有改动,备注为空。至少有三个问题需要决定:

  • 邮箱是否真的能作为客户唯一标识?共享邮箱与邮箱变更怎么处理?
  • 空备注意味着清空,还是这份文件没有提供备注?
  • 用户只想更新姓名,是否也允许改变原记录的内部 ID 或关联关系?

我的默认建议是:只更新用户明确映射并选择更新的字段;“缺少该列”“该单元格为空”“明确要求清空”分别定义。它不是通用行业答案,通讯录、库存和财务数据需要各自的业务规则。

SQLite 的一个容易误读的词

SQLite 官方文档说明,REPLACE 在遇到 UNIQUE 或 PRIMARY KEY 冲突时,会先删除造成冲突的已有行,再继续插入或更新。它不能仅凭名称就被理解成“保留原记录,只改传入字段”。文档也明确区分传统冲突处理语法与后来加入的 UPSERT。

因此,如果业务要求保留记录身份,应检查显式 UPDATE 或适合该业务的 UPSERT 方案,而不是把“覆盖已有”直接翻译成 INSERT OR REPLACE。最终仍要结合表结构、关联和实际 SQL 验证,不能靠换一个关键词就宣告安全。

一个可复现的小实验

为核对上面的语义,2026-11-05 在本地 Python 的 SQLite 3.45.3 内存数据库执行了下面的最小例子。数据是专门构造的,不涉及真实客户:

CREATE TABLE customer(
  id INTEGER PRIMARY KEY,
  email TEXT UNIQUE,
  name TEXT,
  note TEXT DEFAULT ''
);
INSERT INTO customer VALUES(
  1, 'a@example.test', '旧姓名', '已确认的备注'
);
INSERT OR REPLACE INTO customer(email, name)
VALUES('a@example.test', '新姓名');
SELECT id, email, name, note FROM customer;

实际得到 (2, 'a@example.test', '新姓名', '')。这个例子里,原来的 ID 1 变成了 2,没有提供的备注回到了空字符串默认值。它证明的是这套表结构与语句组合的结果,不能推导成所有 REPLACE 都一定改变 ID;如果显式提供 ID、采用不同表结构或触发其他约束,需要另测。

更有用的验收问题是:业务究竟要求保留哪些值?先把“备注仍为已确认的备注、身份仍为原客户”写成预期,再选择满足它的更新语句。

把导入预览写成变更清单

对于小规模导入,我会设计一个确认页:新增多少、更新多少、保持不变多少、需要处理多少。每个待更新对象能展开看“字段原值 → 新值”。这几个数量必须使用同一套互斥分类,不能一条既算更新又算错误。

例如,假设一百行输入中,六十行是新增,二十五行仅更新姓名,十行没有变化,五行有问题;确认页应明确五行的问题是什么,以及整个批次是阻止提交,还是允许用户明确选择跳过后提交其余行。这些数字只是说明分类的假设数据。

错误下载表也应给出可修正信息:源文件行号、业务标识、字段、失败原因。只写“数据库错误”对下一次导入没有帮助。

预览通过,不代表提交时条件仍成立

预览后,另一位成员可能修改客户姓名。我的建议是在预览中保存记录版本,提交时比较;发现变化就暂停相关更新,展示新的差异,让用户重新确认。具体实现可以是版本字段或其他并发控制机制,取决于现有系统。

同时明确原子性:全批次成功才生效,还是允许部分成功?如果选择部分成功,结束页必须列出成功与失败的对象,不应只给一个绿色“完成”。若提供撤销,也要考虑导入之后的正常编辑,不能粗暴用旧快照覆盖别人刚做的修改。

第一轮就值得验证的五种输入

同一文件内重复两次同一个标识;与库内已有记录冲突;缺列与空单元格并存;预览后原记录被修改;失败发生在批次中段。

对每种输入,先写预期变更,再检查最终数据及用户看到的统计。不要只测试“函数没有报错”。一份能讲清楚哪些内容将被改变的预览,才是用户敢按下导入按钮的依据。

资料:SQLite《The ON CONFLICT Clause》https://sqlite.org/lang_conflict.html ,页面标注更新于 2025-11-22,本文查阅于 2026-11-05。本文使用其中 REPLACE 的语义说明;预览流程、字段约定和测试清单是独立设计建议,没有宣称进行过生产验证。

讨论 (0)

0/250

    目录

    关于作者

    心得体会

    TA 还没有留下个人简介

    Solo 独立开发者社区support@solo.xin
    关于社区成长规则SOLO.cc隐私政策用户协议商务合作友情链接订阅更新(RSS)投稿赞助

    © 2023 SOLO · 为独立开发者而生