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 11 MySQL
MySQL root密码的重置方法
Apr 21 MySQL
MySQL EXPLAIN输出列的详细解释
May 12 MySQL
mysql5.7使用binlog 恢复数据的方法
Jun 03 MySQL
MySQL 不等于的三种使用及区别
Jun 03 MySQL
浅谈MySQL 亿级数据分页的优化
Jun 15 MySQL
MYSQL 表的全面总结
Nov 11 MySQL
MySQL中order by的使用详情
Nov 17 MySQL
分享MySQL常用 内核 Debug 几种常见方法
Mar 17 MySQL
将MySQL的表数据全量导入clichhouse库中
Mar 21 MySQL
MySQL数据库 任意ip连接方法
May 20 MySQL
MySQL控制流函数(-if ,elseif,else,case...when)
Jul 07 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
虹吸壶是谁发明的?煮出来的咖啡好喝吗
2021/03/04 冲泡冲煮
用PHP提取中英文词语以及数字的首字母的方法介绍
2013/04/23 PHP
PHP实现图片压缩的两则实例
2014/07/19 PHP
php5.3提示Function ereg() is deprecated Error问题解决方法
2014/11/12 PHP
php递归json类实例
2014/12/02 PHP
PHP简单实现HTTP和HTTPS跨域共享session解决办法
2015/05/27 PHP
js定义对象或数组直接量时各浏览器对多余逗号的处理(json)
2011/03/05 Javascript
《JavaScript高级程序设计》阅读笔记(一) ECMAScript基础
2012/02/27 Javascript
node.js中的events.emitter.listeners方法使用说明
2014/12/10 Javascript
jQuery给元素添加样式的方法详解
2015/12/30 Javascript
轮播的简单实现方法
2016/07/28 Javascript
通过sails和阿里大于实现短信验证
2017/01/04 Javascript
javascript实现简易计算器
2017/02/01 Javascript
jQuery实现获取h1-h6标题元素值的方法
2017/03/06 Javascript
基于javascript的异步编程实例详解
2017/04/10 Javascript
在一般处理程序(ashx)中弹出js提示语
2017/08/16 Javascript
vuejs中监听窗口关闭和窗口刷新事件的方法
2018/09/21 Javascript
jQuery实现中奖播报功能(让文本滚动起来) 简单设置数值即可
2020/03/20 jQuery
微信小程序中的列表切换功能实例代码详解
2020/06/09 Javascript
js实现翻牌小游戏
2020/07/31 Javascript
MySQL最常见的操作语句小结
2015/05/07 Python
python编程开发之类型转换convert实例分析
2015/11/13 Python
python redis连接 有序集合去重的代码
2019/08/04 Python
python关闭占用端口方式
2019/12/17 Python
Django如何使用redis作为缓存
2020/05/21 Python
python 常见的排序算法实现汇总
2020/08/21 Python
python软件测试Jmeter性能测试JDBC Request(结合数据库)的使用详解
2021/01/26 Python
html5定位并在百度地图上显示的示例
2014/04/27 HTML / CSS
美国南部最大的家族百货公司:Belk
2017/01/30 全球购物
Invicta手表官方商店:百年制表历史的瑞士腕表品牌
2019/09/26 全球购物
工程造价与财务管理专业应届生求职信
2013/10/06 职场文书
团支书的期末学习总结自我评价
2013/11/01 职场文书
遗产继承公证书
2014/04/09 职场文书
电子商务专业求职信范文
2015/03/19 职场文书
学习新党章心得体会2016
2016/01/15 职场文书
导游词之襄阳古城
2019/09/27 职场文书