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 相关文章推荐
Windows10下安装MySQL8
Apr 06 MySQL
MySQL 数据丢失排查案例
May 08 MySQL
mysql外连接与内连接查询的不同之处
Jun 03 MySQL
mysql left join快速转inner join的过程
Jun 30 MySQL
mysql5.7的安装及Navicate长久免费使用的实现过程
Nov 17 MySQL
mysql 联合索引生效的条件及索引失效的条件
Nov 20 MySQL
一次SQL如何查重及去重的实战记录
Mar 13 MySQL
MySQL限制查询和数据排序介绍
Mar 25 MySQL
Mysql超详细讲解死锁问题的理解
Apr 01 MySQL
Mysql 如何合理地统计一个数据库里的所有表的数据量
Apr 18 MySQL
MySQL数据库如何查看表占用空间大小
Jun 10 MySQL
关于mysql中string和number的转换问题
Jun 14 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
E路文章系统PHP
2006/12/11 PHP
在PHP中检查PHP文件是否有语法错误的方法
2009/12/23 PHP
PHP删除目录及目录下所有文件的方法详解
2013/06/06 PHP
深入file_get_contents函数抓取内容失败的原因分析
2013/06/25 PHP
php设置session值和cookies的学习示例
2014/03/21 PHP
ThinkPHP3.1新特性之对页面压缩输出的支持
2014/06/19 PHP
使用PHP uniqid函数生成唯一ID
2015/11/18 PHP
PHP查询大量数据内存耗尽问题的解决方法
2016/10/28 PHP
phpStudy中升级MySQL版本到5.7.17的方法步骤
2017/08/03 PHP
laravel 框架实现无限级分类的方法示例
2019/10/31 PHP
javascript动画之圆形运动,环绕鼠标运动作小球
2010/07/20 Javascript
xss文件页面内容读取(解决)
2010/11/28 Javascript
面向对象的Javascript之三(封装和信息隐藏)
2012/01/27 Javascript
JavaScript高级程序设计 阅读笔记(四) ECMAScript中的类型转换
2012/02/27 Javascript
JavaScript中的面向对象介绍
2012/06/30 Javascript
JS+CSS实现弹出全屏灰黑色透明遮罩效果的方法
2014/12/20 Javascript
深入理解JavaScript中的对象
2015/06/04 Javascript
jQuery实现点击小图显示大图代码分享
2015/08/25 Javascript
JS实现兼容火狐及IE iframe onload属性的遮罩层隐藏及显示效果
2016/08/23 Javascript
Bootstrap CSS布局之图像
2016/12/17 Javascript
浅谈js基础数据类型和引用类型,深浅拷贝问题,以及内存分配问题
2017/09/02 Javascript
jQuery实现的简单图片轮播效果完整示例
2018/02/08 jQuery
实例详解BootStrap的动态模态框及静态模态框
2018/08/13 Javascript
Web安全之XSS攻击与防御小结
2018/12/13 Javascript
vue基本使用--refs获取组件或元素的实例
2019/11/07 Javascript
Vue封装Axios请求和拦截器的步骤
2020/09/16 Javascript
Python程序中使用SQLAlchemy时出现乱码的解决方案
2015/04/24 Python
Python实现的手机号归属地相关信息查询功能示例
2017/06/08 Python
python numpy 一维数组转变为多维数组的实例
2018/07/02 Python
python 类之间的参数传递方式
2019/12/20 Python
PyQt5的QWebEngineView使用示例
2020/10/20 Python
Koral官方网站:女性时尚运动服
2019/04/10 全球购物
POP文化和音乐灵感的时尚:Hot Topic
2019/06/19 全球购物
2014年全国爱牙日宣传活动方案
2014/09/21 职场文书
小学班主任经验交流材料
2014/12/16 职场文书
给numpy.array增加维度的超简单方法
2021/06/02 Python