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 用户权限管理
Apr 20 MySQL
mysql数据库入门第一步之创建表
May 14 MySQL
MySQL中distinct与group by之间的性能进行比较
May 26 MySQL
MySQL 亿级数据导入导出及迁移笔记
Jun 18 MySQL
MySQL GRANT用户授权的实现
Jun 18 MySQL
新手入门Mysql--概念
Jun 18 MySQL
MYSQL 的10大经典优化案例场景实战
Sep 14 MySQL
MySQL表类型 存储引擎 的选择
Nov 11 MySQL
mysql聚集索引、辅助索引、覆盖索引、联合索引的使用
Feb 12 MySQL
教你如何让spark sql写mysql的时候支持update操作
Feb 15 MySQL
进阶篇之linux环境下安装MySQL数据库
Apr 09 MySQL
MySQL视图概念以及相关应用
Apr 19 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
IIS6.0+PHP5.x+MySQL5.x+Zend3.0x+GD+phpMyAdmin2.8x通用安装实例(已经完成)
2006/12/06 PHP
织梦模板标记简介
2007/03/11 PHP
php stripslashes和addslashes的区别
2014/02/03 PHP
windows8.1下Apache+Php+MySQL配置步骤
2015/10/30 PHP
Ext第一周 史上最强学习笔记---GridPanel(基础篇)
2008/12/29 Javascript
js+数组实现网页上显示时间/星期几的实用方法
2013/01/18 Javascript
JS实现进入页面时渐变背景色的方法
2015/02/25 Javascript
js图片轮播特效代码分享
2015/09/07 Javascript
利用Vue实现移动端图片轮播组件的方法实例
2017/08/23 Javascript
react native与webview通信的示例代码
2017/09/25 Javascript
微信小程序实现YDUI的ScrollNav组件
2018/02/02 Javascript
Vuejs通过拖动改变元素宽度实现自适应
2020/09/02 Javascript
Python sys.argv用法实例
2015/05/28 Python
matplotlib调整子图间距,调整整体空白的方法
2018/08/03 Python
Python sorted函数详解(高级篇)
2018/09/18 Python
详解Python的三种可变参数
2019/05/08 Python
Tensorflow模型实现预测或识别单张图片
2019/07/19 Python
Python爬虫:将headers请求头字符串转为字典的方法
2019/08/21 Python
python 识别登录验证码图片功能的实现代码(完整代码)
2020/07/03 Python
python爬虫 requests-html的使用
2020/11/30 Python
保加利亚服装和鞋类购物网站:Bibloo.bg
2020/11/08 全球购物
通用C#笔试题附答案
2016/11/26 面试题
七年级地理教学反思
2014/01/26 职场文书
事业单位竞聘上岗实施方案
2014/03/28 职场文书
建筑工地门卫岗位职责
2014/04/30 职场文书
护校行动方案
2014/05/31 职场文书
红领巾广播站广播稿
2014/10/19 职场文书
付款承诺函范文
2015/01/21 职场文书
房租涨价通知
2015/04/23 职场文书
教师节作文之小学四年级
2019/09/03 职场文书
原生JS中应该禁止出现的写法
2021/05/05 Javascript
【海涛教你打DOTA】死灵飞龙第一视角解说
2022/04/01 DOTA
解决 Redis 秒杀超卖场景的高并发
2022/04/12 Redis
Python捕获、播放和保存摄像头视频并提高视频清晰度和对比度
2022/04/14 Python
mysql 获取相邻数据项
2022/05/11 MySQL
MYSQL事务的隔离级别与MVCC
2022/05/25 MySQL