巧用外部表避免大量的insert

昨天开发咨询我一个问题,希望我对下面的语句进行调优。
语句类似下面的形式
SELECT  subscriber_no 
FROM SUBSCRIBER S
WHERE SUBSCRIBER_TYPE = 'RM' 
            and CONV_RUN_NO in (20, 21, 22, 23) 
            and not EXISTS (SELECT s.AGREEMENT_NO 
                      FROM SERVICE_AGREEMENT S, CSM_OFFER C 
                      WHERE s.subscriber_no = S.AGREEMENT_NO 
                                   and SOC = C.SOC_CD 
                                   and SOC_TYPE ='P' 
                       ) 

这个语句的执行计划如下:
Plan hash value: 1111602366
---------------------------------------------------------------------------------------------------------
| Id  | Operation                        | Name                 | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |                      |     2 |    22 | 56648   (1)| 00:11:20 |
|*  1 |  FILTER                          |                      |       |       |            |          |
|*  2 |   TABLE ACCESS FULL              | SUBSCRIBER           |     2 |    22 | 56646   (1)| 00:11:20 |
|   3 |   NESTED LOOPS                   |                      |       |       |            |          |
|   4 |    NESTED LOOPS                  |                      |     2 |    44 |     2   (0)| 00:00:01 |
|*  5 |     INDEX RANGE SCAN             | SERVICE_AGREEMENT_PK |     8 |   104 |     1   (0)| 00:00:01 |
|*  6 |     INDEX RANGE SCAN             | CSM_OFFER_1IX        |     1 |       |     1   (0)| 00:00:01 |
|*  7 |    MAT_VIEW ACCESS BY INDEX ROWID| CSM_OFFER            |     1 |     9 |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter( NOT EXISTS (SELECT 0 FROM "PRDREFSSCA"."CSM_OFFER"
              "C","PRDAPPO"."SERVICE_AGREEMENT" "S" WHERE "S"."AGREEMENT_NO"=:B1 AND "SOC"="C"."SOC_CD" AND
              "SOC_TYPE"='P'))
   2 - filter(("CONV_RUN_NO"=20 OR "CONV_RUN_NO"=21 OR "CONV_RUN_NO"=22 OR "CONV_RUN_NO"=23) AND
              "SUBSCRIBER_TYPE"='RM')
   5 - access("S"."AGREEMENT_NO"=:B1)
   6 - access("SOC"="C"."SOC_CD")
   7 - filter("SOC_TYPE"='P')

从语句来看是真没有多少什么调优空间了。为了想尽快得到结果,我建议加了一个hint  /*+parallel(4)*/
我一再叮嘱他,这中语句最好在备份库中执行,因为备份中的数据是相对比较新的,他也同意。备份库当时负载很低,执行大概用了6分钟的样子。输出结果大约有70多万。
我以为这件事就这么过去了,结果今天早上他找到我说,希望把这70多万条记录存到一个一个临时用户下,需要关联查找更多的细节。
这种问题让人有些纠结。不过为了问题的解决,也不能完全怪他。
尝试一:
首先的一个思想就是想吐省事,在中午的时候尝试生成这些记录。
create table issue_sub_list nologging as 
SELECT  /*+parallel(4)*/ subscriber_no 
FROM SUBSCRIBER S
WHERE SUBSCRIBER_TYPE = 'RM' 
            and CONV_RUN_NO in (20, 21, 22, 23) 
            and not EXISTS (SELECT s.AGREEMENT_NO 
                      FROM SERVICE_AGREEMENT S, CSM_OFFER C 
                      WHERE s.subscriber_no = S.AGREEMENT_NO 
                                   and SOC = C.SOC_CD 
                                   and SOC_TYPE ='P' 
                       ) 
这条语句结果在生产上执行了将近10分钟还没有完成的意思,我查看执行计划的输出列情况,才全表扫描了30%,所以果断终止了这个操作。
这条路不建议使用,我看还有什么别的路可走。
尝试二:
一来就是对得到的数据进一步转换为insert语句。尝试在windows的ultraEdit下面编辑,因为这些id有差不多11M左右,使用文本编辑器没过一会,就提示缓存溢出,卡在那了。
尝试三:
看来文本编辑器还是很有使用限制的,linux环境下的命令方式要相对好很多。百兆上G的日志都没有问题。
我写了如下的脚本。
while read line
do
sqlplus -s n1/n1@xxx  
insert into issue_sublist values( $line);
EOF
done
执行的速度来看,单条语句是没有问题的,但是这条语句执行了10多分钟,还没有完,进步还是让人不够满意。
尝试四:
大家使用外部表来查看数据库的日志信息,这是一种方式,我们也可以应用到这个场景中。来看看外部表的情况。
创建了directory 
create directory EXT_DATAPUMP_DIR as '/oravl01/oracle/ext_datapump/DUMP';
grant read,write on directory EXT_DATAPUMP_DIR to n1;
然后使用语句创建了外部表。
create table issue_sublist_ext(text varchar2(1000)) organization external
(type oracle_loader
 default directory EXT_DATAPUMP_DIR
 location('sub_list.log')
);
Table created.
Elapsed: 00:00:00.01
SQL> select count(*)from issue_sublist_ext;
  COUNT(*)
----------
    769685
Elapsed: 00:00:00.25
SQL> drop table issue_sublist;
Table dropped.
Elapsed: 00:00:00.04
我们来创建一个表存储这些数据。
SQL> create table issue_sublist as select *from issue_sublist_ext;
Table created.
Elapsed: 00:00:00.64
SQL> !exp n1/n1@xxx  file=issue_sublist.dmp tables=issue_sublist buffer=910200
About to export specified tables via Conventional Path ...
. . exporting table                  ISSUE_SUBLIST     769951 rows exported
Export terminated successfully without warnings.

来看看dump的情况,生成的dump文件和文本的大小差不多大小。
TRUABP4 /oravl01/oracle/ext_datapump/DUMP > ll
total 24880
-rw-r--r-- 1 oracle dba 11545275 Mar 11 12:04 sub_list.log
-rw-r--r-- 1 oracle dba      357 Mar 11 12:05 ISSUE_SUBLIST_EXT_8860.log
-rw-r--r-- 1 oracle dba 13869056 Mar 11 12:07 issue_sublist.dmp

这个dump拷贝到生产中,直接imp速度是相当快的,一秒内搞定。
> time imp n1/n1 file=issue_sublist.dmp tables=issue_sublist buffer=910200 fromuser=n1 touser=cust_prsnl
. importing N1's objects into CUST_PRSNL
. . importing table                "ISSUE_SUBLIST"     769951 rows imported
Import terminated successfully without warnings.
real    0m1.277s
user    0m0.133s
sys     0m0.060s
通过这个例子我们可以看到,我们尝试尽可能减少生产的负载,使用外部表间接转换为dump,会减少大量的insert语句执行,而且效率也高。可以作为一种参考。

时间: 2024-10-23 20:16:20

巧用外部表避免大量的insert的相关文章

巧用外部表备份历史数据

在很多的系统中,随着时间的推移,都会沉淀大量的历史数据.一般数据量达到一定程度都会考虑使用分区表来处理.根据业务规则,可能有些历史数据隔一段时间就需要做清理了,这个时候历史数据就需要在分区级进行清理.在不同的系统,不同厂商都有不同的实现方案.但是从数据安全角度来说,都需要做备份工作,也是预防万一. 比如说我们存在一个表charge,就可能会有下面的几种分区规则, 一种是按照日期来分区,这样就能够很清楚的定位到哪些天的数据可以清理. 比如 6月9日的充值记录,分区表就为P_20150609,相关的

如何从Oracle 9i SQL*Loader中生成外部表定义

Oracle 9i中引入的外部表允许您将一个顺序文本文件映射到一个Oracle表定义中.一旦您定义了外部表,您就能应用SQL SELECT语句的所有功能--包括并行查询--来处理它. 外部表以SQL*Loader为基础,但定义它们的语法有所不同.如果您已经熟悉SQL*Loader,就有一条捷径:应用SQL*Loader本身生成一段为您建立外部表定义的脚本. 当您添加一个SQL*Loader命令行时,参数EXTERNAL_TABLE=GENERATE_ONLY,它将把系统使用的控制文件转译成一段S

阿里云RDS PostgreSQL OSS 外部表 - 并行写提速案例

标签 PostgreSQL , oss对象存储 , 阿里云RDS PG , 并行写 , dblink , 异步调用 , 异步任务监控 , OSS外部表 , 数据传输 背景 阿里云RDS PostgreSQL.HybridDB for PostgreSQL提供了一个非常强大的功能,OSS对象存储外部表. 阿里云的RDS PostgreSQL用户可以利用OSS存储冷数据(OSS外部表的形态呈现),实现冷热分离:也可以利用OSS作为数据的中转桥梁,打通其他云端业务,例如HDB FOR PostgreS

Greenplum insert的性能(单步\批量\copy) - 暨推荐使用gpfdist、阿里云oss外部表并行导入

标签 PostgreSQL , Greenplum , HybridDB for PostgreSQL , insert , copy , 外部表 , oss , gpfdist 背景 Greenplum是一款MPP数据库产品,优势是优良的OLAP性能,支持多节点并行计算,实现PB级数据量的实时分析. 除了分析能力,数据写入吞吐也是Greenplum的重要指标,Greenplum支持从master节点写入,也支持从segment节点并行写入. 从segment并行加载的话,性能是线性提升的,因为

Oracle中利用数据泵导出查询结果(二) 外部表的卸载功能

还是上一篇中的测试环境: SQL> CREATE TABLE T1 2  (ID NUMBER, NAME VARCHAR2(30)); Table created. SQL> INSERT INTO T1 2  SELECT ROWNUM, TNAME 3  FROM TAB; 66 rows created. SQL> CREATE TABLE T2 2  (ID NUMBER, NAME VARCHAR2(30)); Table created. SQL> INSERT IN

Oracle中如何管理外部表

外部表是表结构被存放在数据字典,而表数据被存放在OS文件的表.通过使用外部表,不仅可以在数据库中查询OS文件的数据,还可以使用INSERT方式将OS文件数据装载到数据库中,从而实现SQL*Loader所提供的功能.建立外部表后,可以查询外部表的数据,在外部表上执行连接查询,或对外部表的数据进行排序.需要注意,外部表上不能执行DML修改,也不能在外部表上建立索引. 1.建立外部表 --准备工作: [oracle@solaris10 ~]$mkdir /export/home/oracle/dat

OCP1z0-047 : 外部表――不能被DML和建索引

首先看官方文档上的解释: Managing External Tables Oracle Database allows you read-only access to data in external tables. External tables are definedas tables that do not reside in the database, and can be in any format forwhich an access driver is provided. By

PostgreSQL 10 + PostGIS + Sharding(pg_pathman) + MySQL(fdw外部表) on ECS 部署指南(适合新用户)

标签 PostgreSQL , Linux , CentOS 背景 新用户部署PostgreSQL以及空间数据库插件PostGIS的指南. 内网环境RPM打包(可选项) 如果是内网环境,可以将包全部下载到本地再用rpm安装. 安装epel和postgresql yum rpm两个包后再执行: 1.使用yum-utils的yumdownloader下载需要的安装包,包括依赖包. yum install -y yum-utils yumdownloader --resolve --destdir=/

Oracle中创建临时表、外部表和分区表实例

临时表 在Oracle中,临时表是"静态"的,它与普通的数据表一样只需要一次创建,其结构从创建到删除的整个期间都是有效的.相对于其他类型的表,临时表只有在用户实际向表中添加数据时,才会为其分配空间,并且分配的空间来自临时表空间.这就避免了与永久对象的数据争用存储空间. 创建临时表的语法如下: CREATE GLOBAL TEMPORARY TABLE table_name( column_name data_type,[column_name data_type,...] )ON CO