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數據庫之間移動數據提供了一種簡單的方法。