在数据处理过程中,重复记录不仅会导致统计结果失真,还可能大幅降低查询效率。本文将系统解析SQL中三种核心去重方法——DISTINCT、GROUP BY和窗口函数,通过场景化案例与底层原理分析,帮助读者掌握不同方法的适用策略与优化技巧。
一、DISTINCT:简单去重的利器
DISTINCT是SQL中最基础的去重关键字,适用于单列或多列组合的快速去重。其核心逻辑是对查询结果中完全相同的行进行过滤,仅保留唯一记录。
1.1 使用场景与语法
sql
SELECT DISTINCT visitor_id FROM sales_log;
sql
SELECT DISTINCT visitor_id, view_date FROM sales_log;
此时只有当两列的值完全一致时才会被视为重复记录。
1.2 注意事项与局限性
sql
SELECT COUNT(DISTINCT product_id) FROM orders;
二、GROUP BY:分组聚合的灵活应用
GROUP BY通过分组实现去重,尤其适合需要结合统计计算的场景。例如统计每个用户的最近购买时间,同时计算消费总金额。
2.1 基础分组去重
sql
SELECT user_id, MAX(purchase_time) AS last_purchase
FROM transaction
GROUP BY user_id;
此查询会为每个用户保留最新的一条交易记录,并自动去重。
2.2 多字段分组与聚合
当需要展示更多字段时,需注意:
sql
SELECT user_id, product_type, SUM(amount)
FROM orders
GROUP BY user_id, product_type;
2.3 性能优化建议
三、窗口函数:复杂场景的精准控制
窗口函数(如ROW_NUMBER)支持动态排序与条件筛选,适合需要保留特定顺序数据的场景,例如获取每个用户最近3次登录记录中的第一次。
3.1 核心语法与案例
sql
SELECT
FROM (
SELECT ,
ROW_NUMBER OVER (
PARTITION BY user_id
ORDER BY login_time DESC
) AS rn
FROM user_login
) tmp
WHERE rn <= 3;
此处通过`PARTITION BY`按用户分组,`ORDER BY`按时间降序排列,最终筛选排名前三的记录。
3.2 高阶应用场景
3.3 性能权衡
窗口函数需在内存中完成分组排序,数据量超过百万级时可能产生性能压力,建议配合分区表或分页查询使用。
四、方法对比与选型建议
| 方法 | 适用场景 | 性能影响 | 扩展性 |
||-|-|-|
| DISTINCT | 简单单列/多列去重 | 中等(需排序) | 低 |
| GROUP BY | 分组统计与去重结合 | 较高(需聚合) | 高 |
| 窗口函数 | 保留排序规则的复杂去重 | 高(需分区) | 极高(支持复杂逻辑) |
五、实践中的常见误区与避坑指南
1. DISTINCT多列误解:误以为`DISTINCT a, b`是对a和b分别去重,实际是组合去重。
2. GROUP BY字段遗漏:SELECT中的非聚合字段未包含在GROUP BY会导致语法错误。
3. 窗口函数滥用:无索引的大表使用`PARTITION BY`可能引发内存溢出。
4. NULL值处理:DISTINCT和GROUP BY均保留NULL,需通过`WHERE column IS NOT NULL`提前过滤。
数据去重的本质是在准确性与效率之间寻找平衡点。对于日常开发,建议先用DISTINCT实现基础功能,再通过GROUP BY优化统计需求,最后在复杂业务场景中引入窗口函数。建立索引优化与查询分析机制(如EXPLAIN命令),可有效提升去重操作的执行效率。