Oracle 大表数据删除/清理方法小结
costdown遇到了大量数据清理的需求,整理下基本思路及方法
一、 哪些表是大表
1. 按空间大小
包含CLOB大小但不含索引大小,如果库很大,全库统计会比较耗时,可以增加并行或过滤条件,分批处理。
SELECT owner,object_name,SUM(MB) FROM(
select d.owner,d.table_name as object_name,sum(BYTES/1024/1024) MB from dba_extents a,dba_lobs d
where a.SEGMENT_NAME=d.SEGMENT_NAME and a.owner = D.owner and d.tablespace_name = 'ARM' and a.SEGMENT_TYPE like '%LOB%'
group by d.owner,d.table_name
union all
select owner,SEGMENT_NAME as object_name,sum(BYTES/1024/1024) MB from dba_extents
where tablespace_name = 'ARM' and SEGMENT_TYPE like '%TABLE%'
group by owner,SEGMENT_NAME) TMP
GROUP BY owner,object_name
ORDER BY SUM(MB) DESC;
2. 按行数
注意如果没收集过统计信息或者已经不准,会与实际有差异。
select TABLE_NAME,NUM_ROWS from dba_tables where OWNER='xxx' order by NUM_ROWS desc;
整理好后交给开发,确认各大表是否可清理,需保存多久数据。
二、 清理分类
目前大致遇到以下几种场景:
1. 可以drop
- 备份表、临时表、已无用的表
- 时间范围分区表:索引改为local索引后,按分区drop
2. 可以truncate
- 部分日志表
注意对于大表(例如上百G的表)应该分次执行drop或者truncate,推荐方法参考:海量数据表删除方案_ITPUB博客
3. 可以rename然后重建空表
- 可以暂停写入,不通过程序读取的表
- 按业务要求看是否将最近几个月数据插回新表,插回后删除备份表
- 或者不插回数据,几个月后删除备份表
4. 只能delete
- 这个属于绝大部分情况,后面单独讨论
5. 业务接口删除
- 业务关联性很强的表,不能简单按时间删除,需要由业务方编写删除程序或者使用标准接口,典型的案例就是ERP里的标准表。
三、 千万级以上大表如何delete
1. 直接删除的问题
- 耗时长,可能最终遇到ORA-1555报错
- 产生大事务,从库可能出现高延迟,且中断回滚耗时极长
- 可能阻塞业务其他DML操作
- undo表空间过度使用,可能影响到其他用户正常操作
下面虽然用到不同的删除语句,整体的思路都是一样的:分批删除并提交,将大事务化为小事务。另外,删除时注意归档及闪回日志产生量。
2. 按天删除数据
首先需要在对应时间字段(由业务方提供)加索引,一天的数据量大概在一两百万的话问题不大,再多可能就得拆得更细些。
- 时间字段为时间类型(date,timestamp)
DECLAREbegin_date date := to_date('2020-03-01','yyyy-mm-dd');
BEGINWHILE begin_date < to_date('2020-05-01','yyyy-mm-dd') loopDELETE FROM MYTAB WHERE CREATE_DT >= begin_date and CREATE_DT < begin_date+1;commit;begin_date := begin_date+1;end loop;
END;
/
- 时间字段为字符串类型(奇葩设计,但就是有)
DECLAREbegin_date date := to_date('20180901','yyyymmdd');
BEGIN WHILE begin_date < to_date('20181001','yyyymmdd') loopDELETE FROM APS.KIT_MATERIAL_RESULT_ALL WHERE KIT_DAY = to_char(begin_date,'yyyymmdd');commit;begin_date := begin_date+1;end loop;
END;
/
3. 游标对比rowid批量删除
下面两个方法来自:Oracle库Delete删除千万以上普通堆表数据的方法 - AlfredZhao - 博客园
有部分调整,避免全表取数及循环判断时间,原代码请参考原文
删除2020年3-4月的数据,每1万行提交一次
-- del_cur 游标名
-- tmp 要删除数据的表名
-- create_dt 时间字段名declarecursor del_cur is select a.rowid row_id from tmp where create_dt >= to_date('2020-03-01','yyyy-mm-dd') and create_dt < to_date('2020-05-01','yyyy-mm-dd') order by a.rowid;
beginfor v_cusor in del_cur loopdelete from tmp where rowid = v_cusor.row_id;if mod(del_cur%rowcount,10000)=0 thencommit;end if;end loop;commit;
end;
/
3. 直接按rowid删除
-- del_cur 游标名
-- tmp 要删除数据的表名declare
maxrows number default 10000;
delete_cnt number default 0;
begin
select count(1)/maxrows into delete_cnt from tmp where create_dt >= to_date('2020-03-01','yyyy-mm-dd') and create_dt < to_date('2020-05-01','yyyy-mm-dd');
for i in 1..TRUNC(delete_cnt)+1
loop
delete tmp where create_dt >= to_date('2020-03-01','yyyy-mm-dd') and create_dt < to_date('2020-05-01','yyyy-mm-dd') and rownum <= maxrows;
commit;
end loop;
end;
/
4. 如何看delete释放了多少空间
由于delete不降低高水位线,不能直接看表的大小,但也可以通过空闲块的计算来看。TOM大师脚本-show space 多个版本,谢谢大牛们_dnil27295的博客-CSDN博客
下面代码基于 exec show_space_1810 改了一下,原代码出现了两次Total Blocks和Total bytes,调整了名字,同时简化了输出的内容。输出如下,可以看到释放空间约228G。
-- 使用方法
-- set serveroutput on
-- exec show_space('TABLE_NAME','OWNER');
-- 各字段含义参考:https://docs.oracle.com/database/121/ARPLS/d_space.htm#ARPLS68113create or replace procedure show_space
( p_segname_1 in varchar2,
p_owner_1 in varchar2 default user,
p_type_1 in varchar2 default 'TABLE',
p_space in varchar2 default 'AUTO',
p_analyzed in varchar2 default 'Y'
)
as
p_segname varchar2(100);
p_type varchar2(10);
p_owner varchar2(30);
l_unformatted_blocks number;
l_unformatted_bytes number;
l_fs1_blocks number;
l_fs1_bytes number;
l_fs2_blocks number;
l_fs2_bytes number;
l_fs3_blocks number;
l_fs3_bytes number;
l_fs4_blocks number;
l_fs4_bytes number;
l_full_blocks number;
l_full_bytes number;
l_free_blks number;
l_total_blocks number;
l_total_bytes number;
l_unused_blocks number;
l_unused_bytes number;
l_LastUsedExtFileId number;
l_LastUsedExtBlockId number;
l_LAST_USED_BLOCK number;procedure p( p_label in varchar2, p_num in number )
is
begin
dbms_output.put_line( rpad(p_label,40,'.') || p_num );
end;begin
p_segname := upper(p_segname_1); -- rainy changed
p_owner := upper(p_owner_1);
p_type := p_type_1;if (p_type_1 = 'i' or p_type_1 = 'I') then --rainy changed
p_type := 'INDEX';
end if;if (p_type_1 = 't' or p_type_1 = 'T') then --rainy changed
p_type := 'TABLE';
end if;if (p_type_1 = 'c' or p_type_1 = 'C') then --rainy changed
p_type := 'CLUSTER';
end if;dbms_space.unused_space
( segment_owner => p_owner,
segment_name => p_segname,
segment_type => p_type,
total_blocks => l_total_blocks,
total_bytes => l_total_bytes,
unused_blocks => l_unused_blocks,
unused_bytes => l_unused_bytes,
LAST_USED_EXTENT_FILE_ID => l_LastUsedExtFileId,
LAST_USED_EXTENT_BLOCK_ID => l_LastUsedExtBlockId,
LAST_USED_BLOCK => l_LAST_USED_BLOCK
);if p_space = 'MANUAL' or (p_space <> 'auto' and p_space <> 'AUTO') then
dbms_space.free_blocks
( segment_owner => p_owner,
segment_name => p_segname,
segment_type => p_type,
freelist_group_id => 0,
free_blks => l_free_blks );
p( 'Free Blocks', l_free_blks );
end if;/*IF the segment is analyzed */
if p_analyzed = 'Y' then
dbms_space.space_usage(segment_owner => p_owner ,
segment_name => p_segname ,
segment_type => p_type ,
unformatted_blocks => l_unformatted_blocks ,
unformatted_bytes => l_unformatted_bytes,
fs1_blocks => l_fs1_blocks,
fs1_bytes => l_fs1_bytes ,
fs2_blocks => l_fs2_blocks,
fs2_bytes => l_fs2_bytes,
fs3_blocks => l_fs3_blocks ,
fs3_bytes => l_fs3_bytes,
fs4_blocks => l_fs4_blocks,
fs4_bytes => l_fs4_bytes,
full_blocks => l_full_blocks,
full_bytes => l_full_bytes);
-- dbms_output.put_line(rpad(' ',50,'*'));
-- dbms_output.put_line('The segment is analyzed');
-- p( 'Unformatted Blocks', l_unformatted_blocks );
-- p( 'Unformatted Bytes', l_unformatted_bytes );
p( 'Unused Blocks', l_unused_blocks );
p( 'Unused Bytes', l_unused_bytes );
p( '0% -- 25% free space blocks', l_fs1_blocks);
p( '0% -- 25% free space bytes', l_fs1_bytes);
p( '25% -- 50% free space blocks', l_fs2_blocks);
p( '25% -- 50% free space bytes', l_fs2_bytes);
p( '50% -- 75% free space blocks', l_fs3_blocks);
p( '50% -- 75% free space bytes', l_fs3_bytes);
p( '75% -- 100% free space blocks', l_fs4_blocks);
p( '75% -- 100% free space bytes', l_fs4_bytes);
p( 'Full Blocks', l_full_blocks);
p( 'Full bytes', l_full_bytes);
p( 'Total Blocks', l_total_blocks );
p( 'Total Bytes', l_total_bytes );
-- p( 'Last Used Ext FileId', l_LastUsedExtFileId );
-- p( 'Last Used Ext BlockId', l_LastUsedExtBlockId );
-- p( 'Last Used Block', l_LAST_USED_BLOCK );
end if;
end show_space;
/
四、 分区化改造
对于需要定期清理的表,建议在线重定义为分区表,提高删除效率。这又是一个很长的话题了:Oracle 利用在线重定义进行分区表转换_Hehuyi_In的博客-CSDN博客_oracle分区表转换
参考
海量数据表删除方案_ITPUB博客
Oracle库Delete删除千万以上普通堆表数据的方法 - AlfredZhao - 博客园
TOM大师脚本-show space 多个版本,谢谢大牛们_dnil27295的博客-CSDN博客
Oracle 利用在线重定义进行分区表转换_Hehuyi_In的博客-CSDN博客_oracle分区表转换
SQL Server 快速删除/归档数据方法小结_Hehuyi_In的博客-CSDN博客_sqlserver 快速删除数据
Oracle 大表数据删除/清理方法小结相关推荐
- oracle 大表删除数据后,回收空间的问题。
在oracle中由于表结构设计不合理或者需要清楚老数据的时候,经常需要对大表数据进行清理. 一般有一下几种方法: 1. 删除大部分数据,留下小部分数据.我们可以把需要保留的数据转移到别的表,然后再把大 ...
- Oracle数据库重复数据删除的三种情况
在对数据库进行操作过程中我们可能会遇到这种情况,表中的数据可能重复出现,使我们对数据库的操作过程中带来很多的不便,那么怎么删除这些重复没有用的数据呢? 重复数据删除技术可以提供更大的备份容量,实现更长 ...
- ORACLE 大表使用 rowid 切片备份到历史表
ORACLE 大表使用 rowid 切片备份到历史表 概述与要求 思路 备份与清理表的信息 方案 操作前准备 切分字段 备份步骤 清理步骤 脚本 概述与要求 项目需求将几个5000万行大表先备份到历史 ...
- oracle创建索引表,Oracle 大表创建索引
Oracle 大表创建索引 祖仙教小凡仙 海鲨数据库架构师 有个2亿记录的表,发现需要添加一个联合索引,结果就采用普通的create index index_name on tablename (en ...
- MySQL-在线处理大表数据 在线修改大表的表结构
文章目录 生猛干货 官方文档 概述 示例 大表数据的分批处理 修改大表的表结构 方案一 : 从表修改,主从切换 方案二: pt-online-schema-change 搞定MySQL 生猛干货 带你 ...
- oracle 建表字段设置,Oracle创建表、删除表、修改表(添加字段、修改字段、删除字段)语句总结...
关于Oracle创建表.删除表.修改表(添加字段.修改字段.删除字段)语句的简短总结. Oracle创建表: create table 表名 ( 字段名1 字段类型 默认值 是否为空 , 字段名2 字 ...
- Oracle大表清理truncate .. reuse storage
Oracle大表清理truncate .. reuse storage deallocate_unused_clause Purpose Use the deallocate_unused_claus ...
- Mysql大表数据清理
假设大表表名:t_log delete操作会记录mysql日志,大数据量的清理会耗费大量时间,所以一般得用其他方式. 1.如果大表数据对我们来说不重要,完全可以直接清理,那么可以用truncate t ...
- MySQL快速清空大表数据(truncate table table_name;)
MySQL快速清空大表数据 项目初次上线,进行性能测试造的数据量巨大,都是些不可用数据,但又有一些是必须保留的,很多时候需要进行系统性的清理数据或者是,将有用的数据筛选出来之后再插入到表中!保留表结构 ...
- Pl/sql 如何将oracle的表数据导出成excel文件?
oracle将表数据导出成excel文件的方法 1)在SQL窗体上,查询需要导出的数据 --查询数据条件--select MID,CODE,NAME from Dxc_Goods_Cate where ...
最新文章
- 初学者学python好还是c-C 和 Python语言先学哪个好?
- 文巾解题 面试题 01.06. 字符串压缩
- 编程实现将rdd转换为dataframe:源文件内容如下(_大数据 什么是RDD?可以干什么?为什么要有RDD?...
- LeetCode 142——环形链表 II
- 计算机网络技术发源于什么,计算机网络基础试题和答案
- linux 433发送驱动
- 96秒100亿!如何抗住双11高并发流量?
- 学校为什么要单位接收函_学校为什么要做校园文化建设?
- 三星 9810 android 9,【极光ROM】-【三星NOTE9 N960X-9810】-【V17.0 Android-Q-TB9】
- Nginx学习笔记(Docker版)-2
- Python中常用的四个小技巧总结
- 办理登机的英语词组手机键盘_如何使用智能手机使登机轻而易举
- python tkinter编写界面,使用win32com操作excel获取数据生成截图后,wxpy登录微信,给租客发送房租(二)
- Nirvana Chain 为应用而生技术交流酒会在成都成功举办
- 【c语言】一个球从100米高的自由落下,每次落地后反跳回原高度的一半,再落下,再反弹。求第 10次落地时,共经过多少米,第10次反弹多高。
- 简单快速的“0x800c0006 安装失败 .NET framework 等旧版本软件安装失败”的解决方法
- 宇宙即计算~一种新科学:斯蒂芬·沃尔夫勒姆
- Java微信公众号配置验证Token
- 我发现了 tinder 的留存密码
- Blog地址更新:http://www.javabar.com.cn
热门文章
- android dfu升级
- Eclipse连接Github出现not authorized
- Mint-ui MessageBox.confirm 确定和取消事件
- 如何让房间每一个角落都充满 Wi-Fi?
- 树莓派正式开售CM4以及CM4 Lite,32个不同配置,最低25美元起售!
- java: Compilation failed: internal java compiler error
- 神经网络压缩 剪枝 量化 嵌入式计算优化NCNN mobilenet squeezenet shufflenet
- edge 临时文件夹 位置_如何更改Microsoft Edge的下载文件夹的位置
- 个性化定制软件安装包流程指导(按照步骤即可定制化安装流程)
- 笔记本处理器排名_上半年最受欢迎处理器TOP10榜单:AMD终进榜,9代酷睿无缘前10...