再上一篇:10.6散列聚簇表
上一篇:10.7有序散列聚簇表
主页
下一篇:10.9临时表
再下一篇:10.10对象表
文章列表

10.8嵌套表

Oracle 9i 10g编程艺术:深入数据库体系结构

嵌 套表(nested table)是Oracle对象关系扩展的一部分。嵌套表是Oracle中的两种集合类型之 一,它与关系模型中传统的“父/子表对”里的子表很相似。这是 数据元素的一个无序集,所有数据元素 的数据类型都相同,可以是一个内置数据类型,也可以是一个对象数据类型。不过,还不仅如此,因为设 计嵌套表是为了制造 一个假象,好像父表中的每一行都有其自己的子表。如果父表中有100行,那么就有
100个虚拟的嵌套表。但实际来讲,物理上只有一个父表和一个子表。在嵌 套表和父/子表之间还存在一 些显著的语法和语义差别,这一节就会详细介绍这些内容。
使用嵌套表有两种方法。一种方法是在 PL/SQL代码中使用,用来扩展PL/SQL语言。另一种方法作为 一种物理存储机制,持久地存储集合。我个人总是在PL/SQL中使用嵌套表,而从未将嵌套表用作持久存储 机制。
在这一节中,我将简要地介绍创建、查询和修改嵌套表的语法。然后介绍一些实现细节,并说明关于 Oracle如何存储嵌套表有哪些要点需要知道。

10.8.1嵌套表语法

要创建一个有嵌套表的表相当简单,只是管理这个表的语法有点复杂。下面使用简单的EMP和DEPT 表来说明。我们对用关系模式实现的这个小数据模型已经很熟悉了,如下:

ops$tkyte@ORA10GR1> create table dept
2 (deptno number(2) primary key,
3 dname varchar2(14),
4 loc varchar2(13)
5 ); Table created.
ops$tkyte@ORA10GR1> create table emp
2 (empno number(4) primary key,
3 ename varchar2(10),
4 job varchar2(9),
5 mgr number(4) references emp,
6 hiredate date,
7 sal number(7, 2),
8 comm number(7, 2),
9 deptno number(2) references dept
10 ); Table created.
这里有主键和外键。下面建立与之对应的等价实现,不过这里EMP表实现为嵌套表:

ops$tkyte@ORA10GR1> 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.
ops$tkyte@ORA10GR1> create or replace type emp_tab_type
2 as table of emp_type
3 /
Type created.
要 创建一个带嵌套表的表,需要有一个嵌套表类型。以上代码创建了一个复杂的对象类型EMP_TYPE, 并创建了它的一个嵌套表类型 EMP_TAB_TYPE。在PL/SQL中,会像处理数组一样处理这种类型。在SQL中, 这会导致创建一个物理的嵌套表。以下简单的CREATE TABLE语句使用了这个类型:

ops$tkyte@ORA10G> 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.
ops$tkyte@ORA10G> alter table emps_nt add constraint
2 emps_empno_unique unique(empno)
3 /
Table altered.
这 个CREATE TABLE语句中重要的是:其中包括列EMPS(类型为EMP_TAB_TYPE),还有相应的 NESTED TABLE EMPS STORE AS EMPS_NT。这样除了表DEPT_AND_EMP之外,还会创建一个真正的物理表EMPS_NT, 这个表与DEPT_AND_EMP是分开的。我们的 嵌套表的EMPNO列上直接加了一个约束,使EMPNO 像在原来的 关系模型中一样是惟一的。利用嵌套表无法实现前面完整的数据模型:关系模型中存在自引用 约束,倘若 在嵌套表上增加同样的约束,则有:

ops$tkyte@ORA10G> alter table emps_nt add constraint mgr_fk
2 foreign key(mgr) references emps_nt(empno);
alter table emps_nt add constraint mgr_fk
*
ERROR at line 1:

ORA-30730: referential constraint not allowed on nested table column
这是不行的。嵌套表不支持引用完整性约束,因为它们不能引用任何表,甚至它们自己。因此,现在 先不管它。接下来,用现有的EMP和DEPT数据来填充这个表:

ops$tkyte@ORA10G> insert into dept_and_emp
2 select dept.*,
3 CAST( multiset( select empno, ename, job, mgr, hiredate, sal, comm
4 from SCOTT.EMP
5 where emp.deptno = dept.deptno ) AS emp_tab_type )
6 from SCOTT.DEPT
7 /
4 rows created. 这里有两点要注意:
q 只创建了“4”行。DEPT_AND_EMP表中确实只有4行。没有独立地存在14个EMP行。
q 这 个语法开始有点奇怪了。CAST和MULTISET是大多数人从来没有用过的语法。处理数据 库中的对象关系组件时,你会看到很多奇怪的语法。 MULTISET关键字用于告诉Oracle:这个 子查询返回多行(SELECT列表中的子查询先前限制为只返回一行)。CAST用于指示Oracle: 要 把返回的结果集处理为一个集合类型,在这里,我们将MULTISET强制转换(CAST)为一个 EMP_TAB_TYPE。CAST是一个通用的例程, 并不仅限于在集合中使用。例如,如果想从EMP中将 EMPNO列获取为VARCHAR2(20)而不是NUMBER(4)类型,可以使用以下查询:SELECT CAST(EMPNO AS VARCHAR2(20)) E FROM EMP。
现在可以查询数据了。下面来看一行是什么样子:

ops$tkyte@ORA10G> select deptno, dname, loc, d.emps AS employees
2 from dept_and_emp d
3 where deptno = 10
4 /
DEPTNO DNAME LOC EMPLOYEES(EMPNO, ENAME, JOB,
---------- -------------- ---------------- --------------------------
-------------------------
10 ACCOUNTING NEW YORK EMP_TAB_TYPE(EMP_TYPE(7782,
'MANAGER', 7839, '0
'CLARK',
81', 2450, NULL), EMP_
9-JUN-
9, 'KING', 'PRESIDEN

TYPE(783
'17-NOV-81', 5000,
T', NULL,
EMP_TYPE(7934, 'MILL
NULL),
'CLERK', 7782, '23-JAN-
ER',
1300, NULL))
82',
现 有数据都在这里,都放在一个利中。大多数应用都不能处理这个特殊的列,除非是专门针对对象 关系特性编写的。例如,ODBC没有办法处理嵌套表(JDBC、 OCI、Pro*C、PL/SQL和大多数其他API和语 言则可以)。针对这些情况,Oracle提供了一种方法,可以取消集合的嵌套,把它当成一个关系 表来处 理:

ops$tkyte@ORA10G> select d.deptno, d.dname, emp.*
2 from dept_and_emp D, table(d.emps) emp
3 /

DEPTNO DNAME EMPNO ENAME JOB MGR HIREDATE SAL COMM

------------ -------------------- ------------ ---------- ---------------- -----

--------------- -------- ----------

10 ACCOUNTING 7782 CLARK MANAGER 7839 09-JUN-81 2450

10 ACCOUNTING 7839 KING PRESIDENT 17-NOV-81 5000

10 ACCOUNTING 7934 MILLER CLERK 7782 23-JAN-82 1300

20 RESEARCH 7369 SMITH CLERK 7902 17-DEC-80 800

20 RESEARCH 7566 JONES MANAGER 7839 02-APR-81 2975

20 RESEARCH 7788 SCOTT ANALYST 7566 09-DEC-82 3000

20 RESEARCH 7876 ADAMS CLERK 7788 12-JAN-83 1100

20 RESEARCH 7902 FORD ANALYST 7566 03-DEC-81 3000

30 SALES 7499 ALLEN SALESMAN 7698 20-FEB-81 1600 3

00

30 SALES 7521 WARD SALESMAN 7698 22-FEB-81 1250 5

00

30 SALES 7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400

30 SALES 7698 BLAKE MANAGER 7839 01-MAY-81 2850

30 SALES 7844 TURNER SALESMAN 7698 08-SEP-81 1500

0

30 SALES 7900 JAMES CLERK 7698 03-DEC-81 950

14 rows selected.
可以把EMPS列强制转换为一个表,它会自然地为我们完成联结,这里不需要联结条件。实际上,由 于我们的EMP类型根本没有DEPTNO列,所以无法明确地在哪个列上完成联结。这些杂事都由Oracle为我 们做。
那么,怎么更新数据呢?假设我们想给部门10发$100的奖金。可以如下编写代码:

ops$tkyte@ORA10G> update
2 table( select emps
3 from dept_and_emp
4 where deptno = 10
5 )
6 set comm = 100
7 /
3 rows updated.
前 面说过“每一行都有一个虚拟的表”,这句话在这里就得到了应验。在前面所示的SELECT谓词中 , 每行有一个表可能还不太明显:特别是前面没有联结之类的 工作,所有看上去有点“神奇”。不过,UPDATE 语句能很清楚地显示出每行有一个表。我们选择一个具体的表来更新(UPDATE)——这个表没有名字, 只 是用一个查询来标识。如果使用的这个查询不是刚好选择(SELECT)一个表,我们就会得到以下错误:

ops$tkyte@ORA10G> 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
ops$tkyte@ORA10G> update
2 table( select emps
3 from dept_and_emp
4 where deptno > 1
5 )
6 set comm = 100
7 /
table( select emps
*
ERROR at line 2:
ORA-01427: single-row subquery returns more than one row
如 果返回至少一行(一个嵌套表实例也没有),更新就会失败。正常情况下,更新0行是可以的, 但在这里不行,它会返回一个错误,就好像我们的更新中漏写了表名 一样。如果返回了多行(不止一个嵌 套表实例),更新也会失败。正常情况下,更新多行是完全可以的。但是这里显示出,Oracle认为 DEPT_AND_EMP表中的每一行指向另一个表,而不是像关系模型中那样指定另外一个行集。
这 就是嵌套表和父/子关系表之间的语义差别。在嵌套表模型中,每个父行有一个表。而关系模型中 , 每个父行有一个行集。这种差别使用嵌套表有时使用起来有些麻 烦。考虑我们使用的这个模型,它从单个 部门的角度提供了一个很好的数据视图。如果我们想问“KIND为哪个部门工作?”“有多少在职的会计” 等待,这个模 型就很糟糕了。这些问题最好去问EMP关系表,而在嵌套表模型中,我们只能通过DEPT数 据来访问EMP数据。总是必须联结,而无法单独地查询EMP数 据。在这方面,Oracle没有提供公开支持 的方法(也没有相关的文档说明),但是我们可以使用一个技巧来完成(关于这个技巧,稍后还会更多地 介绍)。如 果需要更新EMPS_NT中的每一行,我们必须完成4个更新:分别更新DEPT_AND_EMP中的各行, 从而更新与之关联的虚拟表。
更 新部门10的员工数据时,还有考虑一个问题,我们是在语义上更新DEPT_AND_EMP表中的EMPS 列。要知道,尽管在物理上涉及两个表,但是语义上 只有一个表。即使我们没有更新部门表中的数据,包 含所修改嵌套表的行也会被锁定,不允许其他会话更新。传统的父/子表关系中则不是这样。
正是由于这些原因,我不主张把嵌套表用作一种持久存储机制。作为子表,如果不用单独查询,这种 情况实在是少见。在前面的例子中,EMP表应该是一个强实体。它是独立的,所以需要单独查询。我发现 一般都是这种情况,所以打算通过关系表上的视图来使用嵌套表。
既然我们了解了如何更新一个嵌套表实例,插入和删除就相当容易了。下面向嵌套表实例部门10增 加一行,再从部门20删除一行:

ops$tkyte@ORA10G> 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.
ops$tkyte@ORA10G> delete from table
2 ( select emps from dept_and_emp where deptno = 20 )
3 where ename = 'SCOTT';
1 row deleted.
ops$tkyte@ORA10G> select d.dname, e.empno, ename
2 from dept_and_emp d, table(d.emps) e
3 where d.deptno in ( 10, 20 );

DNAME

EMPNO

ENAME

--------------

----------

----------

ACCOUNTING

7782

CLARK

ACCOUNTING

7839

KING

ACCOUNTING

7934

MILLER

RESEARCH

7369

SMITH

RESEARCH

7566

JONES

RESEARCH

7876

ADAMS

RESEARCH

7902

FORD

ACCOUNTING

1234

NewEmp

8 rows selected.

这就是查询和修改嵌套表的基本语法。你往往会发现,必须像我们刚才那样取消这些表的嵌套(特别
是在查询中),才能使用这些嵌套表。一旦从概念上了解了“每行一个虚拟表”的概念,使用嵌套表就会 容易得多。
前 面我说过,“总是必须联结;而无法单独地查询EMP数据”,但是然后我又告诫说“如果你确实 需要,(利用一个技巧)这也是能办到的”。这种方法没有得到公 开支持,也没有相关的文档说明,所以 只能把它作为最后一个杀手锏。如果你确实需要大批量地更新嵌套表(记住,要利用联结通过 DEPT表来做
到),此时这种 方法才能最好地发挥作用。Oracle中有一个无文档说明的提示(只是简单地提了一下, 而没有充分地说明): NESTED_TABLE_GET_REFS,许多工具都使用了这个提示,如EXP和IMP就利用了这 个提示来处理嵌套表。利用这种方法还可以查看嵌套表 物理结构的更多信息。使用这个提示,可以完成查 询来得到一些“神奇”的结果。EXP(一个数据卸载工具)从嵌套表中抽取数据时就使用了以下查询:

ops$tkyte@ORA10G> SELECT /*+NESTED_TABLE_GET_REFS*/
2 NESTED_TABLE_ID,SYS_NC_ROWINFO$
3 FROM "OPS$TKYTE"."EMPS_NT"
4 /
NESTED_TABLE_ID SYS_NC_ROWINFO$(EMPNO, EN
-------------------------------- -----------------
--------
F60DEEE0FF7D7BC1E030007F01001321 EMP_TYPE(7782, 'CLARK', '
ER', 7839, '09-JUN-8
MANAG
2450, 100)
F60DEEE0FF7D7BC1E030007F01001321 EMP_TYPE(7839, 'KING', 'P
1',
ENT', NULL, '17-NOV-
RESID

5000, 100) ... 但是,如果你描述这个嵌套表,会有点奇怪: ops$tkyte@ORA10G> desc emps_nt
Name Null? Type
81',
----------------------------- -------- --------------------
EMPNO NUMBER(4) ENAME VARCHAR2(10)
JOB VARCHAR2(9) MGR NUMBER(4) HIREDATE DATE
SAL NUMBER(7,2) COMM NUMBER(7,2)
前 面查询的两列居然没有出现。它们是嵌套表隐藏实现的一部分。NESTED_TABLE_ID实际上是父表 DEPT_AND_EMP的一个外键。 DEPT_AND_EMP中确实有一个隐藏列,用于联结至EMPS_NT。
SYS_NC_ROWINFO$“列”是一个神奇的列;它更应该算是一个函数而 不是一个列。这里的嵌套表实际上是 一个对象表(由一个对象类型组成),而SYS_NC_ROWINFO$正是Oracle将行引用为对象所采用的内部方 法 , 而并非引用行的各个标量列。在底层,Oracle会用系统生成的主键和外键实现一个父/子表。如果更进一 步挖掘,可以查询“真正”的数据字典来查看 DEPT_AND_EMP表中的所有列:

sys@ORA10G> select name
2 from sys.col$
3 where obj# = ( select object_id
4 from dba_objects
5 where object_name = 'DEPT_AND_EMP'
6 and owner = 'OPS$TKYTE' )
7 / NAME
------------------------------
DEPTNO DNAME EMPS LOC
SYS_NC0000400005$ 从嵌套表中选出这一列,我们会看到下面的结果:

ops$tkyte@ORA10G> select SYS_NC0000400005$ from dept_and_emp;
SYS_NC0000400005$
-------------------------------- F60DEEE0FF887BC1E030007F01001321
F60DEEE0FF897BC1E030007F01001321
F60DEEE0FF8A7BC1E030007F01001321
F60DEEE0FF8B7BC1E030007F01001321
这 个列名看上去很古怪(SYS_NC0000400005$),这是放在DEPT_AND_EMP表中的系统生成的键。如 果更深层次地挖掘,会发现 Oracle已经在这个列上放上了惟一一个索引。不过,遗憾的是,它没有对 EMP_NT 中的NESTED_TABLE_ID加索引。这个列确实需要加索 引,因为我们总是要从DEPT_AND_EMP联结到EMPS_NT。 如果像刚才那样使用默认值,必须记住关于嵌套表的一个要点:一定要对嵌套表中的 NESTED_TABLE_ID加 索引!
不过现在我有些离题了,我要说的是如何把嵌套表当成一个真正的表来进行处理。 NESTED_TABLE_GET_REFS提示就为我们做了这个工作。可以使用如下的提示:

ops$tkyte@ORA10G> select /*+ nested_table_get_refs */ empno, ename
2 from emps_nt where ename like '%A%';
EMPNO ENAME
---------- ----------
7782 CLARK
7876 ADAMS
7499 ALLEN
7521 WARD
7654 MARTIN
7698 BLAKE
7900 JAMES
7 rows selected.
ops$tkyte@ORA10G> update /*+ nested_table_get_refs */ emps_nt
2 set ename = initcap(ename);
14 rows updated.
ops$tkyte@ORA10G> select /*+ nested_table_get_refs */ empno, ename
2 from emps_nt where ename like '%a%'; EMPNO ENAME
---------- ----------
7782 Clark
7876 Adams
7521 Ward
7654 Martin
7698 Blake
7900 James
6 rows selected.

再 次重申,这个特性没有相关的文档说明,没有得到公开支持。它是一个保障EXP和IMP工作的特 定功能,而且只有在EXP和IMP环境中才能保证这种方法一 定可行。你自己使用时会有风险,而且千万不 要用在生成代码中。实际上,如果你发现确实需要使用这个特性,那么从定义来讲,这说明根本就不应该 使用嵌套表! 这个结构对你完全不适合。可以用这种方法对数据完成一次性修正,或者你对嵌套表很好奇 , 可以通过这个技巧来看看嵌套表里有什么。要报告嵌套表中的数据,公 开支持的方法是消除嵌套,如下:

ops$tkyte@ORA10G> select d.deptno, d.dname, emp.*
2 from dept_and_emp D, table(d.emps) emp
3 / 查询和生产代码中应该使用这种方法。

10.8.2嵌套表存储

我们已经了解了嵌套表结构的一些存储问题。在这一节中,我们将输入地分析Oracle默认创建的结 构,并说明我们对这种结构能有怎样的控制。还是用前面的CREATE语句:

ops$tkyte@ORA10G> 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.
ops$tkyte@ORA10G> alter table emps_nt add constraint emps_empno_unique
2 unique(empno)
3 /
Table altered.
我们知道Oracle实际上会创建一个图10-11所示的结构。

图10-11 嵌套表的物理实现
这 个代码创建了两个实际的表。这里确实会创建我们请求创建的表,但是它有额外的一个隐藏列(默 认情况下,对于表中的每一个嵌套表列,都会有一个额外的隐藏 列)。它还在这个隐藏列上创建了一个惟 一约束。Oracle为我们创建了嵌套表EMPS_NT。这个表有两个隐藏列,其中的SYS_NC_ROWINFO $列并不真 正算是一个列,而应该是一个虚拟列,它会把所有标量元素返回为一个对象。另一个隐藏列是名为 NESTED_TABLE_ID的外键,通过这个外 键可以联结回父表。注意这个列上没有索引。最后,Oracle在 DEPT_AND_EMP表的DEPTNO列上增加了一个索引,以保证主键。所以,我们本 来只请求创建一个表,得到 的却远不止一个表。如果查看这个表,与创建父/子关系时所看到的情况非常相似,但是你要使用DEPTNO 上的现有主键作为 EMPS_NT的外键,而不是生成一个代理键RAW(16)。
如果查看嵌套表示例的DBMS_METADATA.GET_DDL转储信息,可以看到以下内容:

ops$tkyte@ORA10G> begin
2 dbms_metadata.set_transform_param
3 ( DBMS_METADATA.SESSION_TRANSFORM, 'STORAGE', false );
4 end;
5 /
PL/SQL procedure successfully completed.
ops$tkyte@ORA10G> select dbms_metadata.get_ddl( 'TABLE', 'DEPT_AND_EMP' ) from dual;

DBMS_METADATA.GET_DDL('TABLE','DEPT_AND_EMP')

------------------------------------------------------------------------------- CREATE TABLE "OPS$TKYTE"."DEPT_AND_EMP"

( "DEPTNO" NUMBER(2,0), "DNAME" VARCHAR2(14), "LOC" VARCHAR2(13),

"EMPS" "OPS$TKYTE"."EMP_TAB_TYPE" , PRIMARY KEY ("DEPTNO")

USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255

TABLESPACE "USERS" ENABLE

) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING TABLESPACE "USERS"

NESTED TABLE "EMPS" STORE AS "EMPS_NT"

(PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 LOGGING

TABLESPACE "USERS" ) RETURN AS VALUE


到此为止,只有一个新内容,即RETURN AS VALUE。 这个选项用于描述如何向客户应用返回嵌套表。
默认情况下,Oracle会按值把嵌套表返回给客户:具体数据会随各行传输。这个选项也可以设置为 RETURN AS LOCATOR,这说明客户会得到指向数据的一个指针,而不是数据本身。当且仅当客户对这个指针解除引 用(dereference)时,才会把数据传输给 客户。因此,如果你相信客户通常不会查看对应各个父行的嵌 套表(不会查看这些嵌套表中的行),就可以返回一个locator而不是值,这样可以节省网络往 返通信开 销。例如,如果你的客户应用要显示部门列表,当用户双击一个部门时,客户应用会显示出员工信息,你 就可以考虑使用locator。这是因为通常都 不会查看详细信息,查看详细信息的情况是例外,而不是一般 情况。

那 么,我们还能对嵌套表做些什么呢?首先,NESTED_TABLE_ID列必须建立索引。因为我们总是从 父表联结到子表来访问嵌套表,我们确实需要这个索 引。可以使用CREATE INDEX对该列建立索引,但是 最好的解决方案是使用一个IOT来存储嵌套表。嵌套表也是一个适用IOT的绝好例子。它会按 NESTED_TABLE_ID将子行物理地共同存储在一块(所以用最少的物理I/O就能完成表的获取),这样就不 必在RAW(16)列上建立冗余的索 引。再前进一步,由于NESTED_TABLE_ID就是IOT主键的第一列,还应该 加入索引键压缩来避免冗余的NESTED_TABLE_ID(否则会 重复存储)。另外,我们还可以在CREATE TABLE 命令中加入EMPNO列的UNIQUE和NOT NULL约束。因此,对于前面的CREATE TABLE,可以稍作修改,如下 所示:
ops$tkyte@ORA10G> CREATE TABLE "OPS$TKYTE"."DEPT_AND_EMP"
2 ("DEPTNO" NUMBER(2, 0),
3 "DNAME" VARCHAR2(14),
4 "LOC" VARCHAR2(13),
5 "EMPS" "EMP_TAB_TYPE")
6 PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 LOGGING
7 STORAGE(INITIAL 131072 NEXT 131072
8 MINEXTENTS 1 MAXEXTENTS 4096
9 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
10 BUFFER_POOL DEFAULT)
11 TABLESPACE "USERS"
12 NESTED TABLE "EMPS"
13 STORE AS "EMPS_NT"

14 ( (empno NOT NULL, unique (empno), primary key(nested_table_id,empno))
15 organization index compress 1 )
16 RETURN AS VALUE
17 /
Table created.
现在可以得到以下的一组对象。这里没有一个传统表EMP_NT,而是一个IOT EMPS_NT,由图10-12 中表上的索引结构指示。

图10-12嵌套表实现为IOT
如果EMPS_NT是一个使用压缩的IOT,它比原来默认嵌套表占用的存储空间更少,而且其中有我们非 常需要的索引。

10.8.3嵌套表小结

我自己并不把嵌套表用作一种持久存储机制,原因如下:
q 这样会增加不必要的RAW(16)列存储开销。父表和子表都有这个额外的列。父表对于其中 的各个嵌套表列都有一个额外的16字节RAW字段。由于父表通常已经有一个主键(在我们这个 例子中就是DEPTNO),所以完全可以在子表中使用这个键,而不必使用一个系统生成的键。
q 这会在父表上增加另一个惟一约束(相应地带领不必要的开销),而父表中通常已经有一 个惟一约束。
q 如果不使用NESTED_TABLE_GET_REFS(这个方法未得到公开支持),嵌套表本身使用起来 并不2.如果是查询,可以通过消除嵌套来访问嵌套表,但是如果是大批量更新,则无法简单地 消除嵌套。在实际中,表往往都会“自己”查询自己,我还没有见过这方面的例外。
不 过作为一个编程构造,我确实大量使用了嵌套表,并把嵌套表用于视图中。我认为这才是嵌套表 应有的位置。作为一个存储机制,我更倾向于我自己创建父/子表。 创建了父/子表之后,实际上,我们可 以再创建一个视图,使之看上去就好像我们有一个真正的嵌套表一样。也就是说,这样做可以得到嵌套表 构造的所有好处,而 不会引入嵌套表的开销。
如 果你确实把嵌套表用作一个存储机制,一定要保证将嵌套表建立为一个IOT,以避免 NESTED_TABLE_ID上索引的开销以及嵌套表本身的开销。请参 考前面有关IOT的一节,了解基于溢出段和 其他选项来建立IOT时有哪些建议。如果你没有使用IOT,则要确保在嵌套表的NESTED_TABLE_ID 列上创 建一个索引,来避免为查找子行而执行全表扫描。