MySQL8.0 去重

1、需求

最近有个需求就是去重获取MySQL中企业的报表数据,同一个企业的报表可以有多个,获取最新的报表数据,这里数据库的版本是8.0。

这里模拟下需求,创建数据库表 test :

create table test
(
    id          int auto_increment  primary key,
    name        varchar(100)                        null,
    age         int                                 null,
    create_time datetime                            null,
    update_time timestamp default CURRENT_TIMESTAMP null
);

并写入重复数据:

INSERT INTO test (id, name, age, create_time, update_time)
VALUES (1, 'zhangsan', 12, '2022-03-17 17:39:56', '2022-03-17 17:39:56');
INSERT INTO test (id, name, age, create_time, update_time)
VALUES (2, 'zhangsan', 14, '2022-03-17 17:39:58', '2022-03-17 17:39:58');
INSERT INTO test (id, name, age, create_time, update_time)
VALUES (3, 'lisi', 19, '2022-03-17 17:39:57', '2022-03-17 17:39:57');

这里name字段代表重复,age 或者 id 用来测试时哪条数据,使用 create_time 验证最新数据。

2、尝试验证

  • 方式1: 分组获取,使用group by
select id,name, age,create_time,update_time
from test
group by name
order by update_time

以上你会发现 MySQL 5.X 是可以执行的,但是 MySQL 8.0 之后不能执行了。
这里MySQL 5.X 获取的是分组前的第一条数据,但是这种写法是有问题的,不符合。

大家使用的时候,也要主要一些语法在5.X 可以执行,但是8.0之后就无法执行了。

  • 方式2:子查询获取id,并通过主键查询

这里的前提是id自增,所以获取最大的id即为最新数据。

select *
from test
where id in (
    select max(id)
    from test
    group by name)

也可以使用这种写法:

select t1.*
from test t1,(
    select max(id) as id
    from test
    group by name) t2
where t1.id = t2.id

执行结果集:

在这里插入图片描述

3、总结

总体思路还是通过 获取最大时间的主键值,然后通过主键查询到具体数据。

当然假如只需要对名称去重就比较简单只需要使用 select distinct name from test 即可。

假如不想用SQL解决,在代码中处理也是可以的,例如使用Java 就可以先进行分组排序,然后获取最大时间的一条数据即可。

<think>我们正在讨论MySQL8.0窗口函数在中的进阶用法。根据引用[2]中的信息,MySQL8.0支持多种窗口函数,包括序号函数(如ROW_NUMBER,RANK,DENSE_RANK)等,这些函数在操作中非常有用。用户要求的是进阶用法,因此我们不仅要介绍基本的方法,还要展示一些高级技巧,比如:1.使用窗口函数删除复数据2.根据条件选择保留哪一条复记录(如保留最新的一条)3.使用窗口函数进行分区我们根据引用[1]的存储过程示例可以生成测试数据,但用户现在关心的是操作。从之前引用[3]中可以看出,索引对查询性能的影响,因此在使用窗口函数时,我们也可以讨论索引对窗口函数性能的影响。另外,引用[4]提到了事务隔离级别,虽然与没有直接关系,但在操作中如果涉及事务,也需要注意隔离级别。但是,用户的问题集中在窗口函数的进阶用法,所以我们点讨论这个。具体回答结构如下:1.窗口函数的基本原理(回顾)2.进阶用法1:根据指定规则保留复项中的一条(如按时间保留最新)3.进阶用法2:多列组合4.进阶用法3:使用窗口函数进行分页(比如每个分组只取前N条)5.性能优化:索引对窗口函数的影响同时,生成相关问题。注意:我们使用ROW_NUMBER()作为主要函数,因为它是最常用的窗口函数。下面开始编写回答。</think> ### MySQL 8.0 窗口函数在操作中的进阶用法 窗口函数(Window Functions)在MySQL 8.0中提供高效的数据能力,尤其适合处理**保留最新记录**、**分组**等复杂场景。以下是进阶用法详解: --- #### 1. **定向保留复数据** 保留每组复值中**最新/最旧**的一条记录(如按时间戳保留最新记录): ```sql WITH RankedData AS ( SELECT *, ROW_NUMBER() OVER( PARTITION BY email ORDER BY create_time DESC -- 倒序取最新 ) AS rn FROM users ) SELECT * FROM RankedData WHERE rn = 1; -- 只保留组内第一条(即最新记录) ``` - **关键点**: - `ORDER BY create_time DESC`:通过排序控制保留规则; - 修改为`ASC`可保留最旧记录[^2]。 --- #### 2. **多层级(组合字段)** 针对多列组合复(如`姓名+电话`),同时保留组内特定特征记录(如余额最大): ```sql SELECT * FROM ( SELECT *, RANK() OVER( PARTITION BY first_name, last_name, phone ORDER BY balance DESC -- 按余额降序 ) AS rank FROM customers ) AS t WHERE rank = 1; -- 每组取余额最大的一条 ``` - **优势**:比`GROUP BY`更灵活控制保留逻辑[^2]。 --- #### 3. **剔除连续复(会话分析)** 仅保留数据流中**首次出现**的值(常用于日志噪): ```sql SELECT id, event_type, event_time FROM ( SELECT *, LAG(event_type) OVER(ORDER BY event_time) AS prev_event FROM user_events ) AS t WHERE event_type <> prev_event OR prev_event IS NULL; -- 仅选取与上一条不同的记录 ``` - **原理**:`LAG()`对比当前行与前一行[^2]。 --- #### 4. **分区分页** 每类数据仅取前N条(如每种商品保留价格最低的3条): ```sql SELECT * FROM ( SELECT *, DENSE_RANK() OVER( PARTITION BY product_category ORDER BY price ASC ) AS dr FROM products ) AS t WHERE dr <= 3; -- 每类商品取前3条最低价 ``` - **注**:`DENSE_RANK()` vs `RANK()`: - `DENSE_RANK()`:无间隔排名(如1,2,2,3) - `RANK()`:有间隔排名(如1,2,2,4)[^2]。 --- #### ⚠️ **性能优化要点** 1. **索引加速**:对`PARTITION BY`和`ORDER BY`涉及的列(如`email`和`create_time`)建复合索引: ```sql CREATE INDEX idx_dept_time ON users(email, create_time); ``` 2. **避免全表扫描**:窗口函数默认操作全表数据,用`WHERE`预先过滤缩小分区范围。 3. **CTE替代子查询**:使用`WITH`语句(如示例1)提升可读性和执行效率[^3]。 --- ### 应用示例 **数据**: | id | email | create_time | |---|-----------|-------------------| | 1 | a@test.com | 2023-01-01 | | 2 | a@test.com | 2023-02-01 | | 3 | b@test.com | 2023-01-15 | **执行结果(保留最新)**: | id | email | create_time | |---|-----------|-------------------| | 2 | a@test.com | 2023-02-01 | | 3 | b@test.com | 2023-01-15 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值