SQL重复数据面试题_去重与唯一约束考点

5次阅读

面试 sql 重复数据问题需分清查重、去重、防重、修复四场景:查重用 group by+having 或窗口函数;临时去重用 distinct 或 row_number();永久删除用子查询保留 min(id);预防靠 unique 约束或唯一索引,应用层校验须兜底。

SQL 重复数据面试题_去重与唯一约束考点

面试中遇到 SQL 重复数据问题,核心是分清场景:是查重、去重、防止重复,还是修复已有重复。关键不在写多炫的语句,而在理解 业务意图 数据一致性要求

一、怎么快速查出表里有哪些重复数据?

先定位问题,再处理。用 GROUP BY + HAVING 是最直接的方式,重点看重复依据字段(比如姓名 + 手机号组合):

  • 查出所有重复的“姓名 + 邮箱”组合,并统计次数:
    SELECT name, email, COUNT(*) FROM users GROUP BY name, email HAVING COUNT(*) > 1;
  • 想看到重复行的完整记录(含 ID),可用窗口函数(MySQL 8.0+/PostgreSQL/SQL Server 支持):
    SELECT * FROM (SELECT *, COUNT(*) OVER (PARTITION BY name, email) AS cnt FROM users) t WHERE cnt > 1;

二、临时去重:只查不改,返回无重复的结果集

业务查询需要“去重后展示”,但不能删数据——这时不用 DELETE,优先考虑逻辑去重:

  • DISTINCT:适用于整行完全相同(所有字段都一样):
    SELECT DISTINCT name, email FROM users;
  • ROW_NUMBER():按某规则取每组第一条(如保留最新注册的用户):
    SELECT * FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY name, email ORDER BY id DESC) rn FROM users) t WHERE rn = 1;
  • 注意:GROUP BY也能“聚合去重”,但必须明确非分组字段的聚合逻辑(如 MAX(id)),否则报错或结果不可控。

三、永久去重:删除已有重复,只留一条

操作前务必备份!真实删除要谨慎,推荐用子查询或 CTE 锚定保留行:

  • MySQL 常用自连删除(保留最小 id):
    DELETE u1 FROM users u1 INNER JOIN users u2 WHERE u1.name = u2.name AND u1.email = u2.email AND u1.id > u2.id;
  • 通用安全写法(推荐):先查出要删的 ID,再删:
    DELETE FROM users WHERE id NOT IN (SELECT MIN(id) FROM users GROUP BY name, email);
    (注意:若 GROUP BY 字段含 NULL,需额外处理,因 NULL != NULL)

四、预防重复:唯一约束 vs 唯一索引 vs 应用层校验

面试常考设计权衡。根本解法是数据库层面加固:

  • UNIQUE 约束:建表时加,或后期 ALTER,失败时抛异常(如“Duplicate entry”),开发需捕获并提示用户;
  • 唯一索引:效果同 UNIQUE 约束,但可包含 NULL(多数引擎允许多个 NULL),且支持联合、前缀等高级用法;
  • 应用层校验(如先 SELECT 再 INSERT)有竞态风险,高并发下可能仍插入重复——必须配合数据库约束兜底;
  • 扩展思考:若业务允许“软重复”(如同一人多个账号),但需标记主账号,则用外键 + 标志位,而非强行唯一。

不复杂但容易忽略:NULL 值在唯一性判断中的特殊行为、字符集排序规则对比较的影响、以及批量导入时约束是否启用(SET FOREIGN_KEY_CHECKS= 0 等)。真正考察的是你有没有踩过坑、想过边界。

text=ZqhQzanResources