2007年7月27日星期五

Install discoverer 4i in windows XP

需要调优一个工作簿,参考Metalink上的一篇文章,一种比较方便方法直接更改desktop注册表中的一个条目的值便可以进行诊断。由于是通过Citrix的方式访问Discoverer Desktop,并没有Citrix那台主机的管理员权限,所以想在自己的机器上安装Discoverer Desktop。

下面是安装的过程:
1. 下载Discoverer Desktop4.1.37。
下载Discoverer Desktop Edition, Version 4.1.37 for MS Windows 95/98/NT/2000。

2. 安装Discoverer Desktop4.1.37。
接压安装文件,右健点击Setup.exe,点击“Properties”菜单,在弹出的属性窗口中切换到“Compatibility”页。选中“Compatibility mode”的复选框,然后选择“Windows 2000”作为兼容模式。点击确定。


运行Setup.exe文件安装。

如果以前的机器没有安装Oracle相关产品,可以直接跳到步骤3。由于我的机器原来安装有Oracle 9i,如果在安装的时候新建立一个Oracle Home,安装会提示让你必须选在Oracle 9i所对应的Oracle Home。这个时候,如果选择Oracle 9i对应的Oracle Home,安装会提示目录已经存在就是不让你继续。

在网上找到答案,根本的原因就是安装数据库的时候,没有使用DEFAULT_HOEM。有两种方法。
方法一,删除Oracle数据库,现安装Discoverer,再安装数据库,其实不简单。
方法二,修改注册表中。找到注册表中的健值,HKEY_LOCAL_MACHINE/SOFTWARE/ORACLE/ALL_HOMES,建立一个new key,并建立对应的String value(NAME,NLS_LANG,PATH-等)。例如:NAME: Oracle_Default,NLS_LANG :NA,PATH: C:\Oracle_Default其中PATH为自己定义的DEFAULT目录。

将ALL_HOMES下的Oracle_Default的值改为刚才上面定义的“Oracle_Default”。原来的值为Oracle9i的Oracle Home。

运行Oracle Home Selector,选中Oracle_Default。
然后再运行安装程序,选中Oracle_Default进行安装。

3. 安装补丁3201601,将版本升级到4.1.48.6.0。
同样需要更改兼容性。如果不升级,连接EBS的时候,会出现用户名和密码错误的提示,从而不能登录。

参考:Metalink:Note:365285.1

2007年7月26日星期四

EBS表定时分析

EBS中默认定义了并发程序对数据库对象的进行分析,从而对基于Cost的优化器能够有效的工作。这些并发程序主要通过调用程序包FND_STATS来完成。而实际上,这几个并发程序并不是很方便。例如Gather Schema Statistics算是比较方便的一个,一次可以对数据库中的某个Schema下的对象进行分析。但是如果Oracle中有这么多的Schema,需要提交很多次请求才能完成。实际中,通过自己编写的并发程序来完成批量多个Schema下对象的分析。

Gather Table Statistics
Backup Table Statistics
Restore Table Statistics
Gather Schema Statistics
Gather Column Statistics


步骤:
1. 通过SQL*PLUS程序,SPOOL实际饱含分析语句的fnd_stats_run.sql文件,输出到一个应用的目录下,例如$FND_TOP。在这个文件中,对每一个需要分析的对象,都有如下三行语句组成,例如,对于ABM.ABM_BATCH_CALCS:

EXEC DBMS_STATS.DELETE_TABLE_STATS(ownname => 'ABM', tabname => 'ABM_BATCH_CALCS');
ANALYZE TABLE ABM.ABM_BATCH_CALCS ESTIMATE STATISTICS SAMPLE 1 ROWS;
EXEC FND_STATS.GATHER_TABLE_STATS(ownname=>'ABM',tabname=>'ABM_BATCH_CALCS',percent=>30,degree=>1,granularity=>'DEFAULT');

所以,在这个SQL*PLUS中,可以自己编写逻辑确定哪些Schema下的对象需要分析,根据表的记录数,确定分析的百分比等。

2. 将步骤1种程序定义成并发程序FND STATS PRODUCE。

3. 将步骤1中输出到$FND_TOP目录下的SQL*PLUS程序fnd_stats_run.sql,定义成并发程序FND STATS RUN。

4. 定义请求集,将上述两个请求加入请求集。并定义,只有当FND STATS PRODUCE运行成功之后,才运行FND STATS RUN。

5. 最后,定时运行请求集。

在ITPub上提问调查,到底要不要使用系统自带并发程序,意见还真多,FYI:
http://www.itpub.net/822695.html

2007年7月25日星期三

XML Publisher的Excel输出文件字段前面的所有零不丢失

在Itpub上看到问题,Metalink上正好有人问到这个问题,测试通过。

问题:
请教在Oracle XML Publisher,如何解决Excel输出文件自动使前面的所有零不丢失?现在用Excel输出文件时,如果某一个值,如001,会只显示1,自动使前面的所有零丢失。暂时只能加一个单引号,如‘001,避免此情况,但不是好办法,用户需要做修正工作,量多了挺讨厌的。如加一个不可见字符,XML文件报错。有否一个不可见字符,可以使XML文件不报错,或其它办法,以实现上述目的?

方法:
In the rtf template, use the “Insert -> Symbol” function to insert a very small0xA0 (NO-BREAK SPACE) character (font size 1 or 2pt) in front of the number string field.

另外,尝试用Oracle以及Word自带的formate是否都没有办法,Oracle的formate语法支持数字和日期。

参考:
Metalink:Note:417811.1
http://www.itpub.net/820447.html

2007年7月24日星期二

publish image with xml publisher in ebs

本文通过一个例子,展示怎样在EBS使用XMLP输出报表,并在报表中输出存储在数据库Blob字段中的图片。下面将整个开发的过程简略描述一下, EBS 版本为11.5.10.2。
1) 建立数据表。
2) 载入数据。
3) 编写数据编码的函数。
4) 编写报表。
5) 注册并发请求。
6) 运行并发请求,得到用于开发的临时XML数据文件。
7) 开发模板文件。
8) 注册XMLP的数据和模板文件。
9) 运行检查结果。


例子以及拷屏请参考Itpub帖子:http://www.itpub.net/815723.html

已经在Itpub发布,获得一个精华,高兴!

2007年7月11日星期三

Error:Function not available to this responsibility

新环境克隆出来,用户发现对于所有的客户化Form都不能访问,提示的错误为:
Function not available to this responsibility. Change responsibilities or contact you System Administrator.

按照Responsibility,Menu,Function检查,相关定义都没有更改。对比了另外一个环境,发现,发现应用层的服务器上相关Costumer的TOP目录都没有在环境中设置。通过echo $CUSTOMER_TOP,输出为空。所以,当通过Function去找Form的时候,就会遇到如上的错误。

回头想想,问题本应该这样:
1) Form和Report在不同的tie上,客户化的Report都没有问题,只有客户化的Form有问题。
2) 客户化的Menu指向一个客户化的Function(Function对应客户化Form)有问题,如果指向系统标准的Function(例如:Standard Request Submit)没有问题。

2007年7月8日星期日

XML Publisher

已经在itpub中看到不少的关于xml publisher的文章了,在网上已经找到一个非常好入门例子:
XML Publisher Concurrent Program - XMLP

2007年7月5日星期四

《Expert one on one Oracle》- 表- 笔记-4

嵌套表

类似于父/子表,但是有较大的差别。其中一个就是,在嵌套表中,父表中有多少行,就有多少嵌套表。使用嵌套表的两种方法:PL/SQL以及存储机制。
创建,查询,更新嵌套表的例子:

SQL> create or replace type emp_type
2 as object
3 (empno number(4),
4 ename varchar2(10),
5 job varchar2(9),
6 mgr number(4),
7 hiredate date,
8 sal number(7, 2),
9 comm number(7, 2)
10 );
11 /
Type created.

SQL> create or replace type emp_tab_type
2 as table of emp_type
3 /
Type created.

SQL> create table dept_and_emp
2 (deptno number(2) primary key,
3 dname varchar2(14),
4 loc varchar2(13),
5 emps emp_tab_type
6 )
7 nested table emps store as emps_nt;
Table created.

SQL> alter table emps_nt add constraint emps_empno_unique
2 unique(empno)
3 /
Table altered.

SQL> insert into dept_and_emp--插入嵌套表
2 select dept.*,
3 CAST( multiset( select empno, ename, job, mgr, hiredate, sal, comm
4 from emp
5 where emp.deptno = dept.deptno ) AS emp_tab_type )
6 from dept
7 /
5 rows created.
--multiset告诉Oracle子查询可能返回多行
--CAST告诉Oracle将返回结果作为一个集合


SQL> insert into table--插入嵌套子表
2 ( select emps from dept_and_emp where deptno = 10 )
3 values
4 ( 1234, 'NewEmp', 'CLERK', 7782, sysdate, 1200, null );
1 row created.

SQL> delete from table--删除嵌套子表
2 ( select emps from dept_and_emp where deptno = 20 )
3 where ename = 'SCOTT';
1 row deleted.

SQL> select d.dname, e.empno, ename--查询嵌套子表
2 from dept_and_emp d, table(d.emps) e
3 where d.deptno in ( 20 );

DNAME EMPNO ENAME
-------------- ---------- ----------
RESEARCH 7369 SMITH
RESEARCH 7566 JONES
RESEARCH 7876 ADAMS
RESEARCH 7902 FORD


SQL> update--更新嵌套子表
2 table( select emps
3 from dept_and_emp
4 where deptno = 10
5 )
6 set comm = 100
7 /
3 rows updated.

SQL> update
2 table( select emps
3 from dept_and_emp
4 where deptno = 1
5 )
6 set comm = 100
7 /
update
*
ERROR at line 1:
ORA-22908: reference to NULL table value

通过exp/imp可以查看到嵌套表的创建语句。通过运行如下的语句,可以进一步得到:

SQL> SELECT SQL_TEXT FROM V$SQL WHERE UPPER(SQL_TEXT) LIKE
2 '%EMP%';
……
SELECT /*+NESTED_TABLE_GET_REFS+*/ NESTED_TABLE_ID,SYS_NC_ROWINFO$ FROM "SCOTT"."EMPS_NT"

--执行上述查询出来的SQL语句
SQL> SELECT /*+NESTED_TABLE_GET_REFS+*/ NESTED_TABLE_ID,SYS_NC_ROWINFO$
2 from
3 "SCOTT"."EMPS_NT";

--得到表EMPS_NT中两列的值
NESTED_TABLE_ID
--------------------------------
SYS_NC_ROWINFO$(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM)
--------------------------------------------------------------------------------

A704A65FA4D14709807E86B8741D5ED7
EMP_TYPE(7782, 'CLARK', 'MANAGER', 7839, '09-JUN-81', 2450, 100)

A704A65FA4D14709807E86B8741D5ED7
EMP_TYPE(7839, 'KING', 'PRESIDENT', NULL, '17-NOV-81', 5000, 100)

A704A65FA4D14709807E86B8741D5ED7
EMP_TYPE(7934, 'MILLER', 'CLERK', 7782, '23-JAN-82', 1300, 100)

实际上,这两列NESTED_TABLE_ID,SYS_NC_ROWINFO$被隐藏。NESTED_TABLE_ID列是父表实际的外健,SYS_NC_ROWINFO$列,让ORACLE将一列看作一个对象。通过这种方式,ORACLE通过系统产生的主健和外健实现父子表。可以查看系统为父表创建的主健:

SQL> select name
2 from sys.col$
3 where obj# = ( select object_id
4 from user_objects
5 where object_name = 'DEPT_AND_EMP')
6 /

NAME
------------------------------
DEPTNO
DNAME
LOC
EMPS
SYS_NC0000400005$

系统为父表创建了一列SYS_NC0000400005$,为子表的外健,通过如下查询可以确定:

SQL> select SYS_NC0000400005$ from dept_and_emp WHERE SYS_NC0000400005$ = 'A704A
65FA4D14709807E86B8741D5ED7';

SYS_NC0000400005$
--------------------------------
A704A65FA4D14709807E86B8741D5ED7

从而通过NESTED_TABLE_GET_REFS可以单独查询子表,例如:
SQL> select /*+ nested_table_get_refs */ empno, ename from emps_nt where ename l
ike '%AR%';

EMPNO ENAME
---------- ----------
7782 CLARK
7521 WARD
7654 MARTIN


通过如上查询结果,嵌套表的物理结构为:
如果EXP/IMP嵌套表,那么可以发现嵌套表的定义中有RETURN AS VALUE因子,这表示会将潜套表行全部返回给应用的客户端。可以改写为RETURN AS LOCATOR,那么只是将指针方会给应用的客户端。

同时,从上面的例子可以看到,嵌套子表系统创建的主健实际上是没有索引的,而在访问这个子表的时候,ORACLE总是需要关联这个字段,所以最好为这个字段创建索引。更进一步,最好的方式将嵌套子表建立成索引组指表。

临时表

创建临时表时并不分配存储参数。当运行的时候,一个SESSION第一次将数据存入临时表,一个临时段将会创建。每个SESSION都有自己的临时段。

ORACLE的临时表应该预先创建了,只是通常情况下它们都没有数据,直到SESSION存入数据。有两种类型的临时表,基于SESSION的/基于事务的。

create global temporary table temp_table_session
on commit preserve rows--on commit delete rows
as
select * from scott.emp where 1=0
/

临时表的限制:
a. 不能定义参考完整性约束
b. 不能有VARRAY和NESTED TABLE类型的列
c. 不能是IOT/分区表/索引(HASH)CLUSTER表
d. 不能通过ANALYZE产生统计信息

针对最后一条,如果需要临时表在运行的时候有参考的统计信息,可以采用如下的方式:
a. 创建与临时表一样的普通表(表名,数据量都相同),创建索引
b. 对表和索引进行分析
c. 将统计信息导出

begin
dbms_stats.create_stat_table( ownname => USER,
stattab => 'STATS');

dbms_stats.export_table_stats( ownname => USER,
tabname => 'TEMP_ALL_OBJECTS',
stattab => 'STATS');

dbms_stats.export_index_stats( ownname => USER,
indname => 'TEMP_ALL_OBJECTS_IDX',
stattab => 'STATS');
end;
/

d. 删除普通表,创建临时表
e. 导入步骤c中导出的统计信息

begin
dbms_stats.import_table_stats( ownname => USER,
tabname => 'TEMP_ALL_OBJECTS',
stattab => 'STATS');
dbms_stats.import_index_stats( ownname => USER,
indname => 'TEMP_ALL_OBJECTS_IDX',
stattab => 'STATSʹ');
end;
/

对象表

对象表和嵌入表类似,ORACLE为了维持对象表,在普通表上增加了许多隐藏的东西。其实,其最终还是转换成关系型数据库的行和列。所以,对于物理存储的表而言,倾向于不使用对象表,因为不会知道背后隐藏的机制究竟会产生什么样的副作用。当然在存储过程,程序开发中使用是一个不错的选择。例如,用对象视图来同步对象与数据库数据。

SQL> create table people_tab
2 ( name varchar2(30) primary key,
3 dob date,
4 home_city varchar2(30),
5 home_street varchar2(30),
6 home_state varchar2(2),
7 home_zip number,
8 work_city varchar2(30),
9 work_street varchar2(30),
10 work_state varchar2(2),
11 work_zip number
12 )
13 /
Table created.

SQL> create or replace type address_type
2 as object
3 ( city varchar2(30),
4 street varchar2(30),
5 state varchar2(2),
6 zip number
7 )
8 /
Type created.

SQL> create or replace type person_type
2 as object
3 ( name varchar2(30),
4 dob date,
5 home_address address_type,
6 work_address address_type
7 )
8 /
Type created.

SQL> create view people of person_type
2 with object identifier (name)
3 as
4 select name,dob,
5 address_type(home_city,home_street,home_state,home_zip) home_a
6 address_type(work_city,work_street,work_state,work_zip) work_a
7 from people_tab
8 /
View created.

SQL> insert into people values ( 'Tom','15-mar-1965',
2 address_type( 'Reston','123 Main Street','Va','45678' ),
3 address_type( 'Redwood','1 Oracle Way','Ca','23456' ) );
1 row created.

SQL> select name, p.home_address.city from people p;
NAME HOME_ADDRESS.CITY
------------------------------ ------------------------------
Tom Reston

SQL> select name from people_tab;
NAME
------------------------------
Tom

2007年7月3日星期二

《Expert one on one Oracle》- 表- 笔记-3

聚簇表

Cluster就是将拥有相同列的一些表存储在同一个数据块上,将相关数据存储在一起。例如:值为a的行都存储在同一个数据块上。Cluster不同于IOT,是以堆的形式存储。

看一个Cluster的定义:
create cluster emp_dept_cluster
( deptno number(2) )
size 1024
/
Size指定有1024byte的数据每个cluster的key值相关。对于8k的block而言,大概每个块将会存储7个cluster keys。当插入的值不在这些cluster keys中,那么将会插入新的块。Size影响cluster使用的最重要的参数,这个值太大,会造成存储空间的浪费,值太小,会影响到造成过多数据链(类似于IOT的overflow)。

create index emp_dept_cluster_idx
on cluster emp_dept_cluster
/
这条数据的主要工作就是根据cluster的key值,返回块中包含key值的地址。Key实际上是主健。首先会查找cluster key,然后决定block的地址,从而读取数据。

create table dept_clu
( deptno number(2) primary key,
dname varchar2(14),
loc varchar2(13)
)
cluster emp_dept_cluster(deptno)
/

create table emp_clu
( empno number primary key,
ename varchar2(10),
job varchar2(9),
mgr number,
hiredate date,
sal number,
comm number,
deptno number(2) references dept(deptno)
)
cluster emp_dept_cluster(deptno)
/
和普通表相同,除了多了一个关键词cluster,告诉Oracle哪一列映射到cluster的cluster key。

begin
for x in ( select * from scott.dept )
loop
insert into dept_clu
values ( x.deptno, x.dname, x.loc );
insert into emp_clu
select *
from scott.emp
where deptno = x.deptno;
end loop;
end;
/
如果通过如下两个SQL查询,可以发现这两个表的数据都存储在同一个数据块上。
SELECT ROWID, deptno, DBMS_ROWID.rowid_block_number (ROWID) BLOCK
FROM dept_clu;

SELECT ROWID, deptno, DBMS_ROWID.rowid_block_number (ROWID) BLOCK
FROM emp_clu;

上述的例子是插入dept_clu的值然后再插入emp_clu表中和这个值关联的值,为什么不先完全插入完成dept_clu,然后再插入emp_clu呢?如果这样,由于dept_clu中dept中行很小,根据size设置为1024,每块就能存储下7个cluster key相关的行。当完成dept_clu的插入之后,再插入emp_clu。由于emp_clu的行比较大,可能针对一个cluster key,超过1024byte的大小,从而造成了过多的数据链。而采用上述方式载入数据,就会以更紧凑的方式存储数据,而一定是每块7个cluster key。

分别以两种方式载入数据进行对比:
SQL> create table dept_clu
2 ( deptno number(2) primary key,
3 dname varchar2(14),
4 loc varchar2(13)
5 )
6 cluster emp_dept_cluster(deptno)
7 /

Table created.

SQL> create table emp_clu
2 ( empno number primary key,
3 ename varchar2(10),
4 job varchar2(9),
5 mgr number,
6 hiredate date,
7 sal number,
8 comm number,
9 deptno number(2) references dept(deptno),
10 data char(1000) default '*'--与前面不同,这里更改了行大小
11 )
12 cluster emp_dept_cluster(deptno)
13 /
Table created.


--以顺序的方式插入
SQL> insert into dept_clu select * from scott.dept;
5 rows created.

SQL> insert into emp_clu select emp.*,'*' from scott.emp;
14 rows created.

--Highlight部分显示超过半数emp的block与dept不在同一个block
SQL> select dbms_rowid.rowid_block_number(dept_clu.rowid) dept_rid,
2 dbms_rowid.rowid_block_number(emp_clu.rowid) emp_rid,
3 dept_clu.deptno
4 from emp_clu, dept_clu
5 where emp_clu.deptno = dept_clu.deptno
6 /

DEPT_RID EMP_RID DEPTNO
---------- ---------- ----------
18 20 10
18 19 10
18 19 10

18 18 20
18 18 20
18 20 20
18 19 20
18 19 20

18 18 30
18 18 30
18 18 30
18 18 30
18 19 30
18 19 30

14 rows selected.

--以交叉的方式插入
SQL> begin
2 for x in ( select * from scott.dept )
3 loop
4 insert into dept_clu
5 values ( x.deptno, x.dname, x.loc );
6 insert into emp_clu
7 select emp.*,'x'
8 from scott.emp
9 where deptno = x.deptno;
10 end loop;
11 end;
12 /
PL/SQL procedure successfully completed.

--Highlight部分显示只有两个emp的block与dept不在同一个block
SQL> select dbms_rowid.rowid_block_number(dept_clu.rowid) dept_rid,
2 dbms_rowid.rowid_block_number(emp_clu.rowid) emp_rid,
3 dept_clu.deptno
4 from emp_clu, dept_clu
5 where emp_clu.deptno = dept_clu.deptno
6 /

DEPT_RID EMP_RID DEPTNO
---------- ---------- ----------
19 19 30
19 19 30
19 19 30
19 19 30
19 19 30
19 19 30
20 20 20
20 20 20
20 20 20
20 18 20
20 18 20

20 20 10
20 20 10
20 20 10
14 rows selected.

什么情况下不使用cluster?

a. 更新比较频繁的表。
b. 需要经常全表扫描的表。因为可能需要扫描更多的表。
c. 需要频繁truncate的表。

什么情况下使用cluster?

a. 数据主要用来读取,通过索引读取,join比较频繁的表。

Hash Cluster Table

在Hash Cluster Table中,对于一行得健值,Oracle通过内部或者外部函数进行三列(hash)计算,从而决定着一行应该在磁盘位置。采用Hash算法的一个副作用是,如果没有表上建立索引,不能对hash cluster中的表进行range scan。例如:对于select * from emp where deptno between 10 and 20,如果表emp在hash cluster中,那么将会是全表扫描。

在Index Cluster Table中,空间是动态分配的,而在hash cluster table中,空间预先分配的。空间的计算公式为:(HASHKEYS/trunc(blocksize/SIZE))。

散列表有冲突值也是可以OK的。冲突的值被散列表聚簇在一起,这也是散列表的特性。这也是为什么Oracle需要指定HASHKEY和SIZE。当然,要避免那些没有预期的冲突。例如:指定HASHKEY为1000(实际应该为1009,取最接近素数),如果在这个表中放入1100个部分,那么至少就会存在一个冲突。

通过一个SHOW_SPACE的存储过程看,创建一个HASH_CLUSTER,然后查看其空间使用的情况。
SQL> create or replace
2 procedure show_space
3 ( p_segname in varchar2,
4 p_owner in varchar2 default user,
5 p_type in varchar2 default 'TABLE',
6 p_partition in varchar2 default NULL )
7 as
8 l_free_blks number;
9
10 l_total_blocks number;
11 l_total_bytes number;
12 l_unused_blocks number;
13 l_unused_bytes number;
14 l_LastUsedExtFileId number;
15 l_LastUsedExtBlockId number;
16 l_last_used_block number;
17 procedure p( p_label in varchar2, p_num in number )
18 is
19 begin
20 dbms_output.put_line( rpad(p_label,40,'.')
21 p_num );
22 end;
23 begin
24 dbms_space.free_blocks
25 ( segment_owner => p_owner,
26 segment_name => p_segname,
27 segment_type => p_type,
28 partition_name => p_partition,
29 freelist_group_id => 0,
30 free_blks => l_free_blks );
31
32 dbms_space.unused_space
33 ( segment_owner => p_owner,
34 segment_name => p_segname,
35 segment_type => p_type,
36 partition_name => p_partition,
37 total_blocks => l_total_blocks,
38 total_bytes => l_total_bytes,
39 unused_blocks => l_unused_blocks,
40 unused_bytes => l_unused_bytes,
41 last_used_extent_file_id => l_LastUsedExtFileId,
42 last_used_extent_block_id => l_LastUsedExtBlockId,
43 last_used_block => l_last_used_block );
44
45 p( 'Free Blocks', l_free_blks );
46 p( 'Total Blocks', l_total_blocks );
47 p( 'Total Bytes', l_total_bytes );
48 p( 'Unused Blocks', l_unused_blocks );
49 p( 'Unused Bytes', l_unused_bytes );
50 p( 'Last Used Ext FileId', l_LastUsedExtFileId );
51 p( 'Last Used Ext BlockId', l_LastUsedExtBlockId );
52 p( 'Last Used Block', l_last_used_block );
53 end;
54 /

Procedure created.

SQL> create cluster hash_cluster
2 ( hash_key number )
3 hashkeys 1000
4 size 8192
5 /

Cluster created.

SQL> exec show_space('HASH_CLUSTER',user,'CLUSTER');

PL/SQL procedure successfully completed.

SQL> set serveroutput on;
SQL> exec show_space('HASH_CLUSTER',user,'CLUSTER');
Free Blocks.............................0
Total Blocks............................1024
Total Bytes.............................8388608
Unused Blocks...........................14
Unused Bytes............................114688
Last Used Ext FileId....................12
Last Used Ext BlockId...................1033
Last Used Block.........................114

PL/SQL procedure successfully completed.

可以看到,为这个一共分配了1024个块,其中有14个未使用的,1个块用于管理extends,所以共有1024-14-1共有1009个块在HWM下。1009也是1000之后下一个素数。所以,ORACLE总共为这个表分配了8192*1009空间。

刚创建一张 表的时候,HWM下的块的数量为0。然而创建一个HASH CLUSTER,由于ORACLE需要初始化每一个块,类似于将数据架载到这张表上,所以花费的时间较长。同时,对于HASH CLUSTER而言,数据的多少对于全表扫描花费同样多的时间。但是不用担心,创建HASH CLUSTER主要通过HASH KEY来查找,而不是全表扫描来查找。

对于数据相同的两张表,A表存入HASH CLUSTER,B表以堆表的方式存储。查询通常,A表的查询通常会消耗较多的CPU,B表的查询会消耗较多的内存。
什么情况下使用HASH CLUSTER
a. 在一定程度上知道表中行的数量或者上限,能够正确设置HASHKEY以及SIZE,避免重构。如果表生命周期很短,可以容易做到。
b. DML,特别是插入语句消耗资源很少,也不会产生太多负担,更新语句更新HASHKEY则不一样,会产生行迁移。
c. 适合经常需要访问的数据。

2007年7月1日星期日

INSERT堆表时,数据块写入的顺序

读到《Expert one on one Oracle》表部分,TOM说,对于堆表数据的插入,数据会被写到最适合的块,而不是以特定的顺序写入。

进行了测试,了解了FREELIST的部分结构,虽然还有一个疑问,记录于此,以后再解决。表空间非ASSM。

SQL> show parameters db_block_size--8k大小的 block
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
db_block_size integer 8192

SQL> create table t ( i number, a varchar2(4000), b varchar2(4000) );
Table created.

SQL> select FILE_ID, block_id, blocks from dba_extents where segment_name = 'T'
AND OWNER = 'SCOTT';--找到这个表所对应SEGMETN的头,对于8k数据块来说,数据块是从第9个块开始的
FILE_ID BLOCK_ID BLOCKS
---------- ---------- ----------
12 9 8

SQL> alter system dump datafile 12 block 9;--dump segment header
System altered.

nfl = 1, nfb = 1 typ = 1 nxf = 0 ccnt = 0
SEG LST:: flg: UNUSED lhd: 0x00000000 ltl: 0x00000000
End dump data blocks tsn: 13 file#: 12 minblk 9 maxblk 9
-- 根据nfl得知,有一个freelist,根据flag得知,这个freelist尚未使用

SQL> insert into t values ( 1, rpad('*',100,'*'), rpad('*',100,'*') );
1 row created.

SQL> SELECT ROWID, i, DBMS_ROWID.rowid_object (ROWID) OBJECT,
2 DBMS_ROWID.rowid_relative_fno (ROWID) file_no,
3 DBMS_ROWID.rowid_block_number (ROWID) BLOCK,
4 DBMS_ROWID.rowid_row_number (ROWID) rowno
5 FROM t;
ROWID I OBJECT FILE_NO BLOCK ROWNO
------------------ ---------- ---------- ---------- ---------- ----------
AAAHkEAAMAAAAAKAAA 1 30980 12 10 0

SQL> alter system dump datafile 12 block 10;--插入数据位于第10块,dump出来
System altered.

Block header dump: 0x0300000a
Object id on Block? Y
seg/obj: 0x7904 csc: 0x00.55c15a itc: 2 flg: O typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01
--根据flag得知,这个块在freelist上

SQL> alter system dump datafile 12 block 9;
System altered.

alter system dump datafile 12 block 9;
nfl = 1, nfb = 1 typ = 1 nxf = 0 ccnt = 1
SEG LST:: flg: USED lhd: 0x0300000a ltl: 0x0300000a
End dump data blocks tsn: 13 file#: 12 minblk 9 maxblk 9
--顺便看一下block9, flg标示已经变为USED, lhd的值指向block 10的

SQL> insert into t values ( 2, rpad('*',3900,'1'), rpad('*',3900,'1') );
1 row created.

SQL> SELECT ROWID, i, DBMS_ROWID.rowid_object (ROWID) OBJECT,
2 DBMS_ROWID.rowid_relative_fno (ROWID) file_no,
3 DBMS_ROWID.rowid_block_number (ROWID) BLOCK,
4 DBMS_ROWID.rowid_row_number (ROWID) rowno
5 FROM t;
ROWID I OBJECT FILE_NO BLOCK ROWNO
------------------ ---------- ---------- ---------- ---------- ----------
AAAHkEAAMAAAAAKAAA 1 30980 12 10 0
AAAHkEAAMAAAAALAAA 2 30980 12 11 0

SQL> alter system dump datafile 12 block 10;
System altered.

alter system dump datafile 12 block 10;
Block header dump: 0x0300000a
Object id on Block? Y
seg/obj: 0x7904 csc: 0x00.55c15a itc: 2 flg: - typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01
--flg的值为"-",表示当插入第二行的时候,block10已经从freelist中移掉。 为什么???block10有超过 8k*pctfree的空间啊?

SQL> alter system dump datafile 12 block 11;
System altered.
Block header dump: 0x0300000b
Object id on Block? Y
seg/obj: 0x7904 csc: 0x00.55c5b5 itc: 2 flg: O typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01
--block11这个时候已经加入freelist


结论:如果不是assm类型表空间,存在这种可能block不能被插入一个新的数据的时候将从 freelist中摘掉了

后续问题: oracle根据什么算法,将block10丛freelist移除? 不应该啊,因为block10有超过 8k*pctfree的空间。

参考:
在itpub的求助贴
Oracle Freelist和HWM原理及性能优化