MySQL大量脏数据,如何只保留最新的一条?
因为系统的一个Bug,导致数据库表中出现重复数据,需要做的是删除重复数据且只保留最新的一条数据。
具体场景是这样的
有张订单关联额外费用表,而且一个订单号(order_no)记录只能关联同一个费用(cost_id)一次,但是数据库中出现了同一个订单号关联同一个费用n次
当然有人会说上面的问题我们可以建一个 order_no
+ cost_id
的组合唯一索引,这样就算代码有bug但至少数据库表中不会有脏数据。
似乎这样就可以了,然而事情并没有那么简单。
因为我们表中的数据在删除的时候不会真的的删除,而是采用逻辑删除,会有一个 deleted
字段使用0,1标识未删除与已删除。
当然 我们也可以考虑将 order_no
+ cost_id
+ deleted
组合成一个联合唯一索引。
这样就ok了吗?
其实会有一个新的问题,就是如果同一个订单同一个费用如果被删除一次。再去删除会发现无法成功进行此操作,因为该条数据已经存在了,不能在删除了。
所以当时我们并没有建立联合唯一索引,才导致脏数据的产生。
其实上面这种场景网上有个比较好的解决方案,就是我们依旧可以将 order_no + cost_id + deleted 组合成一个联合唯一索引,
但是删除的时候deleted不再是固定的1,而是当前的主键ID,也就是deleted不等于0都是删除状态,如果删除了那deleted值=id
言归正传,接下来我们来讲下该如何修复脏数据的问题
我们先创建一张订单关联费用表
CREATE TABLE `order_cost_detail` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
`order_no` varchar(32) NOT NULL COMMENT '订单号',
`cost_id` int NOT NULL COMMENT '费用Id',
`cost_name` varchar(50) NOT NULL DEFAULT '' COMMENT '费用名称',
`money` decimal(10,2) NOT NULL COMMENT '金额',
`create_time` datetime NOT NULL COMMENT '创建时间',
`deleted` tinyint(1) NOT NULL COMMENT '是否删除(0 否,1 是)',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 COMMENT='订单 - 费用表';
插入一些模拟数据
INSERT INTO `order_cost_detail` (`id`, `order_no`, `cost_id`, `cost_name`, `money`, `create_time`, `deleted`)
VALUES
(1, 'EX202208160000012-3', 2, '停车费', 100.00, '2022-08-19 11:30:48', 0),
(2, 'EX202208160000012-4', 3, '停车费', 100.00, '2023-02-17 11:25:27', 0),
(3, 'EX202208160000012-4', 3, '停车费', 200.00, '2023-02-17 11:25:28', 0),
(4, 'EX202208170000002-1', 1, '路桥费', 300.00, '2022-08-19 11:31:57', 0),
(5, 'EX202208170000002-1', 1, '路桥费', 450.00, '2022-08-19 11:32:57', 0),
(6, 'EX202208180000002-1', 2, '高速费', 225.00, '2022-08-19 11:35:41', 0);
我们的目的很明确,就是要删除 多余的同一订单号费用相同的数据,同时保留最新的一条数据。
我们可以先用sql看下是否有重复数据
SELECT order_no, cost_name, count(*) AS num
FROM order_cost_detail
WHERE deleted = 0
GROUP BY order_no, cost_name
HAVING num > 1
运行结果
发现有两个订单有脏数据,如果实际生产只有两条脏数据那简单,直接查询这两个订单,把重复数据删掉就好了。
但如果有几十条甚至上百条数据呢,总不能一条一条的删吧。
一般我们删除重复数据都会保留最新的那条,所以我们可以这样做
如果主键是自增的,那么重复数据删除的时候,主键最大的一条就是需要保留的,如果主键不是自增的,我们可以根据创建时间,保留创建时间最大的记录
我们先看下,我们需要删除的记录
select *
from order_cost_detail
where id not in (
select max(id) as num
from order_cost_detail
where deleted = 0
group by order_no, cost_name
)
查询结果
根据结果来看确实是这两条记录需要删除,那么我们开始执行删除操作
sql如下
-- 这里是逻辑删除,也就是将需要删除的数据打上deleted = 1 标记
update order_cost_detail
set deleted = 1
where id in (
select id from order_cost_detail where id not in (
select max(id) as num from order_cost_detail where deleted = 0 group by order_no, cost_name
)
)
执行的时候发现报错了
You can't specify target table 'order_cost_detail' for update in FROM clause
它的意思是说,不能在同一语句中,先select出同一表中的某些值,再update这个表,即不能依据某字段值做判断再来更新某字段的值。
这个问题在MySQL官网中有提到解决方案:拉到文档下面 https://dev.mysql.com/doc/refman/8.0/en/update.html
解决方法:select 的结果再通过一个中间表 select 多一次,就可以避免这个错误
update order_cost_detail
set deleted = 1
where id in (
select t.id
from
(
select id from order_cost_detail where id not in (
select max(id) as num from order_cost_detail where deleted = 0 group by order_no, cost_name )
) t
)
执行成功
阿里巴巴手册索引规范,第一条就是
【强制】业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引。
说明
:不要以为唯一索引影响了insert速度,这个速度损耗可以忽略,但提高查找速度是明显的:另外,即使在应用层做了非常完善
的校验和控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。
声明: 公众号如需转载该篇文章,发表文章的头部一定要 告知是转至公众号: 后端元宇宙。同时也可以问本人要markdown原稿和原图片。其它情况一律禁止转载!
MySQL大量脏数据,如何只保留最新的一条?的更多相关文章
- Mysql 删除重复记录,只保留最小的一条
delete from `jb_postcontent` where id not in(select min(id) from (select * from `jb_postcontent`) as ...
- Mysql 保留最新的10条数据
Mysql每天执行计划,保留最新的10条数据,其余的删除 1.Mysql 保留最新的10条数据 sql语句: DELETE tb FROM tbname AS tb,( SELECT id FROM ...
- SQL中删除重复的行(重复数据),只保留一行 转
方法一:使用在T-SQL的编程中 分配一个列号码,以COL1,COL2组合来分区排序,删除DATABASE重复的行(重复数据),只保留一行 // COL1,COL2是数据库DATABASE的栏位 de ...
- mysql 删除重复数据,并保存最新一条数据
删除重复行 DELETE FROM ecm_member_login_session WHERE (number , client_code) IN ( ) AND update_time NOT I ...
- 你真的会玩SQL吗?删除重复数据且只保留一条
在网上看过一些解决方法 我在此给出的方法适用于无唯一ID的情形 表:TB_MACVideoAndPicture 字段只有2个:mac,content mac作为ID,正常情况下mac数据是唯一的,由于 ...
- SQL删除指定条件的重复数据,只保留一条
BEGIN DELETE TB FROM TableName TB WHERE TB.ID IN (SELECT MIN(ID) FROM TableName TB2 GROUP BY TB2.Col ...
- VBS 移除excel数据公式,只保留值
如果将excel数据公式移除,只保留计算之后的值,将大大减少excel文件. 因为有上篇移除excel外部数据链接的经验,进行excel数据公式移除将快的多,方法如下. 首先我们得明白怎么手动移除ex ...
- mysql删除表中重复数据,只保留一个最小的id的记录
语句: delete from table1 where id not in (select minid from (select min(id) as minid from table1 group ...
- 从mysql数据库删除重复记录只保留其中一条
这两天做了一个调用第三方接口的小程序,因为是实时更新数据,所以请求接口的频率就很高,这样有时会出现往数据库插入重复的数据,对数据库造成压力也不方便管理,因为要通过原生sql语句,解决数据库的去重问题. ...
- mysql 分组后取每个组内最新的一条数据
首先,将按条件查询并排序的结果查询出来. mysql order by accepttime desc; +---------------------+------+-----+ | acceptti ...
随机推荐
- vite+vue3使用unplugin-auto-import 无需手动引入api!
近期了解到unplugin-auto-import这个插件 用途是无需每个组件内重复的引入vue vue-router等内置方法 下面举个例子 <script setup> import ...
- Resnet网络--BasicBlock与BottleNeck
ResNetV2的网络深度有18,34,50,101,152.50层以下的网络基础块是BasicBlock,50层及以上的网络基础块是BottleNeck. BasicBlock 图示如下 代码实现 ...
- 【原创】GmSSL Linux编译, 环境搭建
编译环境: wsl ubuntu 20.04 GmSSL Project 算法相关原理文档 由于GmSSL继承自openssl, 为了防止和openssl冲突,最好将GmsSL 编译为静态库 在Lin ...
- Neo4j学习(3)--JavaAPI
Neo4j Java操作 1. Neo4j Java Driver方式操作 使用该方式对数据进行操作时,必须先将Neo4j的服务启动起来. 从官方下载neo4j的Java驱动:https://neo4 ...
- Python学习笔记--函数来啦!
函数 函数,就是组织好的,可重复使用的,用来实现特定功能的代码块 实际的小案例:不使用内置函数len,利用函数知识计算出字符串的长度 实现: 函数的基础定义语法 案例:自动查核酸 实现: 函数的传入参 ...
- webgl 系列 —— 渐变三角形
其他章节请看: webgl 系列 渐变三角形 本文通过一个渐变三角形的示例逐步分析:varying变量.合并缓冲区.图形装配.光栅化.varying 内插 绘制三个点v1 需求:绘制三个相同颜色的点, ...
- 从开源模型、框架到自研,声网 Web 端虚拟背景算法正式发布
根据研究发现,在平均 38 分钟的视频会议里面,大概会有 13 分钟左右的时间用于处理和干扰相关的事情.同时研究也表明在参加在线会议的时候,人们更加倾向于语音会议,其中一个关键原因就是大家不希望个人隐 ...
- P8112 符文破译
题目描述 将字符串 \(T\) 拆成若干个子串,使这些子串为字符串 \(S\) 的前缀,要求拆分形成的子串数最小. 思路整理 实际上并不需要倒着枚举,也不需要线段树,更不需要 Z 函数. 如果你做过 ...
- CSPS2019 括号树 题解
链的部分分 我们设f[i]表示以i结尾的括号序列有多少个,那么i的实际答案就是f的前缀和 显然,所有左括号和不能匹配的右括号的f均为0 对于每一个能匹配的右括号i,我们找到与之匹配的左括号p,以i结尾 ...
- java多线程--3 线程状态、线程方法、线程类型
java多线程--3 线程状态.线程方法.线程类型 线程状态 创建状态: **就绪状态: ** 进入状态: 创建状态:启动线程 阻塞状态:阻塞解除 运行状态:释放CPU资源 阻塞状态: 进入状态: 运 ...