您的位置:首页 > 数据库

SQL小技巧-关联表重复数据清除处理办法

2018-02-11 15:31 253 查看
一、背景描述    数据库有订单表和订单明细表两张表,订单表与订单明细表的关系为一对多,通过订单ID作为外键进行关联,订单表的订单号字段本应设计成唯一约束,但由于数据库约束未在表里体现,并且由于业务操作的一些原因,导致订单表里出现重复的订单号,并且这些订单记录下面都关联了货品,现需要将重复的订单记录删除,同一个订单号只保留一条记录,其余被删除的订单表记录下面的明细记录全部移到保留的那条记录上面。表结构如下所示(表结构有做简化):
订单表t_order: 
CREATE TABLE `t_order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`order_no` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;
订单明细表t_order_detail: CREATE TABLE `t_order_detail` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`order_id` int(11) DEFAULT NULL,
`sku` varchar(50) DEFAULT NULL,
`qty` int(9) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `order_id_fk` (`order_id`),
CONSTRAINT `order_id_fk` FOREIGN KEY (`order_id`) REFERENCES `t_order` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8;
二、思路分析由于订单表的记录重复,并且重复记录都可能有货品信息,而这些货品信息又不能删除,对订单表,需要做去重处理,同一个订单号只保留一条记录;对于订单货品表,只能将更改订单货品表记录的订单ID,不能出现张冠李戴或是“无主订单明细记录”,难点是如何将订单货品表记录的订单号更新成预期的值。
三、实现步骤1、查询订单表数据重复记录。2、备份有订单表有重复订单号的记录,涉及的订单明细记录也备份一下。3、根据订单号查询订单明细表留下的和删除的ID。4、更新订单明细表记录。5、删除没有订单明细数据的订单表记录。6、抽检数据进行核验。
四、SQL及案例演示1、查询订单表数据重复记录
select order_no,count(1) from t_order group by order_no having count(1) > 1;



2、根据订单号查询订单明细表留下的和删除的ID 
select order_id,retain_id from t_order_detail detail
inner join (
select od2.id as retain_id,od1.id as del_id from t_order od1
inner join (select id,order_no from t_order where order_no in ( '20180211001', '20180211002', '20180211003', '20180211004' ) group by order_no) od2 on od2.order_no = od1.order_no and od1.id <> od2.id
) header on detail.order_id = header.del_id
group by order_id,retain_id



tips:这段SQL需要解释一下1)首先看最内层的子查询:
select id,order_no from t_order where order_no in (
'20180211001',
'20180211002',
'20180211003',
'20180211004'
) group by order_no
里面的order_no就是利用第1步查询的结果,通过订单号进行分组,得到的id将是数据库记录默认排序靠前的记录,这些ID对应的订单表数据将是最终要保留下来的记录。 
2)外一层查询: 
select od2.id as retain_id,od1.id as del_id from t_order od1
inner join (select id,order_no from t_order where order_no in ( '20180211001', '20180211002', '20180211003', '20180211004' ) group by order_no) od2 on od2.order_no = od1.order_no and od1.id <> od2.id
根据订单表自身关联查询,子查询的结果od2.id是需要保留的订单记录,外层查询的列值od1.id是要被更新的记录,这段是在建立“保留-删除”订单记录的关联关系,这样才知道那些将要被删除的记录,它下面的订单明细应该更新到哪些订单记录上去,这里是订单明细表记录更新是否正确的关键。3)利用前面2段查询的结果,order_id列是订单明细表关联订单表现有的记录,retain_id是最终去重后要更新的值。
3、将查询的SQL语句改成update语句
update t_order_detail detail
inner join (
select od2.id as retain_id,od1.id as del_id from t_order od1
inner join (select * from t_order where order_no in (
'20180211001',
'20180211002',
'20180211003',
'20180211004'
) group by order_no
) od2 on od2.order_no = od1.order_no and od1.id <> od2.id
) header on detail.order_id = header.del_id
set detail.order_id = header.retain_id



4、删除已经没有订单明细数据的订单表记录先查询一下:
select od.id,od.order_no from t_order od
left join t_order_detail detail on od.id = detail.order_id
where detail.id is null and od.order_no in (
'20180211001',
'20180211002',
'20180211003',
'20180211004'
)



tips:这段SQL运用left join的一个小技巧,上一步的update语句执行完成之后,将要删除的订单表记录已经完成了订单明细表归属的转移(order_id已经更新成保留下来的订单ID),这样这些要被删除的记录,就没有对应的订单明细记录了,left join并且右边表记录为空的,就是应该要删除的订单表记录。
改成删除语句:
delete from t_order where id in (
select od.id from (select * from t_order) od
left join t_order_detail detail on od.id = detail.order_id
where detail.id is null and od.order_no in (
'20180211001',
'20180211002',
'20180211003',
'20180211004'
))



5、核验数据

1)查询订单表重复记录
select order_no,count(1) from t_order group by order_no having count(1) > 1;
查询结果为空,说明重复数据已经去除掉了。2)数据正确性校验,将备份的数据取样几条,与更新后的数据进行比对,看有没有明细表关联错误的,经核验正确无误。
五、小结1、数据表如果有唯一性约束的,建议在数据库表中加上,不要太过于依赖应用程序,程序也可能会因为并发问题导致数据重复,数据库的唯一性约束将会是保证数据准确性的最后一道防线。2、关联表更新的时候,要先备份,以防万一。3、生产环境数据的更新一定要谨慎处理,尤其是涉及关联表的,如案例中订单明细表记录归属在哪条订单表记录下的问题,一旦弄错后果不堪设想。建议是先取几条数据验证更新结果,无误后再批量处理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  SQL