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实现简易版搜索引擎的示例代码
Aug 30 MySQL
mysql分表之后如何平滑上线详解
Nov 01 MySQL
MYSQL 表的全面总结
Nov 11 MySQL
MySQL 数据类型详情
Nov 11 MySQL
MySQL图形化管理工具Navicat安装步骤
Dec 04 MySQL
mysql5.6主从搭建以及不同步问题详解
Dec 04 MySQL
关于k8s环境部署mysql主从的问题
Mar 13 MySQL
MySQL学习之基础操作总结
Mar 19 MySQL
MySQL派生表联表查询实战过程
Mar 20 MySQL
mysql 8.0.27 绿色解压版安装教程及配置方法
Apr 20 MySQL
详解Mysql事务并发(脏读、不可重复读、幻读)
Apr 29 MySQL
SQL Server数据库的三种创建方法汇总
May 08 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 intval的测试代码发现问题
2008/07/27 PHP
PHP和Mysqlweb应用开发核心技术 第1部分 Php基础-1 开始了解php
2011/07/03 PHP
php生成局部唯一识别码LUID的代码
2012/10/06 PHP
深入理解:XML与对象的序列化与反序列化
2013/06/08 PHP
JS 用6N±1法求素数 实例教程
2009/10/20 Javascript
JS 实现导航栏悬停效果(续)
2013/09/24 Javascript
HTTP 304错误的详细讲解
2013/11/13 Javascript
jQuery动画与特效详解
2015/02/01 Javascript
JavaScript获取按钮所在form表单id的方法
2015/04/02 Javascript
javascript实现带下拉子菜单的导航菜单效果
2015/05/14 Javascript
javascript实现动态统计图开发实例
2015/11/21 Javascript
JS判断输入的字符串是否是数字的方法(正则表达式)
2016/11/29 Javascript
Bootstarp基本模版学习教程
2017/02/01 Javascript
js实现网页定位导航功能
2017/03/07 Javascript
Angular4学习之Angular CLI的安装与使用教程
2018/01/04 Javascript
使用Vue如何写一个双向数据绑定(面试常见)
2018/04/20 Javascript
使用express获取微信小程序二维码小记
2019/05/21 Javascript
layui之table checkbox初始化时选中对应选项的方法
2019/09/02 Javascript
[01:01:29]2018DOTA2亚洲邀请赛 4.4 淘汰赛 VP vs Liquid 第一场
2018/04/05 DOTA
Python中time模块与datetime模块在使用中的不同之处
2015/11/24 Python
python正则表达式之作业计算器
2016/03/18 Python
Python进程间通信Queue实例解析
2018/01/25 Python
Python输出\u编码将其转换成中文的实例
2018/12/15 Python
Django自定义模板过滤器和标签的实现方法
2019/08/21 Python
Django REST框架创建一个简单的Api实例讲解
2019/11/05 Python
Python获取浏览器窗口句柄过程解析
2020/07/25 Python
python如何实现递归转非递归
2021/02/25 Python
Yahoo-PHP面试题1
2016/07/20 面试题
翻译专业应届生求职信
2013/11/23 职场文书
财务会计应届生求职信
2013/11/24 职场文书
学校评语大全
2014/05/06 职场文书
我的梦想演讲稿500字
2014/08/21 职场文书
庆祝国庆节标语
2014/10/09 职场文书
2015年科研工作总结范文
2015/05/13 职场文书
Go语言-为什么返回值为接口类型,却返回结构体
2021/04/24 Golang
深入理解go缓存库freecache的使用
2022/02/15 Golang