﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-The blog of Astro Qi-随笔分类-Oracle</title><link>http://www.blogjava.net/AstroQi/category/37307.html</link><description>I'm Astro Qi. If call me, please send email to closoastroqi@126.com</description><language>zh-cn</language><lastBuildDate>Tue, 22 Dec 2009 22:51:45 GMT</lastBuildDate><pubDate>Tue, 22 Dec 2009 22:51:45 GMT</pubDate><ttl>60</ttl><item><title>Oracle分析函数</title><link>http://www.blogjava.net/AstroQi/archive/2009/01/20/252022.html</link><dc:creator>Astro.Qi</dc:creator><author>Astro.Qi</author><pubDate>Tue, 20 Jan 2009 03:33:00 GMT</pubDate><guid>http://www.blogjava.net/AstroQi/archive/2009/01/20/252022.html</guid><wfw:comment>http://www.blogjava.net/AstroQi/comments/252022.html</wfw:comment><comments>http://www.blogjava.net/AstroQi/archive/2009/01/20/252022.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/AstroQi/comments/commentRss/252022.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/AstroQi/services/trackbacks/252022.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 分析函数&nbsp; 它是Oracle分析函数专门针对类似于"经营总额"、"找出一组中的百分之多少" 或"计算排名前几位"等问题设计的。分析函数运行效率高，使用方便。&nbsp; 分析函数是基于一组行来计算的。这不同于聚集函数且广泛应用于OLAP环境中。&nbsp; Oracle从8.1.6开始提供分析函数，分析函数用于计算基于组的某种聚合值，它和聚合函数的不同之处是对于每个组...&nbsp;&nbsp;<a href='http://www.blogjava.net/AstroQi/archive/2009/01/20/252022.html'>阅读全文</a><img src ="http://www.blogjava.net/AstroQi/aggbug/252022.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/AstroQi/" target="_blank">Astro.Qi</a> 2009-01-20 11:33 <a href="http://www.blogjava.net/AstroQi/archive/2009/01/20/252022.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ORACLE的索引和约束详解</title><link>http://www.blogjava.net/AstroQi/archive/2009/01/20/252020.html</link><dc:creator>Astro.Qi</dc:creator><author>Astro.Qi</author><pubDate>Tue, 20 Jan 2009 03:23:00 GMT</pubDate><guid>http://www.blogjava.net/AstroQi/archive/2009/01/20/252020.html</guid><wfw:comment>http://www.blogjava.net/AstroQi/comments/252020.html</wfw:comment><comments>http://www.blogjava.net/AstroQi/archive/2009/01/20/252020.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/AstroQi/comments/commentRss/252020.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/AstroQi/services/trackbacks/252020.html</trackback:ping><description><![CDATA[<p><a href="javascript:;" onclick="javascript:tagshow(event, 'Oracle');" target="_self"><u><strong>Oracle</strong></u></a>的约束<br />
<br />
* 如果某个约束只作用于单独的字段，即可以在字段级定义约束，也可以在表级定义约束，但如果某个约束作用于多个字段，<br />
必须在表级定义约束<br />
* 在定义约束时可以通过CONSTRAINT关键字为约束命名，如果没有指定，ORACLE将自动为约束建立默认的名称<br />
<br />
定义primary key约束(单个字段）<br />
create table employees (empno number(5) primary key,...)<br />
<br />
指定约束名<br />
create table employees (empno number(5) constraint emp_pk primary key,...)<br />
<br />
定义primary key约束(多个字段,在表级定义约束）<br />
create table employees<br />
(empno number(5),<br />
deptno number(3) not null,<br />
constraint emp_pk primary key(empno,deptno)<br />
using index tablespace indx<br />
storage (initial 64K<br />
next 64K<br />
)<br />
)<br />
<br />
ORACLE自动会为具有PRIMARY KEY约束的字段(主码字段)建立一个唯一索引和一个NOT NULL约束,定义PRIMARY KEY约束时可以为它的索引<br />
指定存储位置和存储参数<br />
<br />
alter table employees add primary key (empno)<br />
alter table employees add constraint emp_pk primary key (empno)<br />
alter table employees add constraint emp_pk primary key (empno,deptno)<br />
<br />
not null约束(只能在字段级定义NOT NULL约束,在同一个表中可以定义多个NOT NULL约束)<br />
alter table employees modify deptno not null/null<br />
<br />
unique约束<br />
create table employees<br />
( empno number(5),<br />
ename varchar2(15),<br />
phone varchar2(15),<br />
email varchar2(30) unique,<br />
deptno number(3) not null,<br />
constraint emp_ename_phone_uk unique (ename,phone)<br />
)<br />
<br />
alter table employees<br />
add constraint emp_uk unique(ename,phone)<br />
using index tablespace indx<br />
<br />
定义了UNIQUE约束的字段中不能包含重复值，可以为一个或多个字段定义UNIQUE约束,因此，UNIQUE即可以在字段级也可以在表级定义，<br />
在UNIQUED约束的字段上可以包含空值.<br />
<br />
foreign key约束<br />
<br />
* 定义为FOREIGN KEY约束的字段中只能包含相应的其它表中的引用码字段的值或者NULL值<br />
* 可以为一个或者多个字段的组合定义FOREIGN KEY约束<br />
* 定义了FOREIGN KEY约束的外部码字段和相应的引用码字段可以存在于同一个表中，这种情况称为"自引用"<br />
* 对同一个字段可以同时定义FOREIGN KEY约束和NOT NULL约束<br />
<br />
定义了FOREIGN KEY约束的字段称为"外部码字段",被FORGIEN KEY约束引用的字段称为"引用码字段",引用码必须是主码或唯一码,包含外部码的表称为子表，<br />
包含引用码的表称为父表.<br />
<br />
A:<br />
create table employees<br />
(.....,<br />
deptno number(3) NOT NULL,<br />
constraint emp_deptno_fk foreign key (deptno)<br />
references dept (deptno)<br />
)<br />
<br />
如果子表中的外部码与主表中的引用码具有相同的名称，可以写成:<br />
B:<br />
create table employees<br />
(.....,<br />
deptno number(3) NOT NULL<br />
constraint emp_deptno_fk references dept<br />
)</p>
<p>注意：<br />
上面的例子(B)中not null后面没有加逗号,因为这一句的contraint是跟在那一列deptno后面的，属于列定义，所以都无需指明列。而A例中的是表定义，需要指明那一列，所以要加逗号，不能在列后面定义，还可以写成：<br />
<br />
create table employees<br />
(empno char(4),<br />
deptno char(2) not null constraint emp_deptno_fk references dept,<br />
ename varchar2(10)<br />
)<br />
表定义contraint的只能写在最后，再看两个例子：<br />
<br />
create table employees<br />
(empno number(5),<br />
ename varchar2(10),<br />
deptno char(2) not null constraint emp_deptno_fk references dept,<br />
constraint emp_pk primary key(empno,ename)<br />
)<br />
<br />
create table employees<br />
( empno number(5),<br />
ename varchar2(15),<br />
phone varchar2(15),<br />
email varchar2(30) unique,<br />
deptno number(3) not null,<br />
constraint emp_pk primary key(empno,ename),<br />
constraint emp_phone_uk unique (phone)<br />
)<br />
<br />
添加foreign key约束(多字段／表级）<br />
alter table employees<br />
add constraint emp_jobs_fk foreign key (job,deptno)<br />
references jobs (jobid,deptno)<br />
on delete cascade<br />
<br />
更改foreign key约束定义的引用行为(delete cascade/delete set null/delete no action),默认是delete on action<br />
<br />
引用行为(当主表中一条记录被删除时，确定如何处理字表中的外部码字段）：<br />
delete cascade : 删除子表中所有的相关记录<br />
delete set null :　将所有相关记录的外部码字段值设置为NULL<br />
delete no action: 不做任何操作<br />
<br />
先删除原来的外键约束,再添加约束<br />
ALTER TABLE employees DROP CONSTRAINT emp_deptno_fk;<br />
ALTER TABLE employees ADD CONSTRAINT emp_deptno_fk FOREIGN KEY(deptno) REFERENCES dept(deptno) ON DELETE CASCADE;<br />
<br />
check约束<br />
* 在CHECK约束的表达式中必须引用到表中的一个或多个字段，并且表达式的计算结果必须是一个布尔值<br />
* 可以在表级或字段级定义<br />
* 对同一个字段可以定义多个CHECK约束，同时也可以定义NOT NULL约束<br />
<br />
create table employees<br />
(sal number(7,2)<br />
constraint emp_sal_ck1 check (sal &gt; 0)<br />
)<br />
<br />
alter table employees<br />
add constraint emp_sal_ck2 check (sal &lt; 20000)<br />
<br />
删除约束<br />
<br />
alter table dept drop unique (dname,loc) --指定约束的定义内容<br />
alter table dept drop constraint dept_dname_loc_uk --指定约束名<br />
<br />
删除约束时，默认将同时删除约束所对应的索引，如果要保留索引，用KEEP INDEX关键字<br />
alter table employees drop primary key keep index<br />
<br />
如果要删除的约束正在被其它约束引用，通过ALTER TABLE..DROP语句中指定CASCADE关键字能够同时删除引用它的约束<br />
<br />
利用下面的语句在删除DEPT表中的PRIMARY KEY约束时，同时将删除其它表中引用这个约束的FOREIGN KEY约束:<br />
alter table dept drop primary key cascade<br />
<br />
禁用/激活约束(禁用/激活约束会引起删除和重建索引的操作)<br />
alter table employees disable/enable unique email<br />
alter table employees disable/enable constraint emp_ename_pk<br />
alter tabel employees modify constraint emp_pk disable/enable<br />
alter tabel employees modify constraint emp_ename_phone_uk disable/enable&nbsp;&nbsp;</p>
<p>如果有FOREIGN KEY约束正在引用UNIQUE或PRIMARY KEY约束，则无法禁用这些UNIQUE或PRIMARY KEY约束，<br />
这时可以先禁用FOREIGN KEY约束，然后再禁用UNIQUE或PRIMARY KEY约束；或者可以在ALTER TABLE...DISABLE<br />
语句中指定CASCADE关键字，这样将在禁用UNIQUE或PRIMARY KEY约束的同时禁用那些引用它们的FOREIGN KEY约束，如：<br />
alter table employees disable primary key cascade<br />
<br />
约束数据字典<br />
all_constraints/dba_constraints/user_constraints 约束的基本信息，包括约束的名称，类型，状态<br />
(约束类型：C(CHECK约束),P(主码约束),R(外部码约束),U(唯一码约束))<br />
all_cons_columns/dba/user 约束对应的字段信息</p>
<br />
<strong>Oracle的索引<br />
</strong>&nbsp;&nbsp;&nbsp; 索引和对应的表应该位于不同的表空间中,oracle能够并行读取位于不同硬盘上的数据，可以避免产生I/O冲突<br />
B树索引：在B树的叶节点中存储索引字段的值与ROWID。<br />
唯一索引和不唯一索引都只是针对B树索引而言.<br />
Oracle最多允许包含32个字段的复合索引<br />
<br />
索引创建策略<br />
1.导入数据后再创建索引<br />
2.不需要为很小的表创建索引<br />
3.对于取值范围很小的字段（比如性别字段）应当建立位图索引<br />
4.限制表中的索引的数目<br />
5.为索引设置合适的PCTFREE值<br />
6.存储索引的表空间最好单独设定<br />
<br />
创建不唯一索引<br />
create index emp_ename on employees(ename)<br />
tablespace users<br />
storage(......)<br />
pctfree 0;<br />
<br />
创建唯一索引<br />
create unique index emp_email on employees(email)<br />
tablespace users;<br />
<br />
创建位图索引<br />
create bitmap index emp_sex on employees(sex)<br />
tablespace users;<br />
<br />
创建反序索引<br />
create unique index order_reinx on orders(order_num,order_date)<br />
tablespace users<br />
reverse;<br />
<br />
创建函数索引(函数索引即可以是普通的B树索引，也可以是位图索引)<br />
create index emp_substr_empno<br />
on employees(substr(empno,1,2))<br />
tablespace users;<br />
<br />
修改索引存储参数(与表类似，INITIAL和MINEXTENTS参数在索引建立以后不能再改变)<br />
alter index emp_ename storage(pctincrease 50);<br />
<br />
由于定义约束时由oracle自动建立的索引通常是不知道名称的，对这类索引的修改经常是利用alter table ..using index语句进行的,而不是alter index语句<br />
<br />
利用下面的语句将employees表中primary key约束对应的索引的PCTFREE参数修改为5<br />
alter table employees enable primary key using index pctfree 5;<br />
<br />
清理索引碎片<br />
1.合并索引(只是简单的将B树叶结点中的存储碎片合并在一起，并不会改变索引的物理组织结构）<br />
alter index emp_pk coalesce;<br />
<br />
2.重建索引(不仅能够消除存储碎片,还可以改变索引的全部存储参数设置，并且可以将索引移动到其它的表空间中,重建索引<br />
实际上就是再指定的表空间中重新建立一个新的索引,然后删除原来的索引)<br />
alter index emp_pk rebuild;<br />
<br />
删除索引<br />
drop index emp_ename;<br />
<br />
如果索引中包含损坏的数据块，或者包含过多的存储碎片，需要首先删除这个索引，然后再重建它.<br />
如果索引是在创建约束时由oracle自动产生的,可以通过禁用约束或删除约束的方法来删除对应的索引.<br />
在删除一个表时,oracle会自动删除所有与该表相关的索引.<br />
<br />
索引数据字典<br />
all_indexes/dba_indexes/user_indexes 索引的基本信息<br />
all_ind_columns/dba_ind_columns/user_ind_columns 索引对应的字段信息<br />
<br />
<img src ="http://www.blogjava.net/AstroQi/aggbug/252020.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/AstroQi/" target="_blank">Astro.Qi</a> 2009-01-20 11:23 <a href="http://www.blogjava.net/AstroQi/archive/2009/01/20/252020.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Oracle热备份</title><link>http://www.blogjava.net/AstroQi/archive/2009/01/20/252019.html</link><dc:creator>Astro.Qi</dc:creator><author>Astro.Qi</author><pubDate>Tue, 20 Jan 2009 03:21:00 GMT</pubDate><guid>http://www.blogjava.net/AstroQi/archive/2009/01/20/252019.html</guid><wfw:comment>http://www.blogjava.net/AstroQi/comments/252019.html</wfw:comment><comments>http://www.blogjava.net/AstroQi/archive/2009/01/20/252019.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/AstroQi/comments/commentRss/252019.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/AstroQi/services/trackbacks/252019.html</trackback:ping><description><![CDATA[<a href="javascript:;" onclick="javascript:tagshow(event, 'Oracle');" target="_self"><u><strong>Oracle</strong></u></a> 版本：9i、10g<br />
1、&nbsp; &nbsp; &nbsp; &nbsp; 进行热<a href="javascript:;" onclick="javascript:tagshow(event, '%B1%B8%B7%DD');" target="_self"><u><strong>备份</strong></u></a>的要求？<br />
热备份是一种<a href="javascript:;" onclick="javascript:tagshow(event, '%CA%FD%BE%DD%BF%E2');" target="_self"><u><strong>数据库</strong></u></a>的处于打开时的物理备份，因为数据库在不断发生改变，因此备份的文件必然是不一致的，这就要求数据库必须处于归档日志模式。<br />
<br />
2、&nbsp; &nbsp; &nbsp; &nbsp; 热备份的一般流程？<br />
&nbsp; &nbsp; &nbsp; &nbsp; 查看数据库是否处于归档日志模式，若不是将数据库置为归档日志模式。<br />
Rem check the archived mode<br />
Archive log list;<br />
Rem if not archivelog mode issue the following statements<br />
Shutdown immediate;<br />
Startup mount;<br />
Alter database archivelog;<br />
Alter database open;<br />
&nbsp; &nbsp; &nbsp; &nbsp; 查看当前的归档日志情况确认已归档<br />
Archive log list;<br />
&nbsp; &nbsp; &nbsp; &nbsp; 将表空间置为热备份模式(以users 表空间为例)<br />
Alter tablespace users begin backup;<br />
&nbsp; &nbsp; &nbsp; &nbsp; 物理拷贝数据文件<br />
Rem for <a href="javascript:;" onclick="javascript:tagshow(event, 'windows');" target="_self"><u><strong>windows</strong></u></a><br />
Sql&gt; host copy c:\oracle\ora92\user01.dbf d:\oracle\hotbackup;<br />
Rem for linux/unix<br />
Sql&gt;host cp&nbsp;&nbsp;/oracle/ora92/user01.dbf&nbsp;&nbsp;/oracle/oracle92/user01.dbf<br />
&nbsp; &nbsp; &nbsp; &nbsp; 结束表空间的热备份模式<br />
Alter tablespace users end backup;<br />
&nbsp; &nbsp; &nbsp; &nbsp; 相同方法备份其他表空间(read only 和 offline 表空间不能置为热备份模式)<br />
&nbsp; &nbsp; &nbsp; &nbsp; 备份二进制的控制文件<br />
Alter system backup control file to &#8216;d:\oracle\hotback\controlfile.ctl&#8217; reuse;<br />
Rem reuse 表示当文件存在时覆盖。<br />
<br />
3、&nbsp; &nbsp; &nbsp; &nbsp; 开始热备份时发生什么？<br />
&nbsp; &nbsp; &nbsp; &nbsp; 执行一个checkpoint 将所有dirty data 写入文件（保证热备份开始前文件是一致的）<br />
&nbsp; &nbsp; &nbsp; &nbsp; 锁定文件头的checkpoint scn，但是数据库的改变可以正常进行。<br />
&nbsp; &nbsp; &nbsp; &nbsp; 在alert文件中增加一条记录<br />
<br />
4、&nbsp; &nbsp; &nbsp; &nbsp; 热备份时相关SCN 如何处理？<br />
在
开始进行热备份时，数据文件头的checkpoint scn 和控制文件中该文件的scn
lock住，但是数据文件的dml操作可以正常进行，也就意味着block 的scn 是正常增加的，数据文件并没有锁定。为什么要Lock
文件的checkpoint scn（实际上对应了恢复时的rba） 主要是为了标示进行恢复操作时需要的redo log，
这样就可以保证数据文件内所有的块都能得到恢复，因为在进行数据文件拷贝时无法保证数据文件头是最先拷贝完成的。如果先拷贝的是数据文件的其他部分然后才
去拷贝的数据文件头，这时候很有可能数据文件头的checkpoint scn已经改变了而恢复时应用redo
log的范围是由数据文件头的rba决定的。<br />
当热备份结束时，在备份结束的时候，oracle会把数据文件头和control文件中关于这个数据文件的ＳＣＮ号更新为数据库的ＳＣＮ号。<br />
<br />
5、&nbsp; &nbsp; &nbsp; &nbsp; 为什么热备份时产生的redo大量增加？<br />
首
先说下造成大量增加的直接原因吧，因为数据文件在进行热备份时，该数据文件中的处于data
buffer中数据块在首次发生改变时oracle会把整块的block&nbsp;&nbsp;image 写入redo
log。注意该处首次改变是对处于buffer中的数据块而言的，而不是文件中的block，例如对于datafile1 的第20个块：<br />
File ---data buffer 从文件中读入db buffer<br />
Data buffer-----change&nbsp;&nbsp;产生block image to redo log<br />
Data buffer ------change&nbsp;&nbsp;不产生blcok image 而是正常的change vector<br />
Data buffer ----file&nbsp;&nbsp;从db buffer 写入文件<br />
File ----data buffer&nbsp;&nbsp;又从文件中读入db buffer<br />
Data buffer-----change&nbsp; &nbsp;再次产生block image to redo log<br />
Data buffer ------change&nbsp;&nbsp;不产生blcok image 而是正常的change vector<br />
<br />
之
所以要存储block image主要是为了处理由于oracle 的block size 和操作系统的block size
不一致而可能造成的split block（指数据块不一致）。热备份中我们用的是操作系统命令copy or cp
来拷贝数据文件，这些命令操作的块大小都是由操作系统决定的而操作系统块和数据库块大小一般不相同。如果当数据库块在改变的同时在进行拷贝，就很有可能造
成拷贝的数据块不一致（其中一部份改变前拷贝的一部分是改变后拷贝的）。Oracle 在恢复时将split block 当成是currption
block，这时就需要redo log中的block image覆盖现有块来进行恢复<br />
<br />
6、&nbsp; &nbsp; &nbsp; &nbsp; Rman备份为什么不用锁文件头scn，不增加redo？<br />
锁文件头scn只是为了可以正确标示恢复时的rba，热备份方式中这个rba需要从备份的数据文件本身获取，而rman方式将其写入了controlfile 或catalog,所以不用锁文件头。<br />
Rman 在备份时需要使用两个缓冲区一个输入缓冲区，一个输出缓冲区。数据块在从输入缓冲区传送到输出缓冲区时会进行一致性校验，不一致时会重新读取直到一致。所以rman备份的文件数据块级都是一致的，不需要block image。<br />
（两个缓冲区默认在pga中如果设置了io_slave 会在larger pool 中。）<br />
<br />
7、&nbsp; &nbsp; &nbsp; &nbsp; 热备份过程中发生crash如何恢复？(以users 表空间 user01.dbf&nbsp;&nbsp;file# 9 为例)<br />
如果在热备份的过程中发生crash在startup时会报：ORA-01113文件9需要恢复。<br />
有两种方式可以解决这个问题：<br />
1、&nbsp; &nbsp; &nbsp; &nbsp; alter tablespace users end backup;<br />
2、&nbsp; &nbsp; &nbsp; &nbsp; recover datafile 9;<br />
因为这时报的错误和restore了一个文件后需要恢复时一样的，所以在确定是没有end backup时用第一种方法否则应该用第二种。<br />
<br />
8、&nbsp; &nbsp; &nbsp; &nbsp; 热备份恢复相关视图？<br />
v$recover_file:需要恢复的文件<br />
v$recover_log：需要应用的归档日志<br />
x$kcvfh ：可以查看各个文件的rba<br />
<br />
9、&nbsp; &nbsp; &nbsp; &nbsp; 热备份相关脚本？（备份脚本）<br />
set linesize 800 verify off pagesize 0 feedback off<br />
define dir='c:\oracle\admin\hotback\2008-10-26'<br />
define log='c:\oracle\admin\hotback\2008-10-26\bklog.txt'<br />
define fil='c:\oracle\admin\sqlsheet\bkdatafile.sql'<br />
set serveroutput on<br />
spool &amp;fil<br />
prompt spool &amp;log<br />
prompt archive log list;;<br />
prompt alter system switch logfile;;<br />
declare<br />
cursor c_tbs is select tablespace_name tbsname from dba_tablespaces where status='ONLINE';<br />
cursor c_datafile(tn varchar2) is select file_name from dba_data_files where tablespace_name=tn;<br />
begin<br />
for row_tbs in c_tbs loop<br />
dbms_output.put_line('alter tablespace '||row_tbs.tbsname||' begin backup;');<br />
for row_file in c_datafile(row_tbs.tbsname) loop<br />
&nbsp; &nbsp;dbms_output.put_line('host copy '||row_file.file_name||' &amp;dir;');<br />
end loop;<br />
dbms_output.put_line('alter tablespace '||row_tbs.tbsname||' end backup;');<br />
end loop;<br />
end;<br />
/<br />
prompt alter system switch logfile;;<br />
prompt alter database backup controlfile to '&amp;dir\controfile.ctl';;<br />
prompt archive log list;;<br />
prompt spool off;;<br />
spool off;<br />
@&amp;fil<br />
<br />
10、&nbsp; &nbsp; &nbsp; &nbsp; 当归档日志文件不在默认位置时如何处理？<br />
我们在进行热备份恢复时需要的归档日志文件很有可能从默认的归档位置移走了，在恢复时要一个一个指定归档日志位置很麻烦，可以使用以下方式解决。<br />
&nbsp; &nbsp; &nbsp; &nbsp; Recover from &#8216;new_dir&#8217; datafile 9;&nbsp;&nbsp;-- 该方法我试了下不行，好像已被oracle废除<br />
&nbsp; &nbsp; &nbsp; &nbsp; Set logsource&nbsp;&nbsp;&#8216;new_dir&#8217;&nbsp; &nbsp;--注意没有&#8220;=&#8221;<br />
Recover datafile 9;<br />
<br />
<img src ="http://www.blogjava.net/AstroQi/aggbug/252019.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/AstroQi/" target="_blank">Astro.Qi</a> 2009-01-20 11:21 <a href="http://www.blogjava.net/AstroQi/archive/2009/01/20/252019.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>满足任意几个条件的查询方法</title><link>http://www.blogjava.net/AstroQi/archive/2009/01/19/251887.html</link><dc:creator>Astro.Qi</dc:creator><author>Astro.Qi</author><pubDate>Mon, 19 Jan 2009 05:47:00 GMT</pubDate><guid>http://www.blogjava.net/AstroQi/archive/2009/01/19/251887.html</guid><wfw:comment>http://www.blogjava.net/AstroQi/comments/251887.html</wfw:comment><comments>http://www.blogjava.net/AstroQi/archive/2009/01/19/251887.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/AstroQi/comments/commentRss/251887.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/AstroQi/services/trackbacks/251887.html</trackback:ping><description><![CDATA[<p><span style="color: rgb(0,0,255)">满足5个条件中任意四个条件都可以的实现方法。</span></p>
<p><span style="color: rgb(0,0,255)">一、使用case when实现</span></p>
<p><span style="color: rgb(0,0,255)">select</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(128,128,128)">*</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,255)">from</span><span style="color: rgb(0,0,0)"> 表名 <br />
</span><span style="color: rgb(0,0,255)">where</span><span style="color: rgb(0,0,0)"> (</span><span style="color: rgb(255,0,255)">case</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,255)">when</span><span style="color: rgb(0,0,0)"> 条件1 </span><span style="color: rgb(0,0,255)">then</span><span style="color: rgb(0,0,0)"> </span><span style="font-weight: bold; color: rgb(128,0,0)">1</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,255)">else</span><span style="color: rgb(0,0,0)"> </span><span style="font-weight: bold; color: rgb(128,0,0)">0</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,255)">end </span><span style="color: rgb(128,128,128)">+</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(255,0,255)">case</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,255)">when</span><span style="color: rgb(0,0,0)"> 条件2 </span><span style="color: rgb(0,0,255)">then</span><span style="color: rgb(0,0,0)"> </span><span style="font-weight: bold; color: rgb(128,0,0)">1</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,255)">else</span><span style="color: rgb(0,0,0)"> </span><span style="font-weight: bold; color: rgb(128,0,0)">0</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,255)">end </span><span style="color: rgb(128,128,128)">+</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(255,0,255)">case</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,255)">when</span><span style="color: rgb(0,0,0)"> 条件3 </span><span style="color: rgb(0,0,255)">then</span><span style="color: rgb(0,0,0)"> </span><span style="font-weight: bold; color: rgb(128,0,0)">1</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,255)">else</span><span style="color: rgb(0,0,0)"> </span><span style="font-weight: bold; color: rgb(128,0,0)">0</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,255)">end </span><span style="color: rgb(128,128,128)">+</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(255,0,255)">case</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,255)">when</span><span style="color: rgb(0,0,0)"> 条件4 </span><span style="color: rgb(0,0,255)">then</span><span style="color: rgb(0,0,0)"> </span><span style="font-weight: bold; color: rgb(128,0,0)">1</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,255)">else</span><span style="color: rgb(0,0,0)"> </span><span style="font-weight: bold; color: rgb(128,0,0)">0</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,255)">end </span><span style="color: rgb(128,128,128)">+</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(255,0,255)">case</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,255)">when</span><span style="color: rgb(0,0,0)"> 条件5 </span><span style="color: rgb(0,0,255)">then</span><span style="color: rgb(0,0,0)"> </span><span style="font-weight: bold; color: rgb(128,0,0)">1</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,255)">else</span><span style="color: rgb(0,0,0)"> </span><span style="font-weight: bold; color: rgb(128,0,0)">0</span><span style="color: rgb(0,0,0)"> </span><span style="color: rgb(0,0,255)">end</span><span style="color: rgb(0,0,0)">) </span><span style="color: rgb(128,128,128)">&gt;= 10;</span></p>
<img src ="http://www.blogjava.net/AstroQi/aggbug/251887.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/AstroQi/" target="_blank">Astro.Qi</a> 2009-01-19 13:47 <a href="http://www.blogjava.net/AstroQi/archive/2009/01/19/251887.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Oracle的执行计划</title><link>http://www.blogjava.net/AstroQi/archive/2009/01/19/251884.html</link><dc:creator>Astro.Qi</dc:creator><author>Astro.Qi</author><pubDate>Mon, 19 Jan 2009 05:40:00 GMT</pubDate><guid>http://www.blogjava.net/AstroQi/archive/2009/01/19/251884.html</guid><wfw:comment>http://www.blogjava.net/AstroQi/comments/251884.html</wfw:comment><comments>http://www.blogjava.net/AstroQi/archive/2009/01/19/251884.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/AstroQi/comments/commentRss/251884.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/AstroQi/services/trackbacks/251884.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 一、什么是执行计划 An explain plan is a representation of the access path that is taken when a query is executed within Oracle.二、如何访问数据At the physical level Oracle reads blocks of data. The smallest ...&nbsp;&nbsp;<a href='http://www.blogjava.net/AstroQi/archive/2009/01/19/251884.html'>阅读全文</a><img src ="http://www.blogjava.net/AstroQi/aggbug/251884.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/AstroQi/" target="_blank">Astro.Qi</a> 2009-01-19 13:40 <a href="http://www.blogjava.net/AstroQi/archive/2009/01/19/251884.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Oracle树结构的专用方法</title><link>http://www.blogjava.net/AstroQi/archive/2009/01/19/251881.html</link><dc:creator>Astro.Qi</dc:creator><author>Astro.Qi</author><pubDate>Mon, 19 Jan 2009 05:26:00 GMT</pubDate><guid>http://www.blogjava.net/AstroQi/archive/2009/01/19/251881.html</guid><wfw:comment>http://www.blogjava.net/AstroQi/comments/251881.html</wfw:comment><comments>http://www.blogjava.net/AstroQi/archive/2009/01/19/251881.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/AstroQi/comments/commentRss/251881.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/AstroQi/services/trackbacks/251881.html</trackback:ping><description><![CDATA[<h2>树结构和它的专用函数SYS_CONNECT_BY_PATH</h2>
<div class="t_msgfont" id="postmessage_958531">简单的树型结构<br />
关于树的普通应用<br />
学习了下这个函数, 用ORGINDUSTRIES的表做了个测试:<br />
正常的树型结构<br />
select lpad(' ',6*(level-1))||industry,indlevel,indid,pindid<br />
from ORGINDUSTRIES<br />
start with indid=1<br />
connect by pindid=prior indid<br />
结果显示如下<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Indlevel&nbsp;&nbsp;&nbsp;&nbsp; indid&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pindid<br />
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; 服装与服饰&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;1&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; 0<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;服装&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;2&nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;1<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;女装&nbsp; &nbsp; &nbsp; &nbsp; 3&nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;3&nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;2<br />
<br />
倒型树<br />
下面这个例子是个&#8221;倒数&#8221;—倒过来的树型结构<br />
select lpad(' ',6*(level-1))||industry,indlevel,indid,pindid<br />
from ORGINDUSTRIES<br />
start with indid=20<br />
connect by indid=prior pindid;<br />
这是标准结果:<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Indlevel indid&nbsp; &nbsp; pindid<br />
二手服装&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; 3&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;20&nbsp; &nbsp;&nbsp; &nbsp; 2<br />
&nbsp; &nbsp;&nbsp; &nbsp;服装&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;2&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;1<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;服装与服饰&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;1&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;1&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0<br />
结论<br />
无论正树还是倒树, 关键就在于connect by的条件. <br />
正树:&nbsp;&nbsp;必须是&nbsp;&nbsp;&#8216;父&#8217;= prior &#8216;子&#8217;<br />
倒树:&nbsp;&nbsp;必须是&nbsp;&nbsp;&#8216;子&#8217;= prior &#8216;父&#8217;<br />
<br />
树型结构的条件过滤<br />
采用树型结构的话, 如果我们想将树上的一个分支砍掉.&nbsp;&nbsp;将分支后面的结构都抛弃掉, 这个可以实现麽？当然可以。 但是不是用where， where条件只能去除单一的条件。<br />
所以， 这种树型的过滤条件就需要加在connect by上面。<br />
<br />
测试如下：由于用真实环境比较贴近实际，所以提前用下SYS_CONNECT_BY_PATH函数来显示下环境<br />
<br />
不加任何条件的环境：<br />
select areaname,sys_connect_by_path(areaname,',')<br />
from areas bb<br />
start with areaname='中国大陆'<br />
connect by parentareaid=prior areaid&nbsp;&nbsp;<br />
<br />
结果：<br />
1&nbsp; &nbsp; &nbsp; &nbsp; 中国大陆,中国大陆<br />
2&nbsp; &nbsp; &nbsp; &nbsp; 北京&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,北京<br />
3&nbsp; &nbsp; &nbsp; &nbsp; 北京&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,北京,北京<br />
4&nbsp; &nbsp; &nbsp; &nbsp; 东城区&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,北京,东城区<br />
5&nbsp; &nbsp; &nbsp; &nbsp; 西城区&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,北京,西城区<br />
22&nbsp; &nbsp; &nbsp; &nbsp; 广东&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,广东<br />
23&nbsp; &nbsp; &nbsp; &nbsp; 广州&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,广东,广州<br />
24&nbsp; &nbsp; &nbsp; &nbsp; 汕尾&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,广东,汕尾<br />
25&nbsp; &nbsp; &nbsp; &nbsp; 潮阳&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,广东,潮阳<br />
46&nbsp; &nbsp; &nbsp; &nbsp; 上海&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,上海<br />
47&nbsp; &nbsp; &nbsp; &nbsp; 上海&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,上海,上海<br />
48&nbsp; &nbsp; &nbsp; &nbsp; 黄浦区&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,上海,黄浦区<br />
49&nbsp; &nbsp; &nbsp; &nbsp; 闸北区&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,上海,闸北区<br />
<br />
加了where过滤条件的SQL:<br />
select areaname,sys_connect_by_path(areaname,',')<br />
from areas bb<br />
where bb.areaid&gt;861000<br />
start with areaname='中国大陆'<br />
connect by parentareaid=prior areaid<br />
<br />
结果为：<br />
2&nbsp; &nbsp; &nbsp; &nbsp; 北京&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,北京<br />
3&nbsp; &nbsp; &nbsp; &nbsp; 北京&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,北京,北京<br />
4&nbsp; &nbsp; &nbsp; &nbsp; 东城区&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,北京,东城区<br />
5&nbsp; &nbsp; &nbsp; &nbsp; 西城区&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,北京,西城区<br />
22&nbsp; &nbsp; &nbsp; &nbsp; 广东&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,广东<br />
23&nbsp; &nbsp; &nbsp; &nbsp; 广州&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,广东,广州<br />
24&nbsp; &nbsp; &nbsp; &nbsp; 汕尾&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,广东,汕尾<br />
25&nbsp; &nbsp; &nbsp; &nbsp; 潮阳&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,广东,潮阳<br />
46&nbsp; &nbsp; &nbsp; &nbsp; 上海&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,上海<br />
47&nbsp; &nbsp; &nbsp; &nbsp; 上海&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,上海,上海<br />
48&nbsp; &nbsp; &nbsp; &nbsp; 黄浦区&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,上海,黄浦区<br />
49&nbsp; &nbsp; &nbsp; &nbsp; 闸北区&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,上海,闸北区<br />
<br />
结论：去掉了&#8220;1&nbsp; &nbsp; &nbsp; &nbsp; 中国大陆,中国大陆&#8221;数据<br />
<br />
加了connect by的过滤条件：<br />
select areaname,sys_connect_by_path(areaname,',')<br />
from areas bb<br />
where bb.areaid&gt;861000<br />
start with areaname='中国大陆'<br />
connect by parentareaid=prior areaid&nbsp;&nbsp;and areaname&lt;&gt;'广东'<br />
<br />
结果为：<br />
2&nbsp; &nbsp; &nbsp; &nbsp; 北京&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,北京<br />
3&nbsp; &nbsp; &nbsp; &nbsp; 北京&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,北京,北京<br />
4&nbsp; &nbsp; &nbsp; &nbsp; 东城区&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,北京,东城区<br />
5&nbsp; &nbsp; &nbsp; &nbsp; 西城区&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,北京,西城区<br />
46&nbsp; &nbsp; &nbsp; &nbsp; 上海&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,上海<br />
47&nbsp; &nbsp; &nbsp; &nbsp; 上海&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,上海,上海<br />
48&nbsp; &nbsp; &nbsp; &nbsp; 黄浦区&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,上海,黄浦区<br />
49&nbsp; &nbsp; &nbsp; &nbsp; 闸北区&nbsp; &nbsp; &nbsp; &nbsp; ,中国大陆,上海,闸北区<br />
<br />
结论：去掉了整个广东的分支，&nbsp;&nbsp;在结果集中只有北京和上海<br />
<br />
<br />
SYS_CONNECT_BY_PATH函数<br />
采用SYS_CONNECT_BY_PATH函数为:<br />
<br />
select industry,sys_connect_by_path(industry,'/')<br />
from ORGINDUSTRIES<br />
start with indid=3<br />
connect by indid=prior pindid;<br />
<br />
结果为:<br />
女装&nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;/女装<br />
服装&nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /女装/服装<br />
服装与服饰&nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;/女装/服装/服装与服饰<br />
<br />
这样的话, 就可以实现, 树结构的结果集的单行拼接:<br />
<br />
我们只需要取最大的字段就OK了<br />
<br />
测试如下：<br />
<br />
select max(sys_connect_by_path(industry,'/'))<br />
from ORGINDUSTRIES<br />
start with indid=3<br />
connect by indid=prior pindid;<br />
<br />
结果为：<br />
/女装/服装/服装与服饰<br />
<br />
复杂的树型结构――多列变单列<br />
树型结构也分单树和多树(我的称呼,实际上就是指单支和多支)<br />
对于下面的这种情况， 我们必须要构造的树就属于单支树。<br />
原始环境<br />
环境如下：<br />
select * from test；<br />
<br />
结果为：<br />
1&nbsp; &nbsp; &nbsp; &nbsp; n1<br />
1&nbsp; &nbsp; &nbsp; &nbsp; n2<br />
1&nbsp; &nbsp; &nbsp; &nbsp; n3<br />
1&nbsp; &nbsp; &nbsp; &nbsp; n4<br />
1&nbsp; &nbsp; &nbsp; &nbsp; n5<br />
3&nbsp; &nbsp; &nbsp; &nbsp; t1<br />
3&nbsp; &nbsp; &nbsp; &nbsp; t2<br />
3&nbsp; &nbsp; &nbsp; &nbsp; t3<br />
3&nbsp; &nbsp; &nbsp; &nbsp; t4<br />
3&nbsp; &nbsp; &nbsp; &nbsp; t5<br />
3&nbsp; &nbsp; &nbsp; &nbsp; t6<br />
2&nbsp; &nbsp; &nbsp; &nbsp; m1<br />
<br />
造树<br />
脚本如下：<br />
select no,q,<br />
&nbsp; &nbsp;&nbsp; &nbsp; no+row_number() over( order by no) rn,<br />
&nbsp; &nbsp;&nbsp; &nbsp; row_number() over(partition by no order by no) rn1<br />
from test<br />
<br />
结果如下：<br />
No&nbsp;&nbsp;Q&nbsp;&nbsp;RN RN1<br />
1&nbsp; &nbsp; &nbsp; &nbsp; n1&nbsp; &nbsp; &nbsp; &nbsp; 2&nbsp; &nbsp; &nbsp; &nbsp; 1<br />
1&nbsp; &nbsp; &nbsp; &nbsp; n2&nbsp; &nbsp; &nbsp; &nbsp; 3&nbsp; &nbsp; &nbsp; &nbsp; 2<br />
1&nbsp; &nbsp; &nbsp; &nbsp; n3&nbsp; &nbsp; &nbsp; &nbsp; 4&nbsp; &nbsp; &nbsp; &nbsp; 3<br />
1&nbsp; &nbsp; &nbsp; &nbsp; n4&nbsp; &nbsp; &nbsp; &nbsp; 5&nbsp; &nbsp; &nbsp; &nbsp; 4<br />
1&nbsp; &nbsp; &nbsp; &nbsp; n5&nbsp; &nbsp; &nbsp; &nbsp; 6&nbsp; &nbsp; &nbsp; &nbsp; 5<br />
2&nbsp; &nbsp; &nbsp; &nbsp; m1&nbsp; &nbsp; &nbsp; &nbsp; 8&nbsp; &nbsp; &nbsp; &nbsp; 1<br />
3&nbsp; &nbsp; &nbsp; &nbsp; t1&nbsp; &nbsp; &nbsp; &nbsp; 10&nbsp; &nbsp; &nbsp; &nbsp; 1<br />
3&nbsp; &nbsp; &nbsp; &nbsp; t2&nbsp; &nbsp; &nbsp; &nbsp; 11&nbsp; &nbsp; &nbsp; &nbsp; 2<br />
3&nbsp; &nbsp; &nbsp; &nbsp; t3&nbsp; &nbsp; &nbsp; &nbsp; 12&nbsp; &nbsp; &nbsp; &nbsp; 3<br />
3&nbsp; &nbsp; &nbsp; &nbsp; t4&nbsp; &nbsp; &nbsp; &nbsp; 13&nbsp; &nbsp; &nbsp; &nbsp; 4<br />
3&nbsp; &nbsp; &nbsp; &nbsp; t5&nbsp; &nbsp; &nbsp; &nbsp; 14&nbsp; &nbsp; &nbsp; &nbsp; 5<br />
3&nbsp; &nbsp; &nbsp; &nbsp; t6&nbsp; &nbsp; &nbsp; &nbsp; 15&nbsp; &nbsp; &nbsp; &nbsp; 6<br />
<br />
每列的目的是：<br />
RN1列主要的目的是分组， 按照value值&#8216;1&#8217;，我们可以start with使用它。<br />
<br />
RN列主要用来做connect by使用。 实际上它就是我们要的树。<br />
第一个支： 2，3，4，5，6<br />
第二个支： 8<br />
第三个支： 10，11，12，13，14，15<br />
<br />
中间为什么要断掉：7,9&nbsp;&nbsp;目的就是为了区别每个分支。 到后面看具体的SQL，就明白这里的说法了。<br />
<br />
杀手锏<br />
既然我们有了树， 就可以使用树型函数SYS_CONNECT_BY_PATH和connect by啦，来拼接我们所需要的多列值。<br />
<br />
脚本如下：<br />
select no,sys_connect_by_path(q,',')<br />
from (<br />
select no,q,<br />
&nbsp; &nbsp;&nbsp; &nbsp; no+row_number() over( order by no) rn,<br />
&nbsp; &nbsp;&nbsp; &nbsp; row_number() over(partition by no order by no) rn1<br />
from test<br />
)<br />
start with rn1=1<br />
connect by rn-1=prior rn<br />
<br />
结果为：<br />
1&nbsp; &nbsp; &nbsp; &nbsp; ,n1<br />
1&nbsp; &nbsp; &nbsp; &nbsp; ,n1,n2<br />
1&nbsp; &nbsp; &nbsp; &nbsp; ,n1,n2,n3<br />
1&nbsp; &nbsp; &nbsp; &nbsp; ,n1,n2,n3,n4<br />
1&nbsp; &nbsp; &nbsp; &nbsp; ,n1,n2,n3,n4,n5<br />
2&nbsp; &nbsp; &nbsp; &nbsp; ,m1<br />
3&nbsp; &nbsp; &nbsp; &nbsp; ,t1<br />
3&nbsp; &nbsp; &nbsp; &nbsp; ,t1,t2<br />
3&nbsp; &nbsp; &nbsp; &nbsp; ,t1,t2,t3<br />
3&nbsp; &nbsp; &nbsp; &nbsp; ,t1,t2,t3,t4<br />
3&nbsp; &nbsp; &nbsp; &nbsp; ,t1,t2,t3,t4,t5<br />
3&nbsp; &nbsp; &nbsp; &nbsp; ,t1,t2,t3,t4,t5,t6<br />
<br />
终极武器<br />
最终我们要的值，是单列值， 其实想想， 也就是最长的一行咯。 那么就好办了。 我们直接GROUP BY ，然后取MAX值。<br />
脚本如下：<br />
select no,max(sys_connect_by_path(q,','))<br />
from (<br />
select no,q,<br />
&nbsp; &nbsp;&nbsp; &nbsp; no+row_number() over( order by no) rn,<br />
&nbsp; &nbsp;&nbsp; &nbsp; row_number() over(partition by no order by no) rn1<br />
from test<br />
)<br />
start with rn1=1<br />
connect by rn-1=prior rn <br />
group by no<br />
<br />
结果为：<br />
1&nbsp; &nbsp; &nbsp; &nbsp; ,n1,n2,n3,n4,n5<br />
2&nbsp; &nbsp; &nbsp; &nbsp; ,m1<br />
3&nbsp; &nbsp; &nbsp; &nbsp; ,t1,t2,t3,t4,t5,t6<br />
<br />
如果觉得前面的&#8216;，&#8217;不好看，可以使用ltrim去掉。 或者用substr也可以。<br />
如下：<br />
ltrim(max(sys_connect_by_path(q,',')),',')<br />
或者<br />
substr(max(sys_connect_by_path(q,',')),2)</div>
<img src ="http://www.blogjava.net/AstroQi/aggbug/251881.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/AstroQi/" target="_blank">Astro.Qi</a> 2009-01-19 13:26 <a href="http://www.blogjava.net/AstroQi/archive/2009/01/19/251881.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ORACLE SQL 索引</title><link>http://www.blogjava.net/AstroQi/archive/2009/01/19/251880.html</link><dc:creator>Astro.Qi</dc:creator><author>Astro.Qi</author><pubDate>Mon, 19 Jan 2009 05:16:00 GMT</pubDate><guid>http://www.blogjava.net/AstroQi/archive/2009/01/19/251880.html</guid><wfw:comment>http://www.blogjava.net/AstroQi/comments/251880.html</wfw:comment><comments>http://www.blogjava.net/AstroQi/archive/2009/01/19/251880.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/AstroQi/comments/commentRss/251880.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/AstroQi/services/trackbacks/251880.html</trackback:ping><description><![CDATA[<div>
<div>ORACLE SQL TUNING<br />
一．优化器模式<br />
&nbsp;&nbsp; ORACLE的优化器共有3种:<br />
&nbsp;&nbsp; a.&nbsp; RULE (基于规则)&nbsp;&nbsp; b. COST (基于成本)&nbsp; c. CHOOSE (选择性)<br />
&nbsp;&nbsp; 为了使用基于成本的优化器(CBO, Cost-Based Optimizer) , 你必须定期更新统计信息，以保证数据库中的对象统计信息(object statistics)的准确性.<br />
&nbsp;&nbsp; 如果数据库的优化器模式设置为选择性(CHOOSE),那么实际的优化器模式将和是否运行过analyze命令有关. 如果table已经被analyze过, 优化器模式将自动成为CBO , 反之,数据库将采用RULE形式的优化器。</div>
</div>
<div class="post_content">
<p>二．访问Table的方式<br />
ORACLE 采用两种访问表中记录的方式:<br />
a.&nbsp; 全表扫描 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 全表扫描就是顺序地访问表中每条记录. ORACLE采用一次读入多个数&nbsp;据块(database block)的方式优化全表扫描。<br />
&nbsp;&nbsp;&nbsp; <br />
b.&nbsp; 索引扫描<br />
&nbsp;&nbsp; 你可以采用基于ROWID的访问方式情况,提高访问表的效率, ROWID包含了表中记录的物理位置信息.ORACLE采用索引(INDEX)实现了数据和存放数据的物理位置(ROWID)之间的联系. 通常索引提供了快速访问ROWID的方法,因此那些基于索引列的查询就可以得到性能上的提高.</p>
<p>其中ORACLE对索引又有两种访问模式.<br />
a)索引唯一扫描 ( INDEX UNIQUE SCAN)<br />
大多数情况下, 优化器通过WHERE子句访问INDEX.<br />
例如:<br />
表LOADING有两个索引 : 建立在LOADING列上的唯一性索引LOADING_PK和建立在MANAGER列上的非唯一性索引IDX_MANAGER. <br />
SELECT loading&nbsp; <br />
FROM LOADING<br />
WHERE LOADING = &#8216;ROSE HILL&#8217;;<br />
&nbsp;&nbsp; 在内部 , 上述SQL将被分成两步执行, 首先 , LOADING_PK 索引将通过索引唯一扫描的方式被访问 , 获得相对应的ROWID, 通过ROWID访问表的方式执行下一步检索.<br />
&nbsp;&nbsp; 如果被检索返回的列包括在INDEX列中,ORACLE将不执行第二步的处理(通过ROWID访问表). 因为检索数据保存在索引中, 单单访问索引就可以完全满足查询结果. <br />
&nbsp;&nbsp; 下面SQL只需要INDEX UNIQUE SCAN 操作.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SELECT LOADING<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FROM&nbsp; LOADING<br />
WHERE LOADING = &#8216;ROSE HILL&#8217;;<br />
&nbsp;<br />
&nbsp; b)索引范围查询(INDEX RANGE SCAN)<br />
&nbsp;&nbsp;&nbsp;&nbsp; 适用于两种情况:<br />
1. 基于一个范围的检索<br />
2. 基于非唯一性索引的检索<br />
&nbsp;例1:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SELECT LOADING<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FROM&nbsp; LOADING<br />
WHERE LOADING LIKE &#8216;M%&#8217;;<br />
&nbsp;<br />
WHERE子句条件包括一系列值, ORACLE将通过索引范围查询的方式查询LODGING_PK . 由于索引范围查询将返回一组值, 它的效率就要比索引唯一扫描<br />
低一些.&nbsp; <br />
例2:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SELECT LOADING<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FROM&nbsp; LOADING<br />
WHERE MANAGER = &#8216;BILL GATES&#8217;;<br />
&nbsp;这个SQL的执行分两步, IDX_MANAGER的索引范围查询(得到所有符合条件记录的ROWID) 和下一步同过ROWID访问表得到LOADING列的值. 由于IDX_MANAGER是一个非唯一性的索引,数据库不能对它执行索引唯一扫描. <br />
&nbsp;<br />
&nbsp; 由于SQL返回LOADING列,而它并不存在于IDX_MANAGER索引中, 所以在索引范围查询后会执行一个通过ROWID访问表的操作. <br />
&nbsp; WHERE子句中, 如果索引列所对应的值的第一个字符由通配符(WILDCARD)开始, 索引将不被采用.<br />
SELECT LOADING<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FROM&nbsp; LOADING<br />
WHERE MANAGER LIKE &#8216;％HANMAN&#8217;;<br />
在这种情况下，ORACLE将使用全表扫描.</p>
<p><br />
三．SQL调优的本质就是调整执行计划。<br />
&nbsp;在好多情况下，oracle自动选择的执行计划并不是最优的，这时需要我们人工去干预。(什么是执行计划?)<br />
&nbsp;</p>
<p>对SQL调优基本步骤：<br />
a)&nbsp;捕获SQL语句<br />
b)&nbsp;产生SQL语句的执行计划；<br />
c)&nbsp;验证统计信息(SQL语句涉及到的表格是否做过分析)，表格信息(结果集的记录数，索引)，字段上面数据分布特点<br />
d)&nbsp;通过手工收集到的信息，形成自己理想的执行计划。<br />
e)&nbsp;如果做过分析，则重新分析相关表格或者做柱状图分析。<br />
f)&nbsp;如果没有做过分析，则通过尝试不同的Hint，从而获得合适的执行计划。<br />
g)&nbsp;当我们正常无法调优到位时，可以打开10053事件打开优化器的跟踪，看看Oracle如何选择的.<br />
alter session set events='10053 trace name context forever,level 2';<br />
&nbsp;<br />
四．如何捕获SQL语句<br />
&nbsp;捕获SQL语句的方法有如下几种：<br />
&nbsp;&nbsp;1．SQL TRACE或10046跟踪某个模块。<br />
&nbsp;&nbsp;2．PERFSTAT性能统计包，使用方法见附录二。<br />
&nbsp;&nbsp;3．V$SQL，V$SESSION_WAIT，V$SQL_TEXT<br />
五．如何查看执行计划<br />
&nbsp;查看SQL语句的执行计划有以下几种：<br />
&nbsp;1．Set autotrace on(set autotrace traceonly exp)<br />
&nbsp;2．Explain plan for &#8230;..<br />
&nbsp;&nbsp;@?/rdbms/admin/utlxpls.sql<br />
&nbsp;3．V$SQL_PLAN视图<br />
&nbsp;&nbsp;column operation format a16 <br />
column "Query Plan" format a60 <br />
column options format a15 <br />
column object_name&nbsp; format a20 <br />
column id&nbsp; format 99 </p>
<p>select id,lpad(' ',2*(level-1))||operation||' '||options||' '||object_name||' ' <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ||decode(id,0,'Cost = '||position) "Query Plan" <br />
from (select * <br />
from v$sql_plan&nbsp; <br />
where address='&amp;a') sql_plan <br />
start with id = 0 <br />
connect by prior id = parent_id<br />
/</p>
<p>&nbsp;4．第三方工具，如pl/sql developer,TOAD<br />
&nbsp;<br />
六．SQL语句主要的连接方法</p>
<p>a)&nbsp;Nested-loop join<br />
适合于小表(几千条，几万条记录)与大表做联接<br />
在联接列上有索引。</p>
<p>&nbsp;分内表和外表(驱动表)，靠近from子句的是内表。从效率上讲，小表应该作外表，大表应该作内表，即大表查询时走索引。</p>
<p>COST= Access cost of A(驱动表) + (access cost of B * number of rows from A)</p>
<p>成本计算方法：<br />
&nbsp;设小表100行，大表100000行。</p>
<p>&nbsp;两表均有索引：<br />
&nbsp;如果小表在内，大表在外(驱动表)的话，则扫描次数为：<br />
&nbsp;&nbsp;100000+100000*2 (其中2表示IO次数，一次索引，一次数据)<br />
&nbsp;如果大表在内，小表在外(驱动表)的话，则扫描次数为：<br />
&nbsp;&nbsp;100+100*2.</p>
<p>&nbsp;两表均无索引：<br />
&nbsp;如果小表在内，大表在外的话，则扫描次数为：<br />
&nbsp;&nbsp;100000+100*100000<br />
&nbsp;如果大表在内，小表在外的话，则扫描次数为：<br />
&nbsp;&nbsp;100+100000*100</p>
<p>注意：如果一个表有索引，一个表没有索引，ORACLE会将没有索引的表作驱动表。如果两个表都有索引，则外表作驱动表。如果两个都没索引的话，则也是外表作驱动表。</p>
<p>&nbsp;基本的执行计划如下所示：<br />
&nbsp;&nbsp;NESTED LOOPS<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (BY ROWID)&nbsp; OF&nbsp; our_outer_table<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INDEX (..SCAN) OF outer_table_index(&#8230;.)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (BY ROWID)&nbsp; OF&nbsp; our_inner_table<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INDEX (..SCAN) OF inner_table_index(&#8230;.)</p>
<p>b)&nbsp;Hash join </p>
<p>适合于大表与大表，小表(几十万，几百万)与大表之间的联连。<br />
联接列上不需要索引。</p>
<p>基本执行计划如下：<br />
&nbsp;HASH JOIN<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (&#8230;.)&nbsp; OF&nbsp; tableA<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (&#8230;.)&nbsp; OF&nbsp; tableB</p>
<p>cost= (access cost of A * number of hash partitions of B) + access cost of B</p>
<p>可以看出主要成本在于A表是否可以被Cache。Hash_area_size的大小将决定Hash Join的主要成本。可以看出Hash Join的成本和返回集合并没有直接的关系，所以当返回结果集比较大的时候一般具有较好的性能。</p>
<p>为了加快hash join的速度，可以调大hash_area_size和pga_aggregate_target（默认为25M）的值。</p>
<p><br />
c)&nbsp;Sort Merge join</p>
<p>每一个Row Source在Join列上均排序。<br />
&nbsp;然后两个排序后的Row Source合并后，作一个结果集返回。<br />
&nbsp;Sort/Merge Join仅仅对equal Join有效。</p>
<p>基本执行计划<br />
&nbsp;MERGE (JOIN)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SORT (JOIN) <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (&#8230;.)&nbsp; OF&nbsp; tableA<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SORT (JOIN) <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (&#8230;.)&nbsp; OF&nbsp; tableB</p>
<p>cost= access cost of A + access cost of B +(sort cost of A + sort cost of B)</p>
<p>可以看出Sort的成本是Merge Join的主要构成部分。这样sort_area_size的大小将很大程度决定Merge Join的大小。同样如果A表或者B表已经经过排序的，那么Merge Join往往具有很好的性能。其不会走索引。</p>
<p>没有驱动表的概念，即时响应能力较差。</p>
<p>&nbsp;</p>
<p>七．一般情况下最常见的5种问题</p>
<p>1. Statement not written for indexes&nbsp;25%<br />
2. Indexes are missing or inappropriate&nbsp;16%<br />
3. Use of single-column index merge&nbsp;15%<br />
4. Misuse of nested loop, sort merge, or hash join&nbsp;12%<br />
5. Misuse of IN, EXISTS, NOT IN, NOT EXISTS, or table joins&nbsp;8%<br />
&nbsp;<br />
&nbsp;不过在我们这里，最常见的问题是在第2条，第3条，第4条。</p>
<p>1．&nbsp;Statement not written for indexes<br />
类似于这样的：<br />
SELECT account_name, trans_date, amount <br />
FROM transaction <br />
WHERE SUBSTR(account_name,1,7) = ' CAPITAL'; </p>
<p>WHERE account_name LIKE 'CAPITAL%'; </p>
<p>Account_date 日期</p>
<p>To_char(Account_date,&#8217;YYYY-MM-DD:HH24:MI:SS&#8217;)=&#8217;200508XXX&#8217;;</p>
<p>Account_date=to_date(&#8216;200508&#8230;.&#8217;,&#8217;yyyy-mm-dd);</p>
<p><br />
2．Indexes are missing or inappropriate<br />
&nbsp;<br />
&nbsp;例如REP_C021中有这样一句：<br />
select SUBSIDIARYID,260,'&nbsp;&nbsp;&nbsp; 300电话卡',<br />
&nbsp;&nbsp;&nbsp; sum(decode(feetype, 1, ceil(duration / 60))) +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(decode(feetype, 0, ceil(duration / 60))),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(decode(feetype, 1, ceil(duration / 60))),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(decode(feetype, 0, ceil(duration / 60))),0<br />
&nbsp;&nbsp;&nbsp; from cardsusage200508 a, service b<br />
&nbsp;&nbsp; where a.caller = b.servicecode and<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (b.property = i_property or i_property is null) and <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a.cdrtype = 102<br />
&nbsp;&nbsp; group by SUBSIDIARYID, 260, '&nbsp;&nbsp;&nbsp; 300电话卡';</p>
<p>Execution Plan<br />
----------------------------------------------------------<br />
&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SELECT STATEMENT Optimizer=RULE<br />
&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp; SORT (GROUP BY)<br />
&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp; NESTED LOOPS<br />
&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (FULL) OF 'CARDSUSAGE200508'<br />
&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (BY INDEX ROWID) OF 'SERVICE'<br />
&nbsp;&nbsp; 5&nbsp;&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INDEX (UNIQUE SCAN) OF 'SERVICE_CODE'<br />
&nbsp;<br />
我们取其中的select语句进行调优。在调整之前，原select语句需要6分钟左右。</p>
<p>12:19:20 SQL&gt; select cdrtype,count(*) from cardsusage200508<br />
12:20:12&nbsp;&nbsp; 2&nbsp; group by cdrtype;</p>
<p>CDRT&nbsp;&nbsp; COUNT(*)<br />
---- ----------<br />
102&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 637<br />
106&nbsp;&nbsp;&nbsp;&nbsp; 1973757<br />
107&nbsp;&nbsp;&nbsp;&nbsp; 2390097<br />
112&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 46016<br />
113&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20</p>
<p>针对cardsuage200508表格的特性，我们在CDRTYPE字段上建立一个位图索引CARDSUSAGE_CDRTYPE_BTIDX。<br />
将SQL语句加上以下Hint：<br />
&nbsp; select /*+&nbsp; INDEX(A, CARDSUSAGE_CDRTYPE_BTIDX)*/<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SUBSIDIARYID,260,'&nbsp;&nbsp;&nbsp; 300电话卡',<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(decode(feetype, 1, ceil(duration / 60))) +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(decode(feetype, 0, ceil(duration / 60))),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(decode(feetype, 1, ceil(duration / 60))),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(decode(feetype, 0, ceil(duration / 60))),0<br />
&nbsp;&nbsp;&nbsp; from cardsusage200508&nbsp; a, service b<br />
&nbsp;&nbsp; where a.caller = b.servicecode and<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (b.property = i_property or i_property is null) and <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a.cdrtype = 102<br />
&nbsp;&nbsp; group by SUBSIDIARYID, 260, '&nbsp;&nbsp;&nbsp; 300电话卡';<br />
&nbsp;这样调整后，只需要几秒钟即可出来。</p>
<p>3.&nbsp; Use of single-column index merge<br />
&nbsp;复合索引有的时候比单列索引效率更高。根据where子句中的具体情况，有&nbsp;时可以建立复合索引。例如：<br />
&nbsp;select a.AccountNum,a.ChargeID,a.Total,b.ItemID,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b.Amount,c.billingcycle<br />
&nbsp; from charge_bill a, chargedetail_bill b, Account c<br />
&nbsp;where a.AccountNum &gt; 1 and a.AccountNum &lt;= 1969618 and<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a.status = '0' and a.InvoiceID is null and c.paymentmethod != '7' and<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a.Total &gt; 0 and a.AccountNum = c.AccountNum and<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a.ChargeID = b.ChargeID<br />
&nbsp;order by a.AccountNum, a.ChargeID, b.ItemID;<br />
这样的SQL语句执行需要3分27秒。</p>
<p>我们做了以下优化：<br />
在charge_bill表格的accountnum,status,total,invoiceid列上建立一个复合索引。这样上述SQL语句需要40秒左右。</p>
<p>&nbsp;Resume Service过程中有这么一句：<br />
&nbsp;SELECT NVL(SUM(A.FEE),0)&nbsp;&nbsp; <br />
FROM ACCOUNTBALANCE A,INVOICE B&nbsp; <br />
WHERE A.OBJECTID = B.INVOICEID&nbsp; AND A.ACCOUNTNUM = :b1 <br />
AND B.BILLINGBEGINDATE &lt; TO_DATE(:b2,'yyyymmdd');<br />
该语句需要执行大概72000次。整个过程执行大概需要100分钟左右。</p>
<p>将:b1以具体的值代替，这条SQL语句执行很快，大概0.1秒左右。</p>
<p>我们做了以下优化：<br />
在invoiceid,billingbegindate列上创建了一个索引idx_invoice_hc。<br />
将上述SQL语句改成：<br />
select /*+ use_nl(a,b) index(b,IDX_INVOICE_HC)*/&nbsp; nvl(sum(a.fee),0)<br />
from accountbalance a,invoice b<br />
where a.objectid=b.invoiceid&nbsp; and a.accountnum=m_accountnum<br />
and b.billingbegindate</p>
<p>这样一来，该过程的执行时间快的时候大概在10分钟左右，慢的时候(IO异常紧张的时)大概在30分钟左右。</p>
<p><br />
4.&nbsp;Misuse of nested loop, sort merge, or hash join<br />
&nbsp;表格之间的连接方式和连接顺序都将极大的影响SQL语句的性能。这种问&nbsp;&nbsp;题在平时最常见。ORACLE在处理5张或5张以上的表格的连接时候，很容&nbsp;易出问题。一般情况下，谨记前面表格之间的连接原则，即可以处理此类问&nbsp;题。<br />
&nbsp;<br />
&nbsp;&nbsp; 例如：<br />
&nbsp;&nbsp;select b.SUBSIDIARYID,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c.paymentmethod || ':' || nvl(subscribertype, '9999999'),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'gsm',count(*),sum(decode(untelLOCALCHARGE,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0,decode(duration,0,1,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; decode(sign(duration - 1800),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1, 2 + trunc((duration - 1201) / 600),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2)), trunc((duration + 599) / 600))),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(nvl(GSMCHARGE, 0)),nvl(property, '0'),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SUM(trunc((duration + 599) / 600))<br />
&nbsp; from&nbsp; rt_untelecomusage a ,service b, account c <br />
&nbsp;where a.starttime &gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; to_date(to_char(add_months(to_date('200508 ', 'YYYYMM'), -1),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'YYYYMM') || '20235959',<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'YYYYMMDDHH24MISS') and<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a.starttime &lt; to_date('200508 ' || '21', 'YYYYMMdd') and<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gsmcharge &gt; 0 and a.serviceid = b.serviceid and<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b.accountnum = c.accountnum<br />
&nbsp;group by b.SUBSIDIARYID,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c.paymentmethod || ':' || nvl(subscribertype, '9999999'),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'gsm',nvl(property, '0'); <br />
&nbsp;该语句原先需要4，5个小时左右。</p>
<p>优化：<br />
alter session set hash_area_size=300000000;</p>
<p>select /*+ use_hash(b,c) ordered NO_EXPAND full(a) use_hash(a)*/&nbsp; b.SUBSIDIARYID,c.paymentmethod || ':' || nvl(subscribertype, '9999999'),<br />
&nbsp;&nbsp;&nbsp;&nbsp; 'gsm',count(*), sum(decode(untelLOCALCHARGE,0,decode(duration,0, 1,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; decode(sign(duration - 1800), 1,2 + trunc((duration - 1201) / 600), 2)),<br />
&nbsp;&nbsp;&nbsp; &nbsp;trunc((duration + 599) / 600))),sum(nvl(GSMCHARGE, 0)),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nvl(property, '0'),SUM(trunc((duration + 599) / 600))<br />
&nbsp; from service b, account c,untelecomusage_200508&nbsp; a <br />
&nbsp;where a.starttime &gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; to_date(to_char(add_months(to_date('200508', 'YYYYMM'), -1),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'YYYYMM') || '20235959',<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'YYYYMMDDHH24MISS') and<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a.starttime &lt; to_date('200508' || '21', 'YYYYMMdd') and<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gsmcharge &gt; 0 and a.serviceid = b.serviceid and<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b.accountnum = c.accountnum<br />
&nbsp;group by b.SUBSIDIARYID,c.paymentmethod || ':' || nvl(subscribertype, '9999999'),'gsm',nvl(property, '0');&nbsp; </p>
<p>&nbsp;这样优化后，只需要40分钟左右即可。</p>
<p>八．案例<br />
1．&nbsp;循环Update操作<br />
&nbsp;<br />
&nbsp; 以下过程太慢了， 半个小时连5000条记录都未处理，总 共有7万多条。<br />
declare<br />
&nbsp;&nbsp;&nbsp; cursor c1 is <br />
&nbsp;&nbsp;&nbsp; select caller <br />
&nbsp;&nbsp;&nbsp; from zxx_sms_step where chargemonth=200504 and fee is null;<br />
&nbsp;&nbsp;&nbsp; icnt number;<br />
begin<br />
&nbsp;icnt:=0;<br />
&nbsp;for m_c1 in c1 loop<br />
&nbsp;&nbsp;update zxx_sms_step a set fee=<br />
&nbsp;&nbsp;&nbsp;(select nvl(sum(pascharge),0) from ipasimport_200504 where caller=m_c1.caller and pastag in (1243,1251))<br />
&nbsp;&nbsp;&nbsp;where caller=m_c1.caller and chargemonth=200504;<br />
&nbsp;&nbsp;icnt:=icnt+1;<br />
&nbsp;&nbsp;if icnt=500 then<br />
&nbsp;&nbsp;&nbsp;exit;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;end if;<br />
&nbsp;end loop;<br />
end;</p>
<p>&nbsp;&nbsp; 这样的SQL语句，建议先将update中的子查询生成一张中间表，然后再update。<br />
alter session set hash_area_size=400000000 ;</p>
<p>select /*+use_hash(a,b)*/ b.caller,nvl(sum(a.pascharge),0) from ipasimport_200504 a,zxx_sms_step b <br />
where b.chargemonth=200504 and b.fee is null <br />
and a.caller=b.caller and a.pastag in (1243,1251) <br />
group by b.caller;<br />
&nbsp;这样10分钟不到就可产生中间表，然后再update只需几分钟即可。</p>
<p><br />
2．&nbsp;部分表格未做统计信息分析<br />
&nbsp;<br />
&nbsp;网通OA系统自从oracle服务器从pc服务器上迁到小型机上后，其CPU利用率经常冲到很高。而其中每一个进程在某个瞬间将占用40%左右的CPU。这些进程都是通过jdbc thin client 连过来的。</p>
<p>通过抓取其sql_text，发现以下两条SQL语句不正常。<br />
1.<br />
&nbsp;SQL&gt;&nbsp; select D.flow_inid,D.step_inco,D.deal_man,D.agen_men,D.time_set,D.peri_man,<br />
&nbsp; 2&nbsp;&nbsp; S2.fsub_set,S2.fsub_id,F.mtbl_stru,F.doc_name,F.svr_name <br />
&nbsp; 3&nbsp;&nbsp; from deal_info D,step_inst S1,step_def S2,flow_inst F <br />
&nbsp; 4&nbsp;&nbsp; where D.step_inco=S1.step_inco and S1.flow_id=S2.flow_id <br />
&nbsp; 5&nbsp;&nbsp; and S1.step_code=S2.step_code and S1.flow_inid=F.flow_inid and D.step_type=5<br />
&nbsp; 6&nbsp;&nbsp; and D.fsub_flag is not null and D.fsub_flag=1 and rownum&lt;=1;</p>
<p>其执行计划和统计信息如下：</p>
<p>Execution Plan<br />
----------------------------------------------------------<br />
&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SELECT STATEMENT Optimizer=CHOOSE (Cost=22 Card=1 Bytes=1077)<br />
&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp; COUNT (STOPKEY)<br />
&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp; NESTED LOOPS (Cost=22 Card=1 Bytes=1077)<br />
&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NESTED LOOPS (Cost=21 Card=1 Bytes=360)<br />
&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NESTED LOOPS (Cost=20 Card=1 Bytes=150)<br />
&nbsp;&nbsp; 5&nbsp;&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (FULL) OF 'STEP_INST' (Cost=2 Card=9&nbsp; Bytes=153)<br />
&nbsp;&nbsp; 6&nbsp;&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (BY INDEX ROWID) OF 'DEAL_INFO' (Cost=2 Card=1 Bytes=133)<br />
&nbsp;&nbsp; 7&nbsp;&nbsp;&nbsp; 6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INDEX (RANGE SCAN) OF 'DEAL_INFO_STEP_INCO' (NON-UNIQUE) (Cost=2 <br />
&nbsp;&nbsp; 8&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (BY INDEX ROWID) OF 'FLOW_INST' (Cost=1 Card=1 Bytes=210)<br />
&nbsp;&nbsp; 9&nbsp;&nbsp;&nbsp; 8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INDEX (UNIQUE SCAN) OF 'PK_FLOW_INST' (UNIQUE)<br />
&nbsp; 10&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (BY INDEX ROWID) OF 'STEP_DEF' (Cost=1 Card=1 Bytes=717)<br />
&nbsp; 11&nbsp;&nbsp; 10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INDEX (UNIQUE SCAN) OF 'STEP_DEF_PK11119358638593' (UNIQUE)</p>
<p>Statistics<br />
----------------------------------------------------------<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp; recursive calls<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp; db block gets<br />
&nbsp;&nbsp;&nbsp;&nbsp; 270626&nbsp; consistent gets<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 273&nbsp; physical reads<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp; redo size<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1079&nbsp; bytes sent via SQL*Net to client<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 655&nbsp; bytes received via SQL*Net from client<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2&nbsp; SQL*Net roundtrips to/from client<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp; sorts (memory)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp; sorts (disk)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp; rows processed</p>
<p>这条SQL语句执行的时间也不长，就几秒钟，但是我们看到consistent gets很高有27万多，这个操作就是消耗CPU的祸首。从执行计划来看，其执行计划显然不可理，问题出在表格的连接顺序上面，应该是deal_info表格做为驱动表先访问。</p>
<p>检查这些表格的统计分析，发现step_def表格未做分析，对该表格做统计信息分析，并对deal_info表做柱状图分析后：<br />
analyze table deal_info compute statistics for all indexed columns;</p>
<p>其执行计划正是我们所想要的，同时consistent gets也只有200左右，该操作所消耗的CPU也下降到了1%。</p>
<p>2.表格的柱状图信息没有分析：<br />
SELECT SO.SO_NBR, so_type.name,STATUS.STS_WORDS, SO.REMARKS, SO.CHECK_TYPE,CTRL_ASGN.DISPATCHED_DATE, <br />
CTRL_ASGN.PRE_ALARM_DATE, CTRL_ASGN.ALARM_DATE<br />
from SO,SO_HANDLE, CTRL_ASGN,so_type,status <br />
WHERE&nbsp;SO_HANDLE.SO_NBR=SO.SO_NBR AND SO.SO_NBR=CTRL_ASGN.SO_NBR <br />
AND SO_HANDLE.HANDLE_TYPE_ID=1017<br />
and so.so_type_id=so_type.so_type_id and so.PRIORITY=status.sts_id and status.table_name='SO'<br />
&nbsp;AND STATUS.column_name ='PRIORITY' AND SO_HANDLE.WORK_AREA_ID= 300101<br />
AND SO.STATE= 'B' AND SO.HALT ='N'<br />
AND CTRL_ASGN.STATE = 'B'<br />
AND CTRL_ASGN.STS = 'D';</p>
<p>该SQL语句执行时间要2分钟左右。<br />
执行计划如下：<br />
Execution Plan<br />
----------------------------------------------------------<br />
&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SELECT STATEMENT Optimizer=HINT: RULE<br />
&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp; NESTED LOOPS<br />
&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp; NESTED LOOPS<br />
&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NESTED LOOPS<br />
&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NESTED LOOPS<br />
&nbsp;&nbsp; 5&nbsp;&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (BY INDEX ROWID) OF 'STATUS'<br />
&nbsp;&nbsp; 6&nbsp;&nbsp;&nbsp; 5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INDEX (RANGE SCAN) OF 'PK_STATUS' (UNIQUE)<br />
&nbsp;&nbsp; 7&nbsp;&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (BY INDEX ROWID) OF 'CTRL_ASGN'<br />
&nbsp;&nbsp; 8&nbsp;&nbsp;&nbsp; 7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INDEX (RANGE SCAN) OF 'CTRL_ASGN_0002'<br />
&nbsp;&nbsp; 9&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (BY INDEX ROWID) OF 'SO'<br />
&nbsp; 10&nbsp;&nbsp;&nbsp; 9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INDEX (UNIQUE SCAN) OF 'PK_SO' (UNIQUE)<br />
&nbsp; 11&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (BY INDEX ROWID) OF 'SO_TYPE'<br />
&nbsp; 12&nbsp;&nbsp; 11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INDEX (UNIQUE SCAN) OF 'PK_SO_TYPE' (UNIQUE)<br />
&nbsp; 13&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (BY INDEX ROWID) OF 'SO_HANDLE'<br />
&nbsp; 14&nbsp;&nbsp; 13&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INDEX (RANGE SCAN) OF 'PK_SO_HANDLE' (UNIQUE)</p>
<p>我们收集表格信息和结果集的信息:<br />
SQL&gt; select count(*) from CTRL_ASGN;<br />
&nbsp; COUNT(*)<br />
----------<br />
&nbsp;&nbsp; 1832469<br />
SQL&gt; select count(*) from status;<br />
&nbsp; COUNT(*)<br />
----------<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1718</p>
<p>SQL&gt; select count(*) from so;<br />
&nbsp; COUNT(*)<br />
----------<br />
&nbsp;&nbsp;&nbsp; 300296</p>
<p>SQL&gt; select count(*) from so_type;<br />
&nbsp; COUNT(*)<br />
----------<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 265</p>
<p>SQL&gt; select count(*) from so_handle;<br />
&nbsp; COUNT(*)<br />
----------<br />
&nbsp;&nbsp; 1296263&nbsp;&nbsp; </p>
<p>select count(*) from ctrl_asgn where&nbsp; CTRL_ASGN.STATE = 'B' AND CTRL_ASGN.STS = 'D';<br />
&nbsp; COUNT(*)<br />
----------<br />
&nbsp;&nbsp;&nbsp; 331490<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
select count(*) from so where SO.STATE= 'B' AND SO.HALT ='N';<br />
&nbsp; COUNT(*)<br />
----------<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 361<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
select count(*) from so_handle where SO_HANDLE.HANDLE_TYPE_ID=1017 and SO_HANDLE.WORK_AREA_ID= 300101;<br />
&nbsp; COUNT(*)<br />
----------<br />
&nbsp;&nbsp;&nbsp;&nbsp; 30086</p>
<p>通过对上面这些信息进行分析，我们可以发现这个问题也可以归结为表格之间的连接顺序上面。通过将SO表做柱状图分析后，该SQL语句只需1秒钟即可出来。<br />
Analyze table so compute statistics for all indexed columns;</p>
<p>执行计划变成如下：<br />
Execution Plan<br />
----------------------------------------------------------<br />
&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SELECT STATEMENT Optimizer=CHOOSE (Cost=273 Card=32 Bytes=3936)<br />
&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp; NESTED LOOPS (Cost=273 Card=32 Bytes=3936)<br />
&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp; NESTED LOOPS (Cost=153 Card=30 Bytes=2730)<br />
&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HASH JOIN (Cost=33 Card=30 Bytes=2130)<br />
&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NESTED LOOPS (Cost=31 Card=30 Bytes=1620)<br />
&nbsp;&nbsp; 5&nbsp;&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (FULL) OF 'STATUS' (Cost=2 Card=1 Bytes=25)<br />
&nbsp;&nbsp; 6&nbsp;&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (BY INDEX ROWID) OF 'SO' (Cost=29 Card=59 Bytes=1711)<br />
&nbsp;&nbsp; 7&nbsp;&nbsp;&nbsp; 6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INDEX (RANGE SCAN) OF 'SO_0003' (NON-UNIQUE) (Cost=2 Card=59)<br />
&nbsp;&nbsp; 8&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (FULL) OF 'SO_TYPE' (Cost=1 Card=128 Bytes=2176)<br />
&nbsp;&nbsp; 9&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (BY INDEX ROWID) OF 'SO_HANDLE' (Cost=4 Card=280 Bytes=5600)<br />
&nbsp; 10&nbsp;&nbsp;&nbsp; 9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INDEX (RANGE SCAN) OF 'PK_SO_HANDLE' (UNIQUE) (Cost=3 Card=280)<br />
&nbsp; 11&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (BY INDEX ROWID) OF 'CTRL_ASGN' (Cost=4 Card=13620 Bytes=435840)<br />
&nbsp; 12&nbsp;&nbsp; 11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INDEX (RANGE SCAN) OF 'CTRL_ASGN_0003' (NON-UNIQUE) (Cost=2 Card=13620)</p>
<p>&nbsp;</p>
<p>3．&nbsp;Not exists的使用<br />
--停机保号用户数(除欠费)<br />
select 'XJ'||1||'180','停机保号用户数',count(distinct serviceid),1,'200509',groupid from cbq_lch_usage0 <br />
where subsidiaryid=1 and subid&lt;&gt;'02'&nbsp; and subid&lt;&gt;'06' and status='7' and <br />
serviceid not in (select serviceorderid from cbq_qf_usage1&nbsp; where status&lt;&gt;'3' and status &lt;&gt; '8') <br />
group by 'XJ'||1||'180','停机保号用户数',1,'200509',groupid ;</p>
<p>Execution Plan<br />
----------------------------------------------------------<br />
&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SELECT STATEMENT Optimizer=RULE<br />
&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp; SORT (GROUP BY)<br />
&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp; FILTER<br />
&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (FULL) OF 'CBQ_LCH_USAGE0'<br />
&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (FULL) OF 'CBQ_QF_USAGE1'</p>
<p>Elapsed: 13:48:26.85</p>
<p>调整：<br />
not in 改成not exists<br />
create index idx_serviceorderid on cbq_qf_usage1(serviceorderid) nologging;</p>
<p>select 'XJ'||1||'180','停机保号用户数',count(distinct serviceid),1,'200509',a.groupid <br />
from cbq_lch_usage0 a<br />
where a.subsidiaryid=1 and a.subid&lt;&gt;'02'&nbsp; and a.subid&lt;&gt;'06' and a.status='7' <br />
and not exists(select 1 from cbq_qf_usage1 b where status&lt;&gt;'3' and status&lt;&gt;'8' and a.serviceid=b.serviceorderid)<br />
group by 'XJ'||1||'180','停机保号用户数',1,'200509',a.groupid;</p>
<p>Execution Plan<br />
----------------------------------------------------------<br />
&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SELECT STATEMENT Optimizer=RULE<br />
&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp; SORT (GROUP BY)<br />
&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp; FILTER<br />
&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (FULL) OF 'CBQ_LCH_USAGE0'<br />
&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TABLE ACCESS (BY INDEX) OF 'CBQ_QF_USAGE1'<br />
&nbsp;&nbsp; 5&nbsp;&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INDEX (RANGE SCAN) OF 'IDX_SERVICEORDERID' </p>
<p>Elapsed: 00:00:01.36</p>
<p><br />
九．其他<br />
1．SELECT子句中避免使用 &#8216; * &#8216;<br />
当你想在SELECT子句中列出所有的COLUMN时,使用动态 SQL列引用 &#8216;*&#8217; 是一个方便的方法.不幸的是,这是一个非常低效的方法. 实际上,ORACLE在解析的过程中, 会将&#8217;*&#8217; 依次转换成所有的列名, 这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间. <br />
2．用TRUNCATE替代DELETE<br />
3．使用表的别名(Alias)<br />
当在SQL语句中连接多个表时, 请使用表的别名并把别名前缀于每个Column上.这样一来,就可以减少解析的时间并减少那些由Column歧义引起的语法错误.</p>
<p><br />
4.索引的等级<br />
&nbsp;一般情况索引等级如下：<br />
&nbsp;a) 等式比较比范围比较要高。<br />
&nbsp;b) 唯一性索引比非唯一性索引要高。<br />
&nbsp;c) 一般情况下单列索引等级要比复合索引高，但如果where子句中包含所&nbsp;&nbsp;有复合索引的字段，则复合索引等级高。<br />
&nbsp;例如：<br />
SELECT col1, ... <br />
FROM emp <br />
WHERE emp_name = 'GURRY' <br />
AND emp_no = 127 <br />
AND dept_no = 12 </p>
<p>Index1 (emp_name) <br />
Index2 (emp_no, dept_no, emp_name) <br />
ORACLE将使用索引Index2。</p>
<p>5.统计信息分析<br />
在现实当中，有关analyze分析有以下两种误区：</p>
<p>a) 只要对主要的或者关键的表格做分析即可。其实正确的应该是需要对所有涉及到的表格都做过分析。</p>
<p>b) 做一次分析后即可高枕无忧。事实上，一旦做过分析后，就应该定期更新这些统计信息，以保证统计信息的正确性。</p>
<p>6．Exists总比In快<br />
&nbsp;有许多人认为用Exists总比用In要快,这也是一个误区。有时用in反而比用Exists快。<br />
他们之间的区别如下：<br />
&nbsp;&nbsp;IN subquery，首先执行subquery，由subquery来驱动父查询。而Exists子查询则由父查询来驱动子查询。这就是两者之间的区别。<br />
&nbsp;所以如果子查询小的话，则可以采用in会快一些，如果子查询大的话，则采用exists会快一些。</p>
<p>7．&gt;与&gt;=<br />
&nbsp;大于或小于操作符一般情况下是不用调整的，因为它有索引就会采用索引查找，但有的情况下可以对它进行优化，如一个表有100万记录，一个数值型字段A，<br />
30万记录的A=0，30万记录的A=1，39万记录的A=2，1万记录的A=3。<br />
&nbsp;那么执行A&gt;2与A&gt;=3的效果就有很大的区别了，因为A&gt;2时ORACLE会先找出<br />
为2的记录索引再进行比较，而A&gt;=3时ORACLE则直接找到=3的记录索引。</p>
<p>8. 使用索引来避免排序<br />
&nbsp; 索引是排好序的，在某些情况下可以使用索引来避免排序。<br />
&nbsp; SELECT acc_name,&nbsp;acc_surname<br />
&nbsp; FROM account acct<br />
&nbsp; ORDER BY 1;</p>
<p>&nbsp; SELECT /*+ INDEX_ASC(acct acc_ndx1) */ acc_name,acc_surname<br />
&nbsp; FROM account acct;</p>
<p><br />
9.大对象操作<br />
&nbsp;<br />
a)Big Insert<br />
(1)direct insert(serial and parallel) <br />
insert /*+append*/into tab1 select * from tab2;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Insert /*+append parallel(emp,8)*/ into emp&nbsp; select * from emp_bak;<br />
(2)nologging<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; insert into tab1 nologging select * from tab2;<br />
&nbsp;&nbsp;&nbsp; (3)Large extent size<br />
&nbsp;&nbsp; &nbsp;&nbsp;更大的extent可以获得更好的insert性能。<br />
&nbsp;(5)Large rollback segment</p>
<p>b)Large Index Create<br />
&nbsp; 大的索引extent size值<br />
&nbsp;&nbsp;&nbsp; 大的Sort_area_size值<br />
&nbsp; 采用nologging<br />
&nbsp; 采用parallel <br />
&nbsp; 大的临时表空间</p>
<p>alter session sort_area_size=100000000;<br />
create index xxx on aa(ab) nologging parallel 2;</p>
<p>&nbsp;c)Large Delete<br />
分几次delete。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><br />
附录一<br />
Hint全集<br />
174. /*+ALL_ROWS*/</p>
<p>　　表明对语句块选择基于开销的优化方法,并获得最佳吞吐量,使资源消耗最小化.例如:<br />
SELECT /*+ALL+_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='CCBZZP';</p>
<p>　　175. /*+FIRST_ROWS*/</p>
<p>　　表明对语句块选择基于开销的优化方法,并获得最佳响应时间,使资源消耗最小化.例如:<br />
SELECT /*+FIRST_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='CCBZZP';</p>
<p>　　176. /*+CHOOSE*/</p>
<p>　　表明如果数据字典中有访问表的统计信息,将基于开销的优化方法,并获得最佳的吞吐量;表明如果数据字典中没有访问表的统计信息,将基于规则开销的优化方法;例如:<br />
SELECT /*+CHOOSE*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='CCBZZP';</p>
<p>　　177. /*+ RULE*/</p>
<p>　　表明对语句块选择基于规则的优化方法.例如:<br />
SELECT /*+ RULE */ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='CCBZZP';&nbsp; </p>
<p>　　178. /*+ FULL(TABLE)*/</p>
<p>　　表明对表选择全局扫描的方法.例如:<br />
SELECT /*+FULL(A)*/ EMP_NO,EMP_NAM FROM BSEMPMS A WHERE EMP_NO='CCBZZP';</p>
<p>　　179. /*+ROWID(TABLE)*/</p>
<p>　　提示明确表明对指定表根据ROWID进行访问.例如:<br />
SELECT /*+ROWID(BSEMPMS)*/ * FROM BSEMPMS WHERE ROWID&gt;='AAAAAAAAAAAAAA'<br />
AND EMP_NO='CCBZZP';</p>
<p>　　180. /*+CLUSTER(TABLE)*/ <br />
　<br />
　　提示明确表明对指定表选择簇扫描的访问方法,它只对簇对象有效.例如:<br />
SELECT /*+CLUSTER */ BSEMPMS.EMP_NO,DPT_NO FROM BSEMPMS,BSDPTMS<br />
WHERE DPT_NO='TEC304' AND BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;</p>
<p>181. /*+ INDEX(TABLE&nbsp;&nbsp; INDEX_NAME)*/<br />
/*+index(table ind_name) index(table ind_name)*/<br />
表明对表选择索引的扫描方法.例如:<br />
SELECT /*+INDEX(BSEMPMS SEX_INDEX) USE SEX_INDEX BECAUSE THERE ARE FEWMALE BSEMPMS */ FROM BSEMPMS WHERE SEX='M';</p>
<p>　　182. /*+INDEX_ASC(TABLE INDEX_NAME)*/</p>
<p>　　表明对表选择索引升序的扫描方法.例如:<br />
SELECT /*+INDEX_ASC(BSEMPMS PK_BSEMPMS) */ FROM BSEMPMS WHERE DPT_NO='CCBZZP';</p>
<p>　　183. /*+INDEX_COMBINE*/</p>
<p>　　为指定表选择位图访问路经,如果INDEX_COMBINE中没有提供作为参数的索引,将选择出位图索引的布尔组合方式.例如:<br />
SELECT /*+INDEX_COMBINE(BSEMPMS SAL_BMI HIREDATE_BMI)*/ * FROM BSEMPMS<br />
WHERE SAL&lt;5000000 AND HIREDATE</p>
<p>　　184. /*+INDEX_JOIN(TABLE INDEX_NAME)*/</p>
<p>　　提示明确命令优化器使用索引作为访问路径.例如:<br />
SELECT /*+INDEX_JOIN(BSEMPMS SAL_HMI HIREDATE_BMI)*/ SAL,HIREDATE<br />
FROM BSEMPMS WHERE SAL&lt;60000;</p>
<p>　　185. /*+INDEX_DESC(TABLE INDEX_NAME)*/</p>
<p>　　表明对表选择索引降序的扫描方法.例如:<br />
SELECT /*+INDEX_DESC(BSEMPMS PK_BSEMPMS) */ FROM BSEMPMS WHERE DPT_NO='CCBZZP';</p>
<p>　　186. /*+INDEX_FFS(TABLE INDEX_NAME)*/</p>
<p>　　对指定的表执行快速全索引扫描,而不是全表扫描的办法.例如:<br />
SELECT /*+INDEX_FFS(BSEMPMS IN_EMPNAM)*/ * FROM BSEMPMS WHERE DPT_NO='TEC305';</p>
<p>　　187. /*+ADD_EQUAL TABLE INDEX_NAM1,INDEX_NAM2,...*/</p>
<p>　　提示明确进行执行规划的选择,将几个单列索引的扫描合起来.例如:<br />
SELECT /*+INDEX_FFS(BSEMPMS IN_DPTNO,IN_EMPNO,IN_SEX)*/ * FROM BSEMPMS WHERE EMP_NO='CCBZZP' AND DPT_NO='TDC306';</p>
<p>　　188. /*+USE_CONCAT*/</p>
<p>　　对查询中的WHERE后面的OR条件进行转换为UNION ALL的组合查询.例如:<br />
SELECT /*+USE_CONCAT*/ * FROM BSEMPMS WHERE DPT_NO='TDC506' AND SEX='M';</p>
<p>　　189. /*+NO_EXPAND*/</p>
<p>　　对于WHERE后面的OR 或者IN-LIST的查询语句,NO_EXPAND将阻止其基于优化器对其进行扩展.例如:<br />
SELECT /*+NO_EXPAND*/ * FROM BSEMPMS WHERE DPT_NO='TDC506' AND SEX='M';</p>
<p>　　190. /*+NOWRITE*/</p>
<p>　　禁止对查询块的查询重写操作.</p>
<p>191. /*+REWRITE*/</p>
<p>　　可以将视图作为参数.</p>
<p>　　192. /*+MERGE(TABLE)*/</p>
<p>　　能够对视图的各个查询进行相应的合并.例如:<br />
SELECT /*+MERGE(V) */ A.EMP_NO,A.EMP_NAM,B.DPT_NO FROM BSEMPMS A (SELET DPT_NO<br />
,AVG(SAL) AS AVG_SAL FROM BSEMPMS B GROUP BY DPT_NO) Va WHERE A.DPT_NO=V.DPT_NO<br />
AND A.SAL&gt;V.AVG_SAL;</p>
<p>　　193. /*+NO_MERGE(TABLE)*/</p>
<p>　　对于有可合并的视图不再合并.例如:<br />
SELECT /*+NO_MERGE(V) */ A.EMP_NO,A.EMP_NAM,B.DPT_NO FROM BSEMPMS A (SELET DPT_NO<br />
,AVG(SAL) AS AVG_SAL FROM BSEMPMS B GROUP BY DPT_NO) V WHERE A.DPT_NO=V.DPT_NO<br />
AND A.SAL&gt;V.AVG_SAL;</p>
<p>　　194. /*+ORDERED*/</p>
<p>　　根据表出现在FROM中的顺序,ORDERED使ORACLE依此顺序对其连接.例如:<br />
SELECT /*+ORDERED*/ A.COL1,B.COL2,C.COL3 FROM TABLE1 A,TABLE2 B,TABLE3 C<br />
WHERE A.COL1=B.COL1 AND B.COL1=C.COL1;</p>
<p>　　195. /*+USE_NL(TABLE)*/</p>
<p>　　将指定表与嵌套的连接的行源进行连接,并把指定表作为内部表.例如:<br />
SELECT /*+ORDERED USE_NL(BSEMPMS)*/ BSDPTMS.DPT_NO,BSEMPMS.EMP_NO,BSEMPMS.EMP_NAM FROM BSEMPMS,BSDPTMS WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;</p>
<p>　　196. /*+USE_MERGE(TABLE)*/</p>
<p>　　将指定的表与其他行源通过合并排序连接方式连接起来.例如:<br />
SELECT /*+USE_MERGE(BSEMPMS,BSDPTMS)*/ * FROM BSEMPMS,BSDPTMS WHERE<br />
BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;</p>
<p>　　197. /*+USE_HASH(TABLE)*/</p>
<p>　　将指定的表与其他行源通过哈希连接方式连接起来.例如:<br />
SELECT /*+USE_HASH(BSEMPMS,BSDPTMS)*/ * FROM BSEMPMS,BSDPTMS WHERE<br />
BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;</p>
<p>　　198. /*+DRIVING_SITE(TABLE)*/</p>
<p>　　强制与ORACLE所选择的位置不同的表进行查询执行.例如:<br />
SELECT /*+DRIVING_SITE(DEPT)*/ * FROM BSEMPMS,DEPT@BSDPTMS WHERE BSEMPMS.DPT_NO=DEPT.DPT_NO;</p>
<p>　　199. /*+LEADING(TABLE)*/</p>
<p>　　将指定的表作为连接次序中的首表.</p>
<p>200. /*+CACHE(TABLE)*/</p>
<p>　　当进行全表扫描时,CACHE提示能够将表的检索块放置在缓冲区缓存中最近最少列表LRU的最近使用端例如:<br />
SELECT /*+FULL(BSEMPMS) CAHE(BSEMPMS) */ EMP_NAM FROM BSEMPMS;</p>
<p>　　201. /*+NOCACHE(TABLE)*/</p>
<p>　　当进行全表扫描时,CACHE提示能够将表的检索块放置在缓冲区缓存中最近最少列表LRU的最近使用端，例如:<br />
SELECT /*+FULL(BSEMPMS) NOCAHE(BSEMPMS) */ EMP_NAM FROM BSEMPMS;</p>
<p>　　202. /*+APPEND*/</p>
<p>　　直接插入到表的最后,可以提高速度.<br />
insert /*+append*/ into test1 select * from test4 ;</p>
<p>　　203. /*+NOAPPEND*/</p>
<p>　　通过在插入语句生存期内停止并行模式来启动常规插入.<br />
insert /*+noappend*/ into test1 select * from test4;</p>
<p>附录二<br />
STATSPACK包的使用指南<br />
1.oracle8.1.6开始引进statspack，statspack是诊断oracle性能的强有力的工具。<br />
2.安装前准备<br />
&nbsp;A.首先是系统参数的确认：<br />
job_query_processes：为了建立自动任务，执行数据收集，该参数要大于0<br />
time_statistics：为了收集操作系统计时信息等，需要将其设置为TRUE</p>
<p>B.建议最好是单独的为perfstat用户（即安装statspack要建的用户）单独建立数据表空间和临时表空间，数据表空间至少要有100M的空闲空间，否则创建statspack对象会失败，如果打算长期使用statspack，可以考虑建稍大些的数据表空间。<br />
3.安装 <br />
A.安装脚本<br />
安装的脚本所在目录是$ORACLE_HOME/rdbms/admin，在oracle8.1.6版本安装脚本是statscre.sql，之后 8.1.7版本开始就是spcreate.sql，安装所需用户在9i之前的需要internal或者拥有sysdba权限的用户，9i需要的用户是 sys（9i已经不存在internal用户了）<br />
执行安装脚本如下：<br />
SQL&gt; @$ORACLE_HOME/rdbms/admin/spcreate<br />
&nbsp;<br />
B. 在安装过程中，需要填写perfstat用户的密码，并且选择perfstat用户的数据表空间和临时表空间，安装完成之后，察看相应的.lis文件检查安装是否正确无误，有问题可以通过spdrop.sql完成statspack的卸载，重新运行spcreate.sql完成statspack的安装。</p>
<p>4.&nbsp; 测试<br />
最简单的statspack报告生成，运行两次statspack.snap，然后运行spreport.sql生成一个基于两个时间点的报告。如果是8.1.7.3之前版本的Oracle，需要修改spcpkg.sql，要将substr修改为substrb，如下位置：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; select l_snap_id<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , p_dbid<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , p_instance_number<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , substr(sql_text,1,31) ? substrb(sql_text,1,31)<br />
&nbsp;<br />
建立简单的statspack报告过程如下：<br />
SQL&gt; execute statspack.snap (i_snap_level=&gt;10)<br />
PL/SQL procedure successfully completed.<br />
SQL&gt; execute statspack.snap<br />
PL/SQL procedure successfully completed.<br />
SQL&gt; @$ORACLE_HOME/rdbms/admin/spreport<br />
&nbsp;<br />
Spreport的执行过程中会列出需要选择的快照，你需要填写该报告描述的开始和结束的快照序号，并填写报告的文件名，当然可以不填，使用默认的报告文件名，默认的会生成在目录$ORACLE_HOME/rdbms/admin中<br />
这样就可以验证statspack已经正确的安装完成了<br />
&nbsp;<br />
自动收集statspack快照<br />
正常在真正的环境下，我们是需要连续的采样一段时间，这样生成的statspack才能更好的反映系统的现状，我们是可以通过spauto.sql来自动收集数据的。<br />
&nbsp;<br />
主要可能会设计到修改如下部分的内容<br />
variable jobno number;<br />
variable instno number;<br />
begin<br />
&nbsp; select instance_number into :instno from v$instance;<br />
&nbsp; dbms_job.submit(:jobno, 'statspack.snap;', trunc(sysdate+1/24,'HH'), 'trunc(SYSDATE+1/24,''HH'')', TRUE, :instno);<br />
&nbsp; commit;<br />
end;<br />
/<br />
主要是修改1/24这个值，目前是一个小时自动收集一次数据，如果要改动为半个小时收集一次数据就修改为1/48,同理，进行或大或小的修改。<br />
&nbsp;<br />
执行后，可以在spauto.lis文件中看到当前自动收集数据的job号等信息。当想要生成statspack报告的时候，只要选择任何两个不跨越停机时间的快照序号就可以了。注意，statspack是不能跨越停机的。</p>
</div>
<img src ="http://www.blogjava.net/AstroQi/aggbug/251880.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/AstroQi/" target="_blank">Astro.Qi</a> 2009-01-19 13:16 <a href="http://www.blogjava.net/AstroQi/archive/2009/01/19/251880.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Oracle中表的连接及其调整</title><link>http://www.blogjava.net/AstroQi/archive/2009/01/19/251875.html</link><dc:creator>Astro.Qi</dc:creator><author>Astro.Qi</author><pubDate>Mon, 19 Jan 2009 04:58:00 GMT</pubDate><guid>http://www.blogjava.net/AstroQi/archive/2009/01/19/251875.html</guid><wfw:comment>http://www.blogjava.net/AstroQi/comments/251875.html</wfw:comment><comments>http://www.blogjava.net/AstroQi/archive/2009/01/19/251875.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/AstroQi/comments/commentRss/251875.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/AstroQi/services/trackbacks/251875.html</trackback:ping><description><![CDATA[<div class="art_txt">
<div>
<div>
<p><font style="font-size: 14px">只有对这些问题有了清晰的理解后，我们才能针对特定的查询需求选择合适的连接方式，开发出健壮的数据库应用程序。选择合适的表连接方法对SQL语句运行的性能有着至关重要的影响。下面我们就Oracle常用的一些连接方法及适用情景做一个简单的介绍。</font></p>
<p><font style="font-size: 14px"><font style="font-size: 16px"><strong>一、嵌套循环连接（Nested Loop）</strong> </font></font></p>
<p>嵌套循环连接的工作方式是这样的： </p>
<p>1、Oracle首先选择一张表作为连接的驱动表，这张表也称为外部表（Outer Table）。由驱动表进行驱动连接的表或数据源称为内部表（Inner Table）。 </p>
<p>2、提取驱动表中符合条件的记录，与被驱动表的连接列进行关联查询符合条件的记录。在这个过程中，Oracle首先提取驱动表中符合条件的第一条记录，再与内部表的连接列进行关联查询相应的记录行。在关联查询的过程中，Oracle会持续提取驱动表中其他符合条件的记录与内部表关联查询。这两个过程是并行进行的，因此嵌套循环连接返回前几条记录的速度是非常快的。在这里需要说明的是，由于Oracle最小的IO单位为单个数据块，因此在这个过程中 Oracle会首先提取驱动表中符合条件的单个数据块中的所有行，再与内部表进行关联连接查询的，然后提取下一个数据块中的记录持续地循环连接下去。当然，如果单行记录跨越多个数据块的话，就是一次单条记录进行关联查询的。 </p>
<p>3、嵌套循环连接的过程如下所示：</p>
<table style="width: 566px; height: 121px" cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="566" align="center" bordercolorlight="#000000" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>
            <p>NESTED LOOP
            &lt;Outer Loop&gt;
            &lt;Inner Loop&gt; </p>
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<p>我们可以看出这里面存在着两个循环，一个是外部循环，提取驱动表中符合条件的每条记录。另外一个是内部循环，根据外循环中提取的每条记录对内部表进行连接查询相应的记录。由于这两个循环是嵌套进行的，故此种连接方法称为嵌套循环连接。 </p>
<p>嵌套循环连接适用于查询的选择性强、约束性高并且仅返回小部分记录的结果集。通常要求驱动表的记录（符合条件的记录，通常通过高效的索引访问）较少，且被驱动表连接列有唯一索引或者选择性强的非唯一索引时，嵌套循环连接的效率是比较高的。比如下面这个查询是选用嵌套循环连接的典型例子：</p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#000000" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>
            <p>SQL&gt; select e.empno,e.ename,e.job,d.dname
            2 from emp e,dept d
            3 where e.deptno=d.deptno
            4 and e.empno=7900;</p>
            <p>EMPNO ENAME JOB DNAME
            ---------- ---------- --------- --------------
            7900 JAMES CLERK SALES</p>
            <p>Execution Plan
            ----------------------------------------------------------
            0   SELECT STATEMENT Optimizer=CHOOSE
            1 0 NESTED LOOPS
            2 1 TABLE ACCESS (BY INDEX ROWID) OF 'EMP'
            3 2 INDEX (UNIQUE SCAN) OF 'PK_EMP' (UNIQUE)
            4 1 TABLE ACCESS (BY INDEX ROWID) OF 'DEPT'
            5 4 INDEX (UNIQUE SCAN) OF 'PK_DEPT' (UNIQUE) </p>
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<p>在这个查询中，优化器选择emp作为驱动表，根据唯一性索引PK_EMP快速返回符合条件empno为7900的记录，然后再与被驱动表dept的 deptno关联查询相应的dname并最终返回结果集。由于dept表上面的deptno有唯一索引PK_DEPT，故查询能够快速地定位deptno 对应dname为SALES的记录并返回。 </p>
<p>嵌套循环连接驱动表的选择也是连接中需要着重注意的一点，有一个常见的误区是驱动表要选择小表，其实这是不对的。假如有两张表A、B关联查询，A表有1000000条记录，B表有10000条记录，但是A表过滤出来的记录只有10条，这时候显然用A表当做驱动表是比较合适的。因此驱动表是由过滤条件限制返回记录最少的那张表，而不是根据表的大小来选择的。 </p>
<p>在外连接查询中，如果走嵌套循环连接的话，那么驱动表必然是没有符合条件关联的那张表，也就是后面不加(+)的那张表。这是由于外连接需要提取可能另一张表没符合条件的记录，因此驱动表需要是那张我们要返回所有符合条件记录的表。比如下面这个查询，就是选择了emp表做为驱动表进行连接：</p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#000000" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>
            <p><a href="mailto:Roby@XUE">Roby@XUE</a>&gt; select emp.ename,dept.dname
            2  from emp,dept
            3  where emp.deptno=dept.deptno(+);
            ENAME      DNAME
            ---------- --------------
            SMITH
            ALLEN
            WARD       SALES
            JONES      RESEARCH
            MARTIN     SALES
            BLAKE      SALES
            CLARK      ACCOUNTING
            SCOTT      RESEARCH
            KING       ACCOUNTING
            TURNER     SALES
            ADAMS      RESEARCH
            JAMES      SALES
            FORD       RESEARCH
            MILLER     ACCOUNTING
            14 rows selected.
            Execution Plan
            ----------------------------------------------------------</p>
            <p>            |   0 | SELECT STATEMENT             |         |    14 |   308 |    15
            |   1 |  NESTED LOOPS OUTER          |         |    14 |   308 |    15
            |   2 |   TABLE ACCESS FULL          | EMP     |    14 |   126 |     3
            |   3 |   TABLE ACCESS BY INDEX ROWID| DEPT    |     1 |    13 |     1
            |*  4 |    INDEX UNIQUE SCAN         | DEPT_PK |     1 |       |     0</p>
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<p>嵌套循环连接返回前几行的记录是非常快的，这是因为使用了嵌套循环后，不需要等到全部循环结束再返回结果集，而是不断地将查询出来的结果集返回。在这种情况下，终端用户将会快速地得到返回的首批记录，且同时等待Oracle内部处理其他记录并返回。如果查询的驱动表的记录数非常多，或者被驱动表的连接列上无索引或索引不是高度可选的情况，嵌套循环连接的效率是非常低的。</p>
</div>
</div>
</div>
<p><font style="font-size: 14px"><strong>二、排序合并连接(Sort Merge)</strong> </font></p>
<p><font style="font-size: 14px">排序合并连接的方法非常简单。在排序合并连接中是没有驱动表的概念的，两个互相连接的表按连接列的值先排序，排序完后形成的结果集再互相进行合并连接提取符合条件的记录。相比嵌套循环连接，排序合并连接比较适用于返回大数据量的结果。以下为排序合并连接的例子：</font></p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#000000" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>
            <p><a href="mailto:Roby@XUE"><font style="font-size: 14px">Roby@XUE</font></a><font style="font-size: 14px">&gt; select emp.ename,dept.dname
            2 from emp,dept
            3 where emp.deptno=dept.deptno
            4 /</font></p>
            <p><font style="font-size: 14px">ENAME DNAME
            ---------- --------------
            CLARK ACCOUNTING
            KING ACCOUNTING
            MILLER ACCOUNTING
            JONES RESEARCH
            SCOTT RESEARCH
            FORD RESEARCH
            ADAMS RESEARCH
            TURNER SALES
            JAMES SALES
            WARD SALES
            MARTIN SALES
            BLAKE SALES</font></p>
            <p><font style="font-size: 14px">12 rows selected.</font></p>
            <p><font style="font-size: 14px">Execution Plan</font></p>
            <p><font style="font-size: 14px">---------------------------------------------------------------
            | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
            ---------------------------------------------------------------
            | 0 | SELECT STATEMENT |      | 12 | 264 | 8 (25)| 00:00:01 |
            | 1 | MERGE JOIN       |      | 12 | 264 | 8 (25)| 00:00:01 |
            | 2 | SORT JOIN        |      | 4  | 52  | 4 (25)| 00:00:01 |
            | 3 | TABLE ACCESS FULL| DEPT | 4  | 52  | 3 (0) | 00:00:01 |
            |* 4| SORT JOIN        |      | 12 | 108 | 4 (25)| 00:00:01 |
            |* 5| TABLE ACCESS FULL| EMP  | 12 | 108 | 3 (0) | 00:00:01 |</font></p>
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<p><font style="font-size: 14px">可以看得出来上述查询首先按dept、emp两张表的deptno先排序，然后排序好的结果集再进行合并连接返回最终的记录。 </font></p>
<p><font style="font-size: 14px">排序合并连接在数据表预先排序好的情况下效率是非常高的，也比较适用于非等值连接的情况，比如&gt;、&gt;=、&lt;=等情况下的连接（哈希连接只适用于等值连接）。由于Oracle中排序操作的开销是非常消耗资源的，当结果集很大时排序合并连接的性能很差，于是Oracle在7.3之后推出了新的连接方式——哈希连接。</font></p>
<p><font style="font-size: 14px"><strong>三、哈希连接（Hash join）</strong> </font></p>
<p><font style="font-size: 14px">哈希连接分为两个阶段，如下。 </font></p>
<p><font style="font-size: 14px">1、构建阶段：优化器首先选择一张小表做为驱动表，运用哈希函数对连接列进行计算产生一张哈希表。通常这个步骤是在内存（hash_area_size）里面进行的，因此运算很快。 </font></p>
<p><font style="font-size: 14px">2、探测阶段：优化器对被驱动表的连接列运用同样的哈希函数计算得到的结果与前面形成的哈希表进行探测返回符合条件的记录。这个阶段中如果被驱动表的连接列的值没有与驱动表连接列的值相等的话，那么这些记录将会被丢弃而不进行探测。关于哈希连接更深层次的原理可以参考Itpub上网友logzgh发表的&#8220;hash join算法原理&#8221;帖子（</font><a href="http://www.itpub.net/showthread.php?threadid=315494"><font style="font-size: 14px">http://www.itpub.net/showthread.php?threadid=315494</font></a><font style="font-size: 14px">）。 </font></p>
<p><font style="font-size: 14px">以下为哈希连接的一个例子：</font></p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#000000" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>
            <p><a href="mailto:Roby@XUE"><font style="font-size: 14px">Roby@XUE</font></a><font style="font-size: 14px">&gt; select /**//*+ use_hash(emp,dept) */ emp.ename,dept.dname
            2  from emp,dept
            3  where emp.deptno=dept.deptno;</font></p>
            <p><font style="font-size: 14px">ENAME      DNAME
            ---------- --------------
            WARD       SALES
            JONES      RESEARCH
            MARTIN     SALES
            BLAKE      SALES
            CLARK      ACCOUNTING
            SCOTT      RESEARCH
            KING       ACCOUNTING
            TURNER     SALES
            ADAMS      RESEARCH
            JAMES      SALES
            FORD       RESEARCH
            MILLER     ACCOUNTING</font></p>
            <p><font style="font-size: 14px">12 rows selected.</font></p>
            <p><font style="font-size: 14px">Execution Plan
            ---------------------------------------------------------------------------
            | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
            ---------------------------------------------------------------------------
            |   0 | SELECT STATEMENT   |      |    12 |   264 |     7  (15)| 00:00:01 |
            |*  1 |  HASH JOIN         |      |    12 |   264 |     7  (15)| 00:00:01 |
            |   2 |   TABLE ACCESS FULL| DEPT |     4 |    52 |     3   (0)| 00:00:01 |
            |*  3 |   TABLE ACCESS FULL| EMP  |    12 |   108 |     3   (0)| 00:00:01 |</font></p>
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<p><font style="font-size: 14px">在这个查询中优化器首先选择dept这张表为驱动表，对列deptno运算哈希函数构建一张哈希表，然后再对被驱动表emp的deptno列运算同样的哈希函数计算得到的结果进行探测，最终连接得出符合条件的记录。 </font></p>
<p><font style="font-size: 14px">同嵌套循环外连接一样，哈希循环外连接的驱动表同样是没有符合条件关联的那张表。如下述例子：</font></p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#000000" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre>
            <p><a href="mailto:Roby@XUE"><font style="font-size: 14px">Roby@XUE</font></a><font style="font-size: 14px">&gt; select /**//*+ use_hash(emp,dept) */ emp.ename,dept.dname
            2 from emp,dept
            3 where emp.deptno=dept.deptno(+);
            ENAME DNAME
            ---------- --------------
            MILLER ACCOUNTING
            KING ACCOUNTING
            CLARK ACCOUNTING
            FORD RESEARCH
            ADAMS RESEARCH
            SCOTT RESEARCH
            JONES RESEARCH
            JAMES SALES
            TURNER SALES
            BLAKE SALES
            MARTIN SALES
            WARD SALES
            ALLEN
            SMITH<br />
            </p>
            <font style="font-size: 14px"><font style="font-size: 14px">
            <p><font style="font-size: 14px">12 rows selected.</font></p>
            <p><font style="font-size: 14px">Execution Plan<br />
            </font></p>
            </font></font>
            <p>            --------------------------------------------------------------
            | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
            --------------------------------------------------------------
            | 0 | SELECT STATEMENT |      | 14 | 308 | 7 (15)| 00:00:01 |
            |* 1| HASH JOIN OUTER  |      | 14 | 308 | 7 (15)| 00:00:01 |
            | 2 | TABLE ACCESS FULL| EMP  | 14 | 126 | 3 (0) | 00:00:01 |
            | 3 | TABLE ACCESS FULL| DEPT | 4  | 52  | 3 (0) | 00:00:01 |
            --------------------------------------------------------------</font></p>
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<p><font style="font-size: 14px">哈希连接比较适用于返回大数据量结果集的连接。使用哈希连接必须是在CBO模式下，参数hash_join_enabled设置为true，且只适用于等值连接。从Oracle9i开始，哈希连接由于其良好的性能渐渐取代了原来的排序合并连接。</font></p>
<p><font style="font-size: 14px"><strong>四、跟表连接有关的几个HINT</strong> </font></p>
<p><font style="font-size: 14px">（1）use_nl（t1,t2）：表示对表t1、t2关联时采用嵌套循环连接。 （2）use_merge（t1,t2）：表示对表t1、t2关联时采用排序合并连接。 （3）use_hash（t1,t2）：表示对表t1、t2关联时采用哈希连接。 （4）leading(t)：表示在进行表连接时，选择t为驱动表。 （5）ordred：要求优化器按from列出的表顺序进行连接。 </font></p>
<p><font style="font-size: 14px">需要注意的是在Oracle使用hint时,如果SQL语句中表用别名的话,那么hint中必须使用表的别名,否则hint将不会生效。</font></p>
<img src ="http://www.blogjava.net/AstroQi/aggbug/251875.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/AstroQi/" target="_blank">Astro.Qi</a> 2009-01-19 12:58 <a href="http://www.blogjava.net/AstroQi/archive/2009/01/19/251875.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JDBC ORACLE CLOB (用分页思想解决兆级以上的字符流操作问题)</title><link>http://www.blogjava.net/AstroQi/archive/2008/07/10/214025.html</link><dc:creator>Astro.Qi</dc:creator><author>Astro.Qi</author><pubDate>Thu, 10 Jul 2008 09:05:00 GMT</pubDate><guid>http://www.blogjava.net/AstroQi/archive/2008/07/10/214025.html</guid><wfw:comment>http://www.blogjava.net/AstroQi/comments/214025.html</wfw:comment><comments>http://www.blogjava.net/AstroQi/archive/2008/07/10/214025.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/AstroQi/comments/commentRss/214025.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/AstroQi/services/trackbacks/214025.html</trackback:ping><description><![CDATA[<br />
import java.io.*;<br />
import java.util.*;<br />
import java.sql.*;<br />
&nbsp; <br />
public class ClobTest {<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; private static final String DRIVER = "oracle.jdbc.driver.OracleDriver";<br />
&nbsp;&nbsp;&nbsp; private static final String URL = "jdbc:oracle:thin:@127.0.0.1:1521:ora10g";<br />
&nbsp;&nbsp;&nbsp; private static final String USER = "sc";<br />
&nbsp;&nbsp;&nbsp; private static final String PASSWORD = "sc";<br />
&nbsp;&nbsp;&nbsp; private static Connection conn = null;<br />
&nbsp;&nbsp;&nbsp; private static Statement stmt = null;<br />
<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp; * 往数据库中插入一个新的CLOB对象<br />
&nbsp;&nbsp;&nbsp;&nbsp; */<br />
&nbsp;&nbsp;&nbsp; public static void save(BO obj) throws Exception {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* <span style="color: red;">一定要</span>设定不自动提交，否则抛出<span style="color: red;">ORA-01002: 读取违反顺序</span> */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; boolean defaultCommit = conn.getAutoCommit();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.setAutoCommit(false);<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stmt = conn.createStatement();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 插入一个空的CLOB对象 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stmt.executeUpdate("INSERT INTO TEST_CLOB VALUES ('1000', EMPTY_CLOB())");//<span style="color: red;">一定要</span>使用Oracle中的EMPTY_CLOB()函数<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stmt.close();//记得关掉我哦 :-)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stmt= null;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 查询此CLOB对象并锁定 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //stmt = conn.prepareStatement();//如果是PrepareStatement接口,<span style="color: red;">一定要</span>重新创建该对象,否则抛出<span style="color: red;">ORA-01006: 赋值变量不存</span><span style="color: red;">在</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ResultSet rs = stmt.executeQuery("SELECT CLOBCOL FROM TEST_CLOB WHERE ID='1000' FOR UPDATE");//<span style="color: red;">一定要</span>for update锁定该记录，否则抛出<span style="color: red;">ORA-22920: 未锁定含有 LOB 值的行</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (rs.next()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 取出此CLOB对象 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; oracle.sql.CLOB clob = (oracle.sql.CLOB)rs.getClob("CLOBCOL");<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 向CLOB对象中写入数据 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Writer out = clob.getCharacterOutputStream();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //out.write(new String(obj.getEmail()));//obj.getEmail()返回byte[]类型,但是当obj.getEmail()绝对大时,执行new String(byte[])时,JVM会抛出<span style="color: red;">内存</span><span style="color: red;">溢出</span>异常<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte[] emails = obj.getEmail();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ClobStreamHandler csh = new ClobStreamHandler(emails);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String[] arrx = csh.pagedClobStream();//要解决内存溢出异常，必须把绝对大的byte[]进行<span style="color: red;">分页</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (arrx != null){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; arrx.length; i++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.write(arrx[i]);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.flush();//要解决内存溢出异常,必须<span style="color: red;">一页一页</span>的flush()到数据库<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else out.write("");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 正式提交 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.commit();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 恢复原提交状态 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.setAutoCommit(defaultCommit);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (Exception ex) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 出错回滚 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.rollback();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw ex;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }  finally {相关关闭操作}<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp; * 修改CLOB对象（是在原CLOB对象基础上进行覆盖式的修改）<br />
&nbsp;&nbsp;&nbsp;&nbsp; *<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @param obj - 数据对象<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @throws java.lang.Exception<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @roseuid 3EDA04B60367<br />
&nbsp;&nbsp;&nbsp;&nbsp; */<br />
&nbsp;&nbsp;&nbsp; public static void modify(BO obj) throws Exception {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 设定不自动提交 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; boolean defaultCommit = conn.getAutoCommit();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.setAutoCommit(false);<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 查询CLOB对象并锁定 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ResultSet rs = stmt.executeQuery("SELECT CLOBCOL FROM TEST_CLOB WHERE ID='1000' FOR UPDATE");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (rs.next()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 获取此CLOB对象 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; oracle.sql.CLOB clob = (oracle.sql.CLOB)rs.getClob("CLOBCOL"); &nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 进行覆盖式修改 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Writer out = clob.getCharacterOutputStream();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte[] emails = obj.getEmail();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ClobStreamHandler csh = new ClobStreamHandler(emails);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String[] arrx = csh.pagedClobStream();//要解决内存溢出异常，必须把绝对大的byte[]进行<span style="color: red;">分页</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (arrx != null){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; arrx.length; i++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.write(arrx[i]);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.flush();//要解决内存溢出异常,必须<span style="color: red;">一页一页</span>的flush()到数据库<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else out.write("");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 正式提交 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.commit();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 恢复原提交状态 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.setAutoCommit(defaultCommit);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (Exception ex) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 出错回滚 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.rollback();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw ex;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }  finally {相关关闭操作}<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 恢复原提交状态 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.setAutoCommit(defaultCommit);<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp; * 替换CLOB对象（将原CLOB对象清除，换成一个全新的CLOB对象）<br />
&nbsp;&nbsp;&nbsp;&nbsp; *<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @param obj - 数据对象<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @throws java.lang.Exception<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @roseuid 3EDA04BF01E1<br />
&nbsp;&nbsp;&nbsp;&nbsp; */<br />
&nbsp;&nbsp;&nbsp; public static void replace(BO obj) throws Exception {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 设定不自动提交 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; boolean defaultCommit = conn.getAutoCommit();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.setAutoCommit(false);<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 清空原CLOB对象 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stmt.executeUpdate("UPDATE TEST_CLOB SET CLOBCOL=EMPTY_CLOB() WHERE ID='1000'");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 查询CLOB对象并锁定 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ResultSet rs = stmt.executeQuery("SELECT CLOBCOL FROM TEST_CLOB WHERE ID='1000' FOR UPDATE");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (rs.next()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 获取此CLOB对象 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; oracle.sql.CLOB clob = (oracle.sql.CLOB)rs.getClob("CLOBCOL");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 更新数据 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Writer out = clob.getCharacterOutputStream();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte[] emails = item.getEmail();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ClobStreamHandler csh = new ClobStreamHandler(emails);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String[] arrx = csh.pagedClobStream();//要解决内存溢出异常，必须把绝对大的byte[]进行<span style="color: red;">分页</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (arrx != null){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; arrx.length; i++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.write(arrx[i]);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.flush();//要解决内存溢出异常,必须<span style="color: red;">一页一页</span>的flush()到数据库<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else out.write("");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 正式提交 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.commit();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 恢复原提交状态 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.setAutoCommit(defaultCommit);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (Exception ex) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 出错回滚 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.rollback();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw ex;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } finally {相关关闭操作}<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp; * 读取CLOB对象<br />
&nbsp;&nbsp;&nbsp;&nbsp; */<br />
&nbsp;&nbsp;&nbsp; public static byte[] read() throws Exception {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 设定不自动提交 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; boolean defaultCommit = conn.getAutoCommit();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.setAutoCommit(false);<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 查询CLOB对象 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ResultSet rs = stmt.executeQuery("SELECT * FROM TEST_CLOB WHERE ID='1000'");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (rs.next()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 获取CLOB对象 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; oracle.sql.CLOB c= (oracle.sql.CLOB)rs.getClob("CLOBCOL");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (c != null){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; oracle.jdbc.driver.OracleClobInputStream is = (OracleClobInputStream) c.getAsciiStream();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte[] by = new byte[1024 * 200];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(is.read(by, 0, by.length) != -1){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; baos.write(by, 0, by.length);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; baos.flush();//<span style="color: red;">把数据写入内存</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; baos.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; is.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return baos.toByteArray();//<span style="color: red;">不会内存溢出了</span>,呵呵. 原因是把数据写入了内存,而不是JVM的内存管理区域<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (SQLException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else return new byte[0];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (Exception ex) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.rollback();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw ex;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } finally {相关关闭操作}<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 恢复原提交状态 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.setAutoCommit(defaultCommit);<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp; * 建立测试用表格<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @throws Exception<br />
&nbsp;&nbsp;&nbsp;&nbsp; */<br />
&nbsp;&nbsp;&nbsp; public static void createTables() throws Exception {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stmt.executeUpdate("CREATE TABLE TEST_CLOB (ID VARCHAR2(4), CLOBCOL CLOB)");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stmt.executeUpdate("CREATE TABLE TEST_BLOB (ID VARCHAR2(4), BLOBCOL BLOB)");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (Exception ex) {<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp; public static void main(String[] args) throws Exception {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 装载驱动,建立数据库连接 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class.forName(DRIVER);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn = DriverManager.getConnection(URL, USER, PASSWORD);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stmt = conn.createStatement();<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 建立测试表格 */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createTables();<br />
&nbsp;&nbsp;&nbsp; }<br />
} <br />
<br />
<br />
&nbsp;&nbsp;&nbsp; 对Clob字符流进行分页的算法：<br />
&nbsp;&nbsp;&nbsp; <br />
<div style="border-left-color: #cccccc; padding-top: 4px; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; background-color: #eeeeee; font-size: 13px; width: 98%; "><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #008080;">&nbsp;1</span>&nbsp;<span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;privy.astroqi.oracle.db.handler;<br />
</span><span style="color: #008080;">&nbsp;2</span>&nbsp;<span style="color: #000000;"><br />
</span><span style="color: #008080;">&nbsp;3</span>&nbsp;<span style="color: #008000;">/**</span><span style="color: #008000;"><br />
</span><span style="color: #008080;">&nbsp;4</span>&nbsp;<span style="color: #008000;">&nbsp;*&nbsp;<br />
</span><span style="color: #008080;">&nbsp;5</span>&nbsp;<span style="color: #008000;">&nbsp;*&nbsp;</span><span style="color: #808080;">@author</span><span style="color: #008000;">&nbsp;Astro&nbsp;Qi<br />
</span><span style="color: #008080;">&nbsp;6</span>&nbsp;<span style="color: #008000;">&nbsp;*&nbsp;</span><span style="color: #808080;">@since</span><span style="color: #008000;">&nbsp;&nbsp;2008-07-23&nbsp;00:05<br />
</span><span style="color: #008080;">&nbsp;7</span>&nbsp;<span style="color: #008000;">&nbsp;*<br />
</span><span style="color: #008080;">&nbsp;8</span>&nbsp;<span style="color: #008000;">&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
</span><span style="color: #008080;">&nbsp;9</span>&nbsp;<span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;ClobStreamHandler&nbsp;{<br />
</span><span style="color: #008080;">10</span>&nbsp;<span style="color: #000000;"><br />
</span><span style="color: #008080;">11</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;PAGE_SIZE&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1024</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">200</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">;<br />
</span><span style="color: #008080;">12</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080;">13</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">byte</span><span style="color: #000000;">[]&nbsp;dataes;<br />
</span><span style="color: #008080;">14</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080;">15</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;length;<br />
</span><span style="color: #008080;">16</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080;">17</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;pageCount;<br />
</span><span style="color: #008080;">18</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080;">19</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;ClobStreamHandler(</span><span style="color: #0000ff;">byte</span><span style="color: #000000;">[]&nbsp;data){<br />
</span><span style="color: #008080;">20</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(data&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">){<br />
</span><span style="color: #008080;">21</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">throw</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;java.lang.IllegalArgumentException(</span><span style="color: #000000;">"</span><span style="color: #000000;">参数byte[]不能为空,否则无法处理接下来的操作.</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
</span><span style="color: #008080;">22</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080;">23</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080;">24</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataes&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;data;<br />
</span><span style="color: #008080;">25</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;length&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;dataes.length;<br />
</span><span style="color: #008080;">26</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pageCount&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(length&nbsp;</span><span style="color: #000000;">%</span><span style="color: #000000;">&nbsp;PAGE_SIZE&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">)&nbsp;</span><span style="color: #000000;">?</span><span style="color: #000000;">&nbsp;(length&nbsp;</span><span style="color: #000000;">/</span><span style="color: #000000;">&nbsp;PAGE_SIZE)&nbsp;:&nbsp;(length&nbsp;</span><span style="color: #000000;">/</span><span style="color: #000000;">&nbsp;PAGE_SIZE)&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">;<br />
</span><span style="color: #008080;">27</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080;">28</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080;">29</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080;">30</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;String[]&nbsp;pagedClobStream(){<br />
</span><span style="color: #008080;">31</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080;">32</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String[]&nbsp;arr&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;String[pageCount];<br />
</span><span style="color: #008080;">33</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080;">34</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">;&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;=</span><span style="color: #000000;">&nbsp;pageCount;&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)&nbsp;{<br />
</span><span style="color: #008080;">35</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;sheYuByte&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;length&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">&nbsp;(PAGE_SIZE&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;(i&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">));<br />
</span><span style="color: #008080;">36</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">byte</span><span style="color: #000000;">[]&nbsp;b&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
</span><span style="color: #008080;">37</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(sheYuByte&nbsp;</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;PAGE_SIZE){<br />
</span><span style="color: #008080;">38</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">byte</span><span style="color: #000000;">[PAGE_SIZE];<br />
</span><span style="color: #008080;">39</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br />
</span><span style="color: #008080;">40</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;{<br />
</span><span style="color: #008080;">41</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">byte</span><span style="color: #000000;">[sheYuByte];<br />
</span><span style="color: #008080;">42</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080;">43</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;j&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;j&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;b.length;&nbsp;j</span><span style="color: #000000;">++</span><span style="color: #000000;">){<br />
</span><span style="color: #008080;">44</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b[j]&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;dataes[(i&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">)&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;PAGE_SIZE&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;j];<br />
</span><span style="color: #008080;">45</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080;">46</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;arr[i&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">]&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;String(b);<br />
</span><span style="color: #008080;">47</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080;">48</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080;">49</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;arr;<br />
</span><span style="color: #008080;">50</span>&nbsp;<span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080;">51</span>&nbsp;<span style="color: #000000;">}</span></div>
<img src ="http://www.blogjava.net/AstroQi/aggbug/214025.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/AstroQi/" target="_blank">Astro.Qi</a> 2008-07-10 17:05 <a href="http://www.blogjava.net/AstroQi/archive/2008/07/10/214025.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>