在日常工作中,处理数据库时经常会遇到重复数据的问题。比如一个电商系统中,用户可能因为网络延迟多次提交订单,导致订单表里出现多条相同的记录。这时候就需要用到 SQL 查询去重的技术,把重复的数据筛出来或者直接过滤掉。
使用 DISTINCT 去除完全重复的行
最简单的去重方式是使用 DISTINCT 关键字。它能去掉查询结果中完全相同的行。比如要查看所有不同的用户邮箱,可以这样写:
SELECT DISTINCT email FROM users;
这条语句会返回 users 表中所有不重复的邮箱地址。适用于整行数据都相同的情况。
基于某些字段去重:GROUP BY 的用法
有时候并不是整行重复才算重复,而是关键字段一样就视为重复。例如订单表里,只要用户 ID 和商品 ID 相同,就认为是重复下单。这时可以用 GROUP BY 配合聚合函数来处理:
SELECT user_id, product_id, MIN(order_time) AS first_order
FROM orders
GROUP BY user_id, product_id;
这样每个用户对每件商品只保留最早的一条记录,达到去重目的。
保留一条记录删除其余重复项
如果想从表中真正删掉重复数据,只留一条,可以借助数据库的自增主键。假设表有 id 字段,想要根据 name 和 phone 去重,保留 id 最小的那条:
DELETE FROM customers
WHERE id NOT IN (
SELECT MIN(id)
FROM customers
GROUP BY name, phone
);
这个方法在清理脏数据时特别有用,比如客服系统导入客户信息时不小心重复上传了几次。
使用窗口函数精准控制去重逻辑
对于更复杂的场景,比如需要按时间排序后为每个人保留最近的一次登录记录,窗口函数 ROW_NUMBER() 就派上用场了:
SELECT login_id, user_id, login_time
FROM (
SELECT login_id, user_id, login_time,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_time DESC) AS rn
FROM user_logins
) t
WHERE rn = 1;
这里先把每个用户的登录记录按时间倒序编号,然后只取编号为 1 的,也就是最新的那次,实现高效去重。
避免插入重复数据的设计思路
与其事后清理,不如提前预防。可以在数据库表上加唯一索引,比如在用户注册表中对手机号建立唯一约束:
ALTER TABLE users ADD UNIQUE INDEX idx_phone (phone);
这样一来,程序再尝试插入相同手机号就会报错,强制业务层做判断,减少重复数据产生。
实际应用中,去重不只是技术问题,还涉及业务规则。比如两个订单金额、时间、商品都一样,到底算不算重复?这得和产品沟通清楚。SQL 提供了工具,但怎么用还得结合具体情况。”}