Oracle数据库表类型sql
6.1 表的类型数据库
1. 堆组织表编程
2. 索引组织表数据结构
3. 聚簇表ide
4. 散列聚簇表函数
5. 嵌套表工具
6. 临时表性能
7. 对象表测试
8. 外部表优化
一张表最多有1000列;表的行数理论上没有限制;表上索引个数能够是列的全排列数,并且一次性可以使用32个;表的数量没有限制。
6.2 术语
高水位标记 High Water Mark: 曾经包含数据的最右边的块。在全表扫描时,Oracle将扫描高水标记一下的全部块,即便它们不含数据。TRUNCATE将从新设置高水标记。
自由列表 Freelist: 在Oracle中用来跟踪高水标记如下有空闲空间的块对象。保留在高水标记以上的块,只有Freelist为空时才能被用到。
并行更新数据时,配置多个Freelist能提升总体性能,代价是增长了存储空间。
6.3 堆组织表
应 用中99%(或者更多)的状况下使用的可能都是堆组织表,不过随着IOT的出现,这种情况之后可能会有所改观,由于IOT自己就能够加索引。执行 CREATE TABLE语句时,默认获得的表类型就是堆组织表。若是你想要任何其余类型的表结构,就须要在CREATE语句自己中指定它。
堆 (heap)是计算机科学领域中获得深刻研究的一种经典数据结构。它实际上就是一个很大的空间、磁盘或内存区(固然,这里所说的磁盘是指数据库表的相应磁 盘),会以一种显然随机的方式管理。数据会放在最合适的地方,而不是以某种特定顺序来放置。许多人但愿能按数据放入表中的顺序从表中取出数据,可是对于 堆,这是没法保证的。
6.4 索引组织表
数据在IOT中根据主键存储和排序。IOT特别适用于IR(信息检索)、空间和OLAP应用程序。
IOT名义上是表,但它们的段其实是索引段。要显示空间使用等就要先把IOT表的名字转换成潜在的索引名。默认值是SYS_IOT_TOP_<object_id>,object_id是为表分配的内部对象ID。推荐在建表时指定索引名。
主要应用
对只包含主键列的表:使用堆组织表将有100%多的额外开销;
1. 构建本身的索引结构:例如本身实现一个提供大小写不敏感查询的相似函数索引
CREATE TABLE emp AS SELECT * FORM scott.emp;
CREATE TABLE upper_name
(x$ename,x$rid,
PRIMARY KEY(x$ename,x$rid)
)
ORGANIZATION INDEX
AS
SELECT UPPER(ename),ROWID FROM emp;
CREATE OR REPLACE TRIGGER upper_ename
AFTER INSERT OR UPDATE OR DELETE ON emp
FOR EACH ROW
BEGIN
IF (UPDATING AND (:OLD.ename||'x'<>:NEW.ename||'x'))
THEN
DELETE FROM upper_name
WHERE x$ename=UPPER(:OLD.ename)
AND x$rid=:OLD.rowid;
INSERT INTO upper_ename(x$ename,x$rid) VALUES (UPPER(:NEW.ename),:NEW.rowid);
ELSIF (INSERTING)
THEN
INSERT INTO upper_ename(x$ename,x$rid) VALUES (UPPER(:NEW.ename),:NEW.rowid);
ELSIF (DELETING)
THEN
DELETE FROM upper_name
WHERE x$ename=UPPER(:OLD.ename)
AND x$rid=:OLD.rowid;
END IF;
END;
2. 须要增强数据的共同定位或但愿数据按特定的顺序物理存储时
对应Sybase和SQL Server用户,这种状况会采用聚簇索引,而这可能达到110%的额外开销,而IOT没有。常常用BETWEEN对主键或者惟一键进行查询,则会下降I/O数量。
主要选项
NOCOMPRESS/COMPRESS N
压缩N列,即对其中前N列相同的值进行压缩。从而可以容许更多数据进入Buffer Cache,代价是略多的CPU能量。
OVERFLOW PCTTHRESHOLD N/INCLUDING column_name
索引段的存储要密集于普通数据段(每块的行数要多),通常PCTUSED是没有意义的。而OVERFLOW子句容许设置另外一个段以容许IOT中的行数据太大时溢出的这个段中。它再次引入PCTUSED,这样PCTUSED和PCTFREE对OVERFLOW段有对于堆组织表中相同的含义。而使用方法是以下中的一种:
PCTTHRESHOLD--当行中数据超出此百分比,该行尾部的列溢出到溢出块;
INCLUDING--指定列以前的列均存入索引块,以后的列存入溢出块。
二次索引
只要主键是IOT,能够在索引中拥有索引。但不像其余通常索引,它不包含真正rowid(物理地址),而是基于主键IOT的逻辑rowid,做用稍小。对于IOT的二次索引访问实际有两个扫描执行(通常表只需一个扫描索引结构),一个在二次结构中,一个在IOT自己中。
索引组织表小结
在 创建IOT时,最关键的是适当地分配数据,即哪些数据存储在索引块上,哪些数据存储在溢出段上。对溢出条件不一样的各类场景进行基准测试,查看对 INSERT、UPDATE、DELETE和SELECT分别有怎样的影响。若是结构只创建一次,并且要频繁读取,就应该尽量地把数据放在索引块上(最 合适获取),要么频繁地组织索引中的数据(不适于修改)。堆
6.5 索引聚簇表
Oracle中聚簇是存储一组表的方法,而不是如同SQL Server、Sybase中那样(那是Oracle中的IOT)。概念上是经过聚簇码列将几张表"预链接",尽量将聚簇码列相同的几张表的行放入同一个块中。
CREATE CLUSTER emp_dept_cluster
(deptno NUMBER(2))
SIZE 1024;
CREATE INDEX emp_dept_cluster_idx
ON CLUSTER emp_dept_cluster;
CREATE TABLE dept
(deptno NUMBER(2) PRIMARY KEY,
dname VARCHAR2(14),
loc VARCHAR2(3)
)
CLUSTER emp_dept_cluster(deptno);
CREATE TABLE emp
(empno NUMBER PRIMARY KEY,
ename VARCHAR2(10),
...
deptno NUMBER(2) REFERENCES dept(deptno)
)
CLUSTER emp_dept_cluster(deptno);
BEGIN
FOR x IN(SELECT * FROM scott.dept)
LOOP
INSERT INTO dept VALUES(x.deptno,x.dname,x.loc);
INSERT INTO emp
SELECT * FROM scott.emp
WHERE deptno=x.deptno;
END LOOP;
END;
注意这里的插入方法,这将尽量保证每一个块中放置尽量多的聚簇码值,并让能够"预链接"的两个表中的值尽量在同一个块中。
DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid)可用于检查rowid所属块。
很容易发现dept和emp有重复的rowid,表和rowid能够惟一肯定行,rowid伪列只有在一张表中才是惟一的!
不使用聚簇的状况:
1.聚簇可能消极影响DML性能;
2.全扫描表的性能会受到影响--不只仅扫描一个表,而是对多个表全扫描;
3.聚簇中的表不能TRUNCATE。
6.6 散列聚簇表
概念相似索引聚簇表,但用散列函数代替了聚簇码索引。Oracle采用行的码值,使用内部函数或者自定义的函数进行散列运算,从而指定数据的存放位置。这样没有在表中增长传统的索引,所以不能Range Scan散列聚簇中的表,而只能全表扫描(除非单独创建索引)。
CREATE CLUSTER hash_cluster
(hash_key NUMBER)
HASHKEYS 1000
SIZE 8192;
索引聚簇须要空间时是动态分配,而散列聚簇表在建立时肯定了散列码数(HASHKEY)。Oracle采用第一个不小于HASHKEY的质数做为散列码数,将散列码数*SIZE就获得分配的空间(字节),可容纳HASHKEYS/TRUNC(BLOCKSIZE/SIZE)字节的数据。
性能上,散列聚簇表消耗较少I/O,较多CPU,所需执行时间较少,大致取决于CPU时间(固然可能要等待I/O,取决于配置)。
下列状况下使用散列聚簇表较为合适:
1. 在必定程度上精确知道整个过程当中表中记录行数或者合理的上限,以肯定散列码数;
2. 不大量执行DML,尤为是插入。更新不会产生显著的额外开销,除非更新HASHKEY,这样会致使行迁移;
3. 老是经过HASHKEY值访问数据。
6.7 嵌套表
两种使用嵌套表的方法:
1. PL/SQL代码中做为扩展PL/SQL语言;
2. 做为物理存储机制,以持久地存储集合。
嵌套表语法
建立嵌套表类型:
CREATE TABLE dept
(deptno NUMBER(2) PRIMARY KEY,
dname VARCHAR2(14),
loc VARCHAR2(13)
);
CREATE TABLE emp
(empno NUMBER(4) PRIMARY KEY,
ename VARCHAR2(10),
job VARCHAR2(9),
mgr NUMBER(4) REFERENCES emp,
hiredate DATE,
sal NUMBER(7, 2),
comm NUMBER(7, 2),
deptno NUMBER(2) REFERENCES dept
);
INSERT INTO dept SELECT * FROM scott.dept;
INSERT INTO emp SELECT * FROM scott.emp;
CREATE OR REPLACE TYPE emp_type
AS OBJECT
(empno NUMBER(4),
ename VARCHAR2(10),
job VARCHAR2(9),
mgr NUMBER(4),
hiredate DATE,
sal NUMBER(7, 2),
comm NUMBER(7, 2)
);
CREATE OR REPLACE TYPE emp_tab_type
AS TABLE OF emp_type;
使用嵌套表:
CREATE TABLE dept_and_emp
(deptno NUMBER(2) PRIMARY KEY,
dname VARCHAR2(14),
loc VARCHAR2(13),
emps emp_tab_type
)
NESTED TABLE emps STORE AS emps_nt;
能够在嵌套表上增长约束:
ALTER TABLE emps_nt ADD CONSTRAINT emps_empno_unique
UNIQUE(empno) ;
嵌套表不支持参照完整性约束,不能参考任何其余表甚至本身:
ALTER TABLE emps_nt ADD CONSTRAINT mgr_fk
FOREIGN KEY(mgr) REFERENCES emps_nt(empno);
会产生错误ORA-30730。
INSERT INTO dept_and_emp
SELECT dept.*,
CAST( MULTISET( SELECT empno, ename, job, mgr, hiredate, sal, comm
FROM emp
WHERE emp.deptno = dept.deptno ) AS emp_tab_type )
FROM dept;
MULTISET用来告诉Oracle子查询返回不止一行,CAST用来告诉Oracle将返回设置为一个集合类型。
查询时,嵌套表中的数据将在同一列中:
SELECT deptno, dname, loc, d.emps AS employees
FROM dept_and_emp d
WHERE deptno = 10;
Oracle一样提供方法去掉集合的嵌套,像关系型表同样处理(可以将EMPS列看成一个表,并天然链接且不须要链接条件):
SELECT d.deptno, d.dname, emp.*
FROM dept_and_emp D, TABLE(d.emps) emp;
按照"每行实际是一张表"的思想来更新:
UPDATE
TABLE( SELECT emps
FROM dept_and_emp
WHERE deptno = 10
)
SET comm = 100;
但若是返回SELECT emps FROM dept_and_emp WHERE deptno = 10少于一行,更新将失败(普通状况下更新0行是许可的),并返回ORA-22908错误--如同更新语句没有写表名同样;若是返回多于一行,更新也会失败,返回ORA-01427错误。这说明Oracle在使用了嵌套表后认为每一行指向另外一个表,而不是如同关系型模型那样认为是另外一个行集。
插入与删除的语法:
INSERT INTO TABLE
(SELECT emps FROM dept_and_emps WHERE deptno=10)
VALUES
(1234,'NewEmp','Clerk',7782,SYSDATE,1200,NULL);
DELETE FROM TABLE
(SELECT emps FROM dept_and_emps WHERE deptno=20)
WHERE ename='SCOTT';
通常而言,必须老是链接,而不能单独查询嵌套表(如EMPS)中的数据,可是若是确实须要,是能够的。提示NESTED_TABLE_GET_REFS被用于EXP和IMP处理嵌套表。
SELECT /*+NESTED_TABLE_GET_REFS+*/
NESTED_TABLE_ID, SYS_NC_ROWINFO$
FROM "TKYTE"."EMPS_NT";
而咱们察看EMPS_NT的表结构是看不到NESTED_TABLE_ID,SYS_NC_ROWINFO$两列的。对父表DEPT_AND_EMP来讲NESTED_TABLE_ID是一个外键。
使用这个提示就能够直接操做嵌套表了:
UPDATE /*+NESTED_TABLE_GET_REFS+*/ emps_nt
SET ename=INITCAP(ename);
嵌套表存储
上例中,现实产生了两张表:
DEPT_AND_EMP
deptno
NUMBER(2)
dname
VARCHAR2(14)
loc
VARCHAR2(13)
SYS_NC0000400005$
RAW(16)
EMPS_NT
SYS_NC_ROWINFO$
NESTED_TABLE_ID
RAW(16)
empno
NUMBER(4)
ename
VARCHAR2(10)
job
VARCHAR2(9)
mgr
NUMBER(4)
hiredate
DATE
sal
NUMBER(7,2)
comm
NUMBER(7,2)
默认状况下,每一个嵌套表列都产生一个额外的RAW(16)隐藏列,并在其上建立了惟一约束,用以指向嵌套表。而嵌套表中有两个隐藏列:SYS_NC_ROWINFO$是做为一个对象返回全部标量元素的一个伪列;另外一个NESTED_TABLE_ID的外键回指向父表。
能够看到真实代码:
CREATE TABLE TKYTE.DEPT_AND_EMP
(DEPTNO NUMBER(2,0),
DNAME VARCHAR2(14),
LOC VARCHAR2(13),
EMPS EMP_TAB_TYPE)
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 LOGGING
STORAGE(INITIAL 131072 NEXT 131072
MINEXTENTS 1 MAXEXTENTS 4096
PCTINCREASE 0 FREELISTS 1 FREELIST GROUP 1
BUFFER_POOL DEFAULT)
TABLESPACE USER
NESTED TABLE EMPS
STORE AS EMPS_NT
RETURN BY VALUE;
RETURN BY VALUE用来描述嵌套表如何返回到客户应用程序中。
NESTED_TABLE_ID列必须是索引的,那么较好的解决办法就是使用IOT存储嵌套表。
CREATE TABLE TKYTE.DEPT_AND_EMP
(DEPTNO NUMBER(2,0),
DNAME VARCHAR2(14),
LOC VARCHAR2(13),
EMPS EMP_TAB_TYPE)
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 LOGGING
STORAGE(INITIAL 131072 NEXT 131072
MINEXTENTS 1 MAXEXTENTS 4096
PCTINCREASE 0 FREELISTS 1 FREELIST GROUP 1
BUFFER_POOL DEFAULT)
TABLESPACE USER
NESTED TABLE EMPS
STORE AS EMPS_NT
((empno NOT NULL,
UNIQUE(empno),
PRIMARY KEY(nested_table_id,empno))
ORGANIZATION INDEX COMPRESS 1)
RETURN BY VALUE;
这样与最初默认的嵌套表相比,使用了较少的存储空间并有最须要的索引。
不使用嵌套表做为永久存储机制的缘由
1.增长了RAW(16)列的额外开销,父表和子表都将增长这个额外的列;
2.当一般已经有惟一约束时,父表上的惟一约束是额外开销;
3.没有使用不支持的结构(NESTED_TABLE_GET_REFS),嵌套表不容易使用。
通常推荐在编程结构和视图中使用嵌套表。若是要使用嵌套表做为存储机制,确保嵌套表是IOT,以免NESTED_TABLE_ID和嵌套表自己中索引的额外开销。
6.8 临时表
Oracle的临时表与其余数据库中的不一样,其定义是"静态"的。以事务(ON COMMIT DELETE ROWS)或者会话(ON COMMIT PRESERVE ROWS)为基础,只是说明数据的生命期,而在数据库中建立临时表一次,其结构老是有效的,被做为对象存在数据字典中了,这样也就容许对临时表创建视图、存储过程当中用静态SQL引用临时表等等。
在实际开发中,考虑到DDL是消耗较大的操做,应该避免在运行时操做,而是将应用程序须要的临时表在程序安装时就建立,而只是在存储过程当中简单的INSERT、SELECT。
临时表不支持的永久表的特性有:
1. 不能用参照完整性约束,也不能被参照完整性约束所引用;
2. 不能有VARRAY或者NESTED TABLE类型的列;
3. 不能是IOT;
4. 不能是索引或者散列聚簇;
5. 不能分区;
6. 经过ANALYZE命令不能产生统计信息,也便是说优化器在临时表上没有真正的统计功能。
因为缺乏统计功能,那么CBO(基于成本的优化器)的性能将受到极大的影响,所以应当尽量使用INLINE VIEW。
要让临时表拥有正确的统计信息,CBO产生正确的决策,能够先创建一张结构与临时表彻底相同的普通表:
CREATE TABLE temp_all_objects
AS
SELECT * FROM all_objects WHERE 1=0;
CREATE INDEX temp_all_objects_idx ON temp_all_objects(object_id);
选择插入表明性数据后进行分析:
...
ANALYZE TABLE temp_all_objects COMPUTE STATISTICS FOR ALL INDEX;
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,
tabname => 'TEMP_ALL_OBJECTS_IDX',
stattab => 'STATS');
END;
创建临时表:
DROP TABLE temp_all_objects;
CREATE GLOBAL TEMPORARY TABLE temp_all_objects
AS
SELECT * FROM all_objects WHERE 1=0;
导入正确的信息后CBO将使用这些信息决定执行模式:
CREATE INDEX temp_all_objects_idx ON temp_all_objects(object_id);
BEGIN
DBMS_STATS.IMPORT_TABLE_STATS(ownname => USER,
tabname => 'TEMP_ALL_OBJECTS',
stattab => 'STATS');
DBMS_STATS.IMPORT_INDEX_STATS(ownname => USER,
tabname => 'TEMP_ALL_OBJECTS_IDX',
stattab => 'STATS');
END;
6.8 对象表
基于类型(Type)建立的表,而不是做为列的集合。建立语法:
CREATE TABLE t OF some_type;
对于下例:
CREATE OR REPLACE TYPE address_type
AS OBJECT
(city VARCHAR2(30),
street VARCHAR2(30),
state VARCHAR2(2),
zip NUMBER
);
CREATE OR REPLACE TYPE person_type
AS OBJECT
(name VARCHAR2(30),
dob DATE,
home_address address_type,
work_address address_type
);
CREATE TABLE people OF person_type;
经过执行以下语句,能够看到数据库中实际存放的结构:
SELECT name,segcollength
FROM SYS.COL$
WHERE obj#=(SELECT object_id
FROM user_objects
WHERE object_name='PEOPLE');
PEOPLE
SYS_NC_OID$
16
SYS_NC_ROWINFO$
1
NAME
30
DOB
7
HOME_ADDRESS
1
SYS_NC00006$
30
SYS_NC00007$
30
SYS_NC00008$
2
SYS_NC00009$
22
WORK_ADDRESS
1
SYS_NC00011$
30
SYS_NC00012$
30
SYS_NC00013$
2
SYS_NC00014$
22
SYS_NC_OID$是系统为表产生的Object ID,RAW(16),其上有惟一性索引。它是一主键为基础,并非系统产生的,是一个伪列,且没有在硬盘上真正消耗空间;
SYS_NC_ROWINFO$相似于嵌套表中,可做为单独一列返回整行;
NAME, DOB是表中原有标量;
HOME_ADDRESS, WORK_ADDRESS可做为单个对象,返回所表明的列的集合;
SYS_NCnnnnn$是内嵌对象类型的标量实现。
外部表(external table)-- 很大程度上能够替代sqlload
这些表并不存储在数据库自己中,而是放在数据库以外,即放在日常的操做系统文件中。在Oracle9i及以上版本中,利用外部表能够查询数 据库以外的一个文件,就好像这个文件也是数据库中日常的表同样。外部表对于向数据库加载数据最有用(外部表是很是强大的数据加载工具)。Oracle 10g则更进一步,还引入了一个外部表卸载功能,在不使用数据库连接的状况下,这为在Oracle数据库之间移动数据提供了一种简单的方法。