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 22 MySQL
JDBC连接的六步实例代码(与mysql连接)
May 12 MySQL
MySQL大小写敏感的注意事项
May 24 MySQL
mysql联合索引的使用规则
Jun 23 MySQL
MySQL query_cache_type 参数与使用详解
Jul 01 MySQL
将MySQL的表数据全量导入clichhouse库中
Mar 21 MySQL
mysql中数据库覆盖导入的几种方式总结
Mar 25 MySQL
MySQL实现配置主从复制项目实践
Mar 31 MySQL
Innodb存储引擎中的后台线程详解
Apr 03 MySQL
详解Mysql事务并发(脏读、不可重复读、幻读)
Apr 29 MySQL
MySQL sql模式设置引起的问题
May 15 MySQL
SQL语句中EXISTS的详细用法大全
Jun 25 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 选项及相关信息函数库
2006/12/04 PHP
ThinkPHP模板替换与系统常量及应用实例教程
2014/08/22 PHP
11个用于提高排版水平的基于jquery的文字效果插件
2012/09/14 Javascript
jQuery 插件仿百度搜索框智能提示(带Value值)
2013/01/22 Javascript
js中parseInt函数浅谈
2013/07/31 Javascript
js定时器怎么写?就是在特定时间执行某段程序
2013/10/11 Javascript
Node.js中HTTP模块与事件模块详解
2014/11/14 Javascript
js/jquery判断浏览器类型的方法小结
2015/05/12 Javascript
jquery append 动态添加的元素事件on 不起作用的解决方案
2015/07/30 Javascript
jQuery中each()、find()和filter()等节点操作方法详解(推荐)
2016/05/25 Javascript
总结十个Angular.js由浅入深的面试问题
2016/08/26 Javascript
JS 拦截全局ajax请求实例解析
2016/11/29 Javascript
js判断是否是手机页面
2017/03/17 Javascript
微信小程序与后台PHP交互的方法实例分析
2018/12/10 Javascript
JQuery+Bootstrap 自定义全屏Loading插件的示例demo
2019/07/03 jQuery
Vue使用lodop实现打印小结
2019/07/06 Javascript
解决vue项目刷新后,导航菜单高亮显示的位置不对问题
2019/11/01 Javascript
VUE实现自身整体组件销毁的示例代码
2020/01/13 Javascript
Vue循环中多个input绑定指定v-model实例
2020/08/31 Javascript
python enumerate函数的使用方法总结
2017/11/15 Python
django 解决manage.py migrate无效的问题
2018/05/27 Python
对python字典元素的添加与修改方法详解
2018/07/06 Python
Python实现定时自动关闭的tkinter窗口方法
2019/02/16 Python
python中setuptools的作用是什么
2020/06/19 Python
python如何提升爬虫效率
2020/09/27 Python
交通文明倡议书
2014/05/16 职场文书
投标保密承诺书
2014/05/19 职场文书
销售竞赛活动方案
2014/08/23 职场文书
优秀共产党员演讲稿
2014/09/04 职场文书
工作经常出错的检讨书
2014/09/13 职场文书
导游词之天津盘山
2019/11/01 职场文书
使用Redis实现秒杀功能的简单方法
2021/05/08 Redis
Python还能这么玩之用Python修改了班花的开机密码
2021/06/04 Python
SQL Server作业失败:无法确定所有者是否有服务器访问权限的解决方法
2021/06/30 SQL Server
Go语言安装并操作redis的go-redis库
2022/04/14 Golang
Nginx如何限制IP访问只允许特定域名访问
2022/07/23 Servers