一文详解小白也能懂的SQL高效去重技巧

 更新时间:2025年07月06日 09:37:47   作者:一勺菠萝丶  
当你的数据中有重复记录时,如何快速找到每个分组的最新一条,一个优雅的SQL查询就能解决,下面小编就来和大家详细讲解一下SQL高效的去重技巧吧

生活中的例子

想象你管理一家网店,同一个订单(order_number)中的同一商品(product)可能有多次更新记录(比如库存变化、价格调整)。你只想查看每个订单商品的最新状态,这时就需要用到"分组取最新记录"的操作。

原理解析:给数据分组并编号

SELECT
  *,
  ROW_NUMBER() OVER (
    PARTITION BY order_number, product, craft, trade_name 
    ORDER BY create_time DESC
  ) AS rn
FROM client_product

这个查询的核心是ROW_NUMBER()函数,它像老师给学生排队一样:

  • 分组(PARTITION BY):把相同订单+产品+工艺+贸易名称的记录分成一组
  • 排序(ORDER BY):每组内按创建时间倒序排列(最新时间排第一)
  • 编号(rn):给每组内的记录标记序号(1,2,3…)

完整查询解析

SELECT *
FROM (
  -- 步骤1:给所有记录标记组内序号
  SELECT *,
    ROW_NUMBER() OVER (
      PARTITION BY order_number, product, craft, trade_name 
      ORDER BY create_time DESC
    ) AS rn
  FROM client_product
  WHERE 
    production_order_number IS NOT NULL  -- 排除生产订单号为空
    AND order_number IS NOT NULL         -- 排除订单号为空
    AND craft != ''                      -- 排除工艺为空
    AND del_flag = '0'                   -- 只取未删除记录
    AND deliver_status != '0'            -- 排除未交付状态
) AS ranked
-- 步骤2:只取每组最新记录
WHERE rn = 1

关键步骤拆解

1.数据过滤(WHERE)

只处理有效数据:非空订单号、有生产订单号、工艺不为空、未删除、已交付

2.分组标记(ROW_NUMBER)

订单号产品创建时间组内序号(rn)
A1001手机壳2023-01-051(最新)
A1001手机壳2023-01-032
B2002数据线2023-01-041(最新)

3.筛选结果(WHERE rn=1)

只保留每组中rn=1的记录,即每个组合的最新数据

实际应用场景

  • 订单管理:获取每个订单的最新状态
  • 设备监控:读取每个传感器的最新读数
  • 用户行为:提取每个用户最近一次登录记录
  • 价格跟踪:查看每个商品的最新定价

性能小贴士

当数据量很大时:

  • order_number, product, craft, trade_name上创建索引
  • create_time上创建降序索引
  • 定期清理历史数据

方法补充

以下是几种去重的SQL写法

在 SQL 中,数据去重有多种实现方式,以下是几种常见写法及其适用场景:

1. 使用 DISTINCT 关键字

语法:

SELECT DISTINCT column1 [, column2, ...]  
FROM table_name;  

说明:直接对指定字段组合进行唯一性筛选,仅保留首次出现的记录。

示例:

SELECT DISTINCT address FROM student; -- 获取不重复的地址  

局限性:

  • 若对多字段去重,需所有字段值完全相同才视为重复。
  • 无法同时返回非去重字段的原始值,仅能展示去重字段。

2. 使用 GROUP BY 子句

语法:

SELECT column1 [, aggregate_function(column2), ...]  
FROM table_name  
GROUP BY column1 [, column2, ...];  

说明:按指定字段分组,结合聚合函数(如 MAXMINCOUNT 等)获取其他字段信息。
示例:

SELECT MIN(id), address FROM student GROUP BY address; -- 按地址去重,返回每组最小 id  

注意:非聚合字段可能来自不同记录,导致数据逻辑上不一致(如不同 id 对应同一 address 时,聚合函数外的字段取值无明确规律)。

3. 使用窗口函数(如 ROW_NUMBER()

语法:

SELECT *  
FROM (  
    SELECT *, ROW_NUMBER() OVER (PARTITION BY column1 ORDER BY column2) AS rn  
    FROM table_name  
) AS t  
WHERE rn = 1;  

说明:先按 PARTITION BY 分组,再按 ORDER BY 排序并生成行号,筛选行号为 1 的记录。

示例:

SELECT id, name, address  
FROM (  
    SELECT *, ROW_NUMBER() OVER (PARTITION BY address ORDER BY id ASC) AS rn  
    FROM student  
) AS a  
WHERE a.rn = 1; -- 按地址去重,保留每组 id 最小的记录  

优势:可精准控制保留哪条记录(如按时间、ID 排序取最新或最旧),但低版本 MySQL 不支持窗口函数。

4. 使用 IN 子查询

语法:

SELECT *  
FROM table_name  
WHERE id IN (SELECT MAX(id) FROM table_name GROUP BY column1);  

说明:通过子查询找到每组唯一标识字段(如自增 id)的最大值,再筛选主表中对应记录。

示例:

SELECT * FROM student WHERE id IN (SELECT MAX(id) FROM student GROUP BY address); -- 按地址去重,取每组最大 id 的记录  

适用场景:表中存在唯一标识字段(如 id),且需保留特定条件(如最大 / 最小 id)的记录。

5. 使用 NOT EXISTS

语法:

SELECT a.*  
FROM table_name a  
WHERE NOT EXISTS (  
    SELECT 1 FROM table_name b  
    WHERE a.column1 = b.column1 AND a.id < b.id  
);  

示例:

SELECT a.* FROM student a WHERE NOT EXISTS (SELECT 1 FROM student b WHERE a.address = b.address AND a.id < b.id); -- 按地址去重,保留每组 id 最大的记录  

逻辑:对于每一行 a,若不存在 b 行(同 column1 且 id 更大),则保留 a

6. 使用 UNION 去重

语法:

SELECT column1 [, column2, ...]  
FROM table_name1  
UNION  
SELECT column1 [, column2, ...]  
FROM table_name2;  

说明:合并多个查询结果并自动去重(UNION ALL 保留全部记录,不进行去重)。

示例:

SELECT address FROM student UNION SELECT address FROM teacher; -- 合并两表地址并去重  

注意:大数据量时效率较低,建议先用 UNION ALL 再结合其他方法去重。

7. 使用 INNER JOIN + GROUP BY

语法:

SELECT a.*  
FROM table_name a  
INNER JOIN (  
    SELECT column1, MAX(id) AS max_id  
    FROM table_name  
    GROUP BY column1  
) b ON a.column1 = b.column1 AND a.id = b.max_id;  

示例:

SELECT a.* FROM student a  
INNER JOIN (SELECT address, MAX(id) AS max_id FROM student GROUP BY address) b  
ON a.address = b.address AND a.id = b.max_id; -- 按地址去重,取每组最大 id 的记录  

逻辑:先通过子查询获取每组最大 id,再与主表关联筛选。

实际应用中,可根据数据库特性(如是否支持窗口函数)、数据规模、业务需求(如保留特定记录)选择合适的方法。例如,简单单字段去重优先用 DISTINCT;需保留其他字段且数据一致性要求不高时用 GROUP BY;需精准控制保留记录时用窗口函数或 IN/NOT EXISTS 等。

总结

这个查询就像给每个分组内的记录按时间倒序排队,然后只取排在第一位的记录

通过这个技巧,你可以轻松地从重复数据中提取最新记录,让数据清洗和分析变得更高效!下次遇到类似需求时,不妨试试这个强大的ROW_NUMBER()函数吧!

(注:实际使用时需根据业务需求调整分组字段和排序规则)

到此这篇关于一文详解小白也能懂的SQL高效去重技巧的文章就介绍到这了,更多相关SQL去重内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • MySQL 如何将查询结果导出到文件(select … into Statement)

    MySQL 如何将查询结果导出到文件(select … into Statement)

    我们经常会遇到需要将SQL查询结果导出到文件,以便后续的传输或数据分析的场景,本文就MySQL中select…into的用法进行演示,感兴趣的朋友跟随小编一起看看吧
    2024-08-08
  • MySQL中where 1=1方法的使用及改进

    MySQL中where 1=1方法的使用及改进

    这篇文章主要介绍了MySQL中where 1=1方法的使用及改进,文章主要通对where 1 = 1的使用及改进展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-05-05
  • 从索引到架构的MySQL大表查询优化实战指南

    从索引到架构的MySQL大表查询优化实战指南

    在MySQL实际开发中,大表查询慢是最常见、最头疼的性能问题,本文将从索引优化、SQL优化、架构优化、配置优化四个维度出发,结合可复现的实战SQL、原理分析、避坑指南,给出一套全链路的大表查询优化方案,帮你把性能提升100倍以上
    2026-03-03
  • SQL实战之行列互转

    SQL实战之行列互转

    本文介绍了在Hive中进行行转列的几种方法,包括使用CASE WHEN/IF、Get_Json_Object、Str_To_Map以及UNION ALL和EXPLODE函数,每种方法都有其适用场景,感兴趣的可以了解一下
    2024-12-12
  • mysql表物理文件被误删的解决方法

    mysql表物理文件被误删的解决方法

    最近因为失误不小心误删了mysql表的物理文件,这个时候该怎么办呢?然后抓紧从网上找解决的方法,终于解决了,现在将解决的方法及过程分享给大家,有需要的朋友们可以参考借鉴,感兴趣的朋友们下面来一起学习学习吧。
    2016-11-11
  • Mysql数据库之主从分离实例代码

    Mysql数据库之主从分离实例代码

    本篇文章主要介绍了Mysql数据库之主从分离实例代码,MySQL数据库设置读写分离,可以使对数据库的写操作和读操作在不同服务器上执行,提高并发量和相应速度。
    2017-03-03
  • MySQL的子查询及相关优化学习教程

    MySQL的子查询及相关优化学习教程

    这篇文章主要介绍了MySQL的子查询及相关优化学习教程,使用子查询时需要注意其对数据库性能的影响,需要的朋友可以参考下
    2015-11-11
  • MySQL中的快照读和当前读用法

    MySQL中的快照读和当前读用法

    快照读不加锁,读事务开始时的数据快照,确保一致性;当前读加锁,读最新数据,用于更新及锁定操作,两者在RC和RR隔离级别下表现不同,MVCC机制支持快照读,而加锁机制保障当前读的数据一致性
    2025-08-08
  • CentOS6.4上使用yum安装mysql

    CentOS6.4上使用yum安装mysql

    这篇文章主要为大家详细介绍了CentOS6.4上使用yum安装mysql图文教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • Mysql的语句生成后门木马的方法

    Mysql的语句生成后门木马的方法

    这篇文章主要介绍了Mysql的语句生成后门木马的方法,大家不要随意搞破坏哦,小伙伴们学习下就好了。
    2015-04-04

最新评论