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 重写查询语句的三种策略
May 10 MySQL
MySQL 分组查询的优化方法
May 12 MySQL
MySql新手入门的基本操作汇总
May 13 MySQL
MySql存储过程之逻辑判断和条件控制
May 26 MySQL
MySQL单表千万级数据处理的思路分享
Jun 05 MySQL
SQL实现LeetCode(175.联合两表)
Aug 04 MySQL
Mysql排序的特性详情
Nov 01 MySQL
Mysql排查分析慢sql之explain实战案例
Apr 19 MySQL
mysql使用FIND_IN_SET和group_concat两个方法查询上下级机构
Apr 20 MySQL
详解Mysq MVCC多版本的并发控制
Apr 29 MySQL
mysql 排序失效
May 20 MySQL
MySQL的表级锁,行级锁,排它锁和共享锁
Jul 15 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
Cannot modify header information错误解决方法
2008/10/08 PHP
简单说说PHP优化那些事(经验分享)
2014/11/27 PHP
php列出mysql表所有行和列的方法
2015/03/13 PHP
CodeIgniter生成静态页的方法
2016/05/17 PHP
PHP简单计算两个时间差的方法示例
2017/06/20 PHP
php简单读取.vcf格式文件的方法示例
2017/09/02 PHP
PHP实现链表的定义与反转功能示例
2018/06/09 PHP
通过JAVAScript实现页面自适应
2007/01/19 Javascript
提交按钮的name='submit'引起的js失效问题及原因
2015/02/25 Javascript
JavaScript中的条件判断语句使用详解
2015/06/03 Javascript
JQuery悬停控制图片轮播——代码简单
2015/08/05 Javascript
纯js实现瀑布流布局及ajax动态新增数据
2016/04/07 Javascript
Winform客户端向web地址传参接收参数的方法
2016/05/17 Javascript
Javascript实现跑马灯效果的简单实例
2016/05/31 Javascript
Jquery针对tr td的一些实用操作方法(必看篇)
2016/10/05 Javascript
vuex(vue状态管理)的特殊应用案例分享
2020/03/03 Javascript
[02:38]DOTA2英雄基础教程 噬魂鬼
2014/01/03 DOTA
python获取指定路径下所有指定后缀文件的方法
2015/05/26 Python
Python 使用os.remove删除文件夹时报错的解决方法
2017/01/13 Python
python利用拉链法实现字典方法示例
2017/03/25 Python
Python实现带参数与不带参数的多重继承示例
2018/01/30 Python
python素数筛选法浅析
2018/03/19 Python
Python实现Wordcloud生成词云图的示例
2020/03/30 Python
详解CSS3 rem(设置字体大小) 教程
2017/11/21 HTML / CSS
css3 伪类选择器快速复习小结
2019/09/10 HTML / CSS
HTML5 本地存储之如果没有数据库究竟会怎样
2013/04/25 HTML / CSS
HTML5操作WebSQL数据库的实例代码
2017/08/26 HTML / CSS
澳大利亚拥有最好的家具和家居用品在线目的地:Nestz
2019/02/23 全球购物
获奖的大学生创业计划书
2014/01/05 职场文书
安全检查验收制度
2014/01/12 职场文书
幼儿园爱国卫生月活动总结
2014/06/30 职场文书
法人授权委托书样本
2014/09/19 职场文书
2014年前台个人工作总结
2014/11/14 职场文书
二手房购房意向书
2015/05/09 职场文书
高二英语教学反思
2016/03/03 职场文书
volatile保证可见性及重排序方法
2022/08/05 Java/Android