postgresql如何找到表中重复数据的行并删除


Posted in MySQL onMay 08, 2023

postgresql找到表中重复数据的行并删除

创建测试表并插入数据

create table aaa(id bigserial,col1 varchar(255));
insert into aaa values(1,'b'),(2,'a'),(3,'b'),(4,'c');
select * from aaa;

找到重复行并删除

方法1:ctid表示数据行在它所处的表内的物理位置,ctid由两个数字组成,第一个数字表示物理块号,第二个数字表示在物理块中的行号。

select * from aaa where ctid not in(select max(ctid) from aaa group by col1);

删除重复行

delete from aaa where ctid not in(select max(ctid) from aaa group by col1);

方法2:利用exists

找到重复行

select * from aaa t1 where  exists (select 1 from aaa t2 where t1.col1=t2.col1 and t1.id<t2.id )----exists后的意思是同一列相等,但是自增id不相等且id小的那一个

删除重复行

delete from aaa t1 where  exists (select 1 from aaa t2 where t1.col1=t2.col1 and t1.id<t2.id )

postgresql常用的删除重复数据方法

最高效方法

测试环境验证,6600万行大表,删除2200万重复数据仅需3分钟

delete from deltest a where a.ctid = any(array (select ctid from (select row_number() over (partition by id), ctid from deltest) t where t.row_number > 1));

PG中三种删除重复数据方法

首先创建一张基础表,并插入一定量的重复数据。

create table deltest(id int, name varchar(255));
create table deltest_bk (like deltest);
insert into deltest select generate_series(1, 10000), 'ZhangSan';
insert into deltest select generate_series(1, 10000), 'ZhangSan';
insert into deltest_bk select * from deltest;

1. 常规删除方法

最容易想到的方法就是判断数据是否重复,对于重复的数据只保留ctid最小(或最大)的数据,删除其他的。

explain analyse delete from deltest a where a.ctid <> (select min(t.ctid) from deltest t where a.id=t.id);
-------------------------------------------------------------------------------------------
    Delete on deltest a  (cost=0.00..195616.30 rows=1518 width=6) (actual time=67758.866..67758.866 rows=0 loops=1)
       ->  Seq Scan on deltest a  (cost=0.00..195616.30 rows=1518 width=6) (actual time=32896.517..67663.228 rows=10000 loops=1)
         Filter: (ctid <> (SubPlan 1))
         Rows Removed by Filter: 10000
         SubPlan 1
           ->  Aggregate  (cost=128.10..128.10 rows=1 width=6) (actual time=3.374..3.374 rows=1 loops=20000)
                 ->  Seq Scan on deltest t  (cost=0.00..128.07 rows=8 width=6) (actual time=0.831..3.344 rows=2 loops=20000)
                       Filter: (a.id = id)
                       Rows Removed by Filter: 19998
Total runtime: 67758.931 ms
select count(*) from deltest;
count
-------
10000

可以看到,id相同的数据,保留ctid最小的,其他的删除。相当于把deltest表中的数据删掉一半,耗时达到67s多。相当慢。

2. group by删除方法

group by方法通过分组找到ctid最小的数据,然后删除其他数据。

explain analyse delete from deltest a where a.ctid not in (select min(ctid) from deltest group by id);
-------------------------------------------------------------------------------------------
    Delete on deltest a  (cost=131.89..2930.46 rows=763 width=6) (actual time=30942.496..30942.496 rows=0 loops=1)
       ->  Seq Scan on deltest a  (cost=131.89..2930.46 rows=763 width=6) (actual time=10186.296..30814.366 rows=10000 loops=1)
         Filter: (NOT (SubPlan 1))
         Rows Removed by Filter: 10000
         SubPlan 1
           ->  Materialize  (cost=131.89..134.89 rows=200 width=10) (actual time=0.001..0.471 rows=7500 loops=20000)
                 ->  HashAggregate  (cost=131.89..133.89 rows=200 width=10) (actual time=10.568..13.584 rows=10000 loops=1)
                       ->  Seq Scan on deltest  (cost=0.00..124.26 rows=1526 width=10) (actual time=0.006..3.829 rows=20000 loops=1)
     Total runtime: 30942.819 ms
select count(*) from deltest;
count
-------
10000

可以看到同样是删除一半的数据,使用group by的方式,时间节省了一半。但仍含需要30s,下面试一下第三种删除操作。

3. 高效删除方法

explain analyze delete from deltest a where a.ctid = any(array (select ctid from (select row_number() over (partition by id), ctid from deltest) t where t.row_number > 1));
-----------------------------------------------------------------------------------------
    Delete on deltest a  (cost=250.74..270.84 rows=10 width=6) (actual time=98.363..98.363 rows=0 loops=1)
    InitPlan 1 (returns 0)−>SubqueryScanont(cost=204.95..250.73rows=509width=6)(actualtime=29.446..47.867rows=10000loops=1)Filter:(t.rownumber>1)RowsRemovedbyFilter:10000−>WindowAgg(cost=204.95..231.66rows=1526width=10)(actualtime=29.436..44.790rows=20000loops=1)−>Sort(cost=204.95..208.77rows=1526width=10)(actualtime=12.466..13.754rows=20000loops=1)SortKey:deltest.idSortMethod:quicksortMemory:1294kB−>SeqScanondeltest(cost=0.00..124.26rows=1526width=10)(actualtime=0.021..5.110rows=20000loops=1)−>TidScanondeltesta(cost=0.01..20.11rows=10width=6)(actualtime=82.983..88.751rows=10000loops=1)TIDCond:(ctid=ANY(0)−>SubqueryScanont(cost=204.95..250.73rows=509width=6)(actualtime=29.446..47.867rows=10000loops=1)Filter:(t.rownumber>1)RowsRemovedbyFilter:10000−>WindowAgg(cost=204.95..231.66rows=1526width=10)(actualtime=29.436..44.790rows=20000loops=1)−>Sort(cost=204.95..208.77rows=1526width=10)(actualtime=12.466..13.754rows=20000loops=1)SortKey:deltest.idSortMethod:quicksortMemory:1294kB−>SeqScanondeltest(cost=0.00..124.26rows=1526width=10)(actualtime=0.021..5.110rows=20000loops=1)−>TidScanondeltesta(cost=0.01..20.11rows=10width=6)(actualtime=82.983..88.751rows=10000loops=1)TIDCond:(ctid=ANY(0))
    Total runtime: 98.912 ms
select count(*) from deltest;
count
-------
10000

可以看到,居然只要98ms

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。

MySQL 相关文章推荐
MySQL官方导出工具mysqlpump的使用
May 21 MySQL
浅谈mysql返回Boolean类型的几种情况
Jun 04 MySQL
解决mysql模糊查询索引失效问题的几种方法
Jun 18 MySQL
MySQL REVOKE实现删除用户权限
Jun 18 MySQL
MySQL系列之七 MySQL存储引擎
Jul 02 MySQL
一篇文章带你深入了解Mysql触发器
Aug 02 MySQL
教你如何让spark sql写mysql的时候支持update操作
Feb 15 MySQL
MySQL为数据表建立索引的原则详解
Mar 03 MySQL
MySQL 表锁定 LOCK和UNLOCK TABLES的 SQL语法
Apr 18 MySQL
MySql如何将查询的出来的字段进行转换
Jun 14 MySQL
MySQL使用IF语句及用case语句对条件并结果进行判断 
Sep 23 MySQL
MySQL深分页问题解决思路
Dec 24 MySQL
SQL Server数据库的三种创建方法汇总
May 08 #MySQL
SQL中去除重复数据的几种方法汇总(窗口函数对数据去重)
May 08 #MySQL
MySQL中TIMESTAMP类型返回日期时间数据中带有T的解决
Dec 24 #MySQL
MySQL实现用逗号进行拼接、以逗号进行分割
Dec 24 #MySQL
MySQL数据管理操作示例讲解
Dec 24 #MySQL
MySQL深分页问题解决思路
Dec 24 #MySQL
DQL数据查询语句使用示例
Dec 24 #MySQL
You might like
PHP中构造函数和析构函数解析
2014/10/10 PHP
smarty中js的调用方法示例
2014/10/27 PHP
php设计模式之观察者模式实例详解【星际争霸游戏案例】
2020/03/30 PHP
JavaScript字符串插入、删除、替换函数使用示例
2013/07/25 Javascript
js获取当前月的第一天和最后一天的小例子
2013/11/18 Javascript
JavaScript设计模式之装饰者模式介绍
2014/12/28 Javascript
基于Flowplayer打造一款免费的WEB视频播放器附源码
2015/09/06 Javascript
每天一篇javascript学习小结(Array数组)
2015/11/11 Javascript
获取当前月(季度/年)的最后一天(set相关操作及应用)
2016/12/27 Javascript
JQuery中Ajax的操作完整例子
2017/03/07 Javascript
Vue2组件tree实现无限级树形菜单
2017/03/29 Javascript
angular.js实现购物车功能
2017/10/23 Javascript
微信小程序中进行地图导航功能的实现方法
2018/06/29 Javascript
vue百度地图 + 定位的详解
2019/05/13 Javascript
Vue解决移动端弹窗滚动穿透问题
2020/12/15 Vue.js
利用JS判断元素是否为数组的方法示例
2021/01/08 Javascript
详解在Python程序中解析并修改XML内容的方法
2015/11/16 Python
Python学习笔记之if语句的使用示例
2017/10/23 Python
python爬虫获取小区经纬度以及结构化地址
2018/12/30 Python
对python中Json与object转化的方法详解
2018/12/31 Python
jupyter notebook 中输出pyecharts图实例
2020/04/23 Python
Python3+Appium实现多台移动设备操作的方法
2019/07/05 Python
Python的条件锁与事件共享详解
2019/09/12 Python
Python语言编写智力问答小游戏功能
2020/10/13 Python
发现两个有趣的CSS3动画效果
2013/08/14 HTML / CSS
canvas 基础之图像处理的使用
2020/04/10 HTML / CSS
中国综合网上购物商城:苏宁易购
2016/08/09 全球购物
日本高端护肤品牌:Tatcha
2016/08/29 全球购物
为什么UNION ALL比UNION快
2016/03/17 面试题
服装设计专业毕业生推荐信
2013/11/09 职场文书
学校后勤人员职责
2013/12/27 职场文书
高中生第一学年自我鉴定
2014/09/12 职场文书
2015年师德表现自我评价
2015/03/05 职场文书
2015年大学生村官工作总结
2015/04/21 职场文书
迎客户欢迎词三篇
2019/09/27 职场文书
Python pandas读取CSV文件的注意事项(适合新手)
2021/06/20 Python