Oracle11g增加了表的虛拟列,這個列的資料并沒有存儲在資料檔案中,而是Oracle通過列資料的生成放到了資料字典中。虛拟列的數值是通過真實列中的資料計算而來的。虛拟列的位置可以放在它參考的列的前面,也可以包括多個實際列的值,但是不能引用其他的虛拟列:
SQL> CREATE TABLE yu_test2 (c_vl_1 NUMBER, c_vl_2 AS (c_vl_1+1) );
Table created
SQL> CREATE TABLE yu_test2 (c_vl_1 NUMBER, c_vl_2 AS (c_vl_1+1), c_vl_3 AS (c_vl_2+1) );
CREATE TABLE yu_test2 (c_vl_1 NUMBER, c_vl_2 AS (c_vl_1+1), c_vl_3 AS (c_vl_2+1) )
ORA-54012: 在清單達式中引用了虛拟列
以上隻是一個簡單的虛拟列的例子,實際上虛拟列的完整寫法應該包括列名、資料類型、GENERATED ALWAYS關鍵字、AS加清單達式和VIRTUAL關鍵字。其中GENERATED ALWAYS和VIRTUAL為可選關鍵字,主要用于描述虛拟列的特性,寫與不寫沒有本質差別。而列的資料類型如果忽略,那麼Oracle會根據AS後面的表達式最終結果的資料類型來确定虛拟列的資料類型:
SQL> CREATE TABLE yu_test1 ( v_cl_1 VARCHAR2(30), v_cl_2 CHAR(50) GENERATED ALWAYS AS (LOWER(v_cl_1)) VIRTUAL );
Table created
SQL> desc yu_test1;
Name Type Nullable Default Comments
------ ------------ -------- --------------- --------
V_CL_1 VARCHAR2(30) Y
V_CL_2 CHAR(50) Y LOWER("V_CL_1")
虛拟列可以使用Oracle自帶的函數或使用者定義的函數,不過對于使用者定義的函數要求必須聲明函數的确定性(DETERMINISTIC),虛拟列必須是對實際列進行操作後的結果,不能使用沒有實際列當做入參的函數,也就是說,虛拟列必須和表字段有關聯:
SQL> CREATE OR REPLACE FUNCTION FUN_TEST1 RETURN NUMBER AS
2 BEGIN
3 RETURN 1;
4 END;
5 /
Function created
SQL> drop table yu_test1 purge;
Table dropped
SQL> CREATE TABLE yu_test2 (c_vl_1 NUMBER, c_vl_2 AS (FUN_TEST1) );
CREATE TABLE yu_test2 (c_vl_1 NUMBER, c_vl_2 AS (FUN_TEST1) )
ORA-54016: 指定了無效的清單達式
SQL> CREATE OR REPLACE FUNCTION FUN_TEST1 (c_in number) RETURN NUMBER AS
2 BEGIN
3 RETURN 1;
4 END;
5 /
Function created
SQL> CREATE TABLE yu_test2 (c_vl_1 NUMBER, c_vl_2 AS (FUN_TEST1(c_vl_1)) );
CREATE TABLE yu_test2 (c_vl_1 NUMBER, c_vl_2 AS (FUN_TEST1(c_vl_1)) )
ORA-30553: 函數不能确定
SQL>
SQL> CREATE OR REPLACE FUNCTION FUN_TEST1 (c_in number) RETURN NUMBER DETERMINISTIC AS
2 BEGIN
3 RETURN 1;
4 END;
5 /
Function created
SQL> CREATE TABLE yu_test2 (c_vl_1 NUMBER, c_vl_2 AS (FUN_TEST1(c_vl_1)) );
Table created
SQL>
DETERMINISTIC是必須的。
不過Oracle雖然在建立建立的時候會檢查函數的确定性,在表建立之後,卻可以将函數替換為非确定性函數:
SQL> insert into yu_test2 (c_vl_1) values(1);
1 row inserted
SQL> commit;
Commit complete
SQL> DROP FUNCTION FUN_TEST1;
Function dropped
SQL> select * from yu_test2;
select * from yu_test2
ORA-00904: "YUZH"."FUN_TEST1": 辨別符無效
SQL> CREATE OR REPLACE FUNCTION FUN_TEST1 (c_in number) RETURN NUMBER AS
2 BEGIN
3 RETURN 2;
4 END;
5 /
Function created
SQL> select * from yu_test2;
C_VL_1 C_VL_2
---------- ----------
1 2
建立了虛拟列可以有效的減少資料的存儲,簡化查詢語句中對列進行的處理,而且還可以利用虛拟列進行分區。不過虛拟列還會帶來其他問題。首先包含了虛拟列的表在INSERT INTO語句中不能省略COLUMN清單。由于虛拟列的值是由其他列的值計算得出的,且Oracle并不存儲虛拟列的值,是以無論是INSERT還是UPDATE都不能對虛拟列進行修改:
SQL> insert into yu_test2 values(1);
insert into yu_test2 values(1)
ORA-00947: 沒有足夠的值
SQL> insert into yu_test2 values(1,1);
insert into yu_test2 values(1,1)
ORA-54013: 不允許對虛拟列執行 INSERT 操作
SQL> insert into yu_test2 (c_vl_1,c_vl_2) values(1,1);
insert into yu_test2 (c_vl_1,c_vl_2) values(1,1)
ORA-54013: 不允許對虛拟列執行 INSERT 操作
SQL> insert into yu_test2 (c_vl_1) values(1);
1 row inserted
SQL> commit;
Commit complete
SQL> update yu_test2 set c_vl_1=2;
2 rows updated
SQL> update yu_test2 set c_vl_2=2;
update yu_test2 set c_vl_2=2
ORA-54017: 不允許對虛拟列執行 UPDATE 操作
SQL> commit
2 ;
Commit complete
如果程式選擇使用了一些工具來自動生成表的INSERT、UPDATE語句,那麼遇到包含虛拟列的表就會報錯。出于同樣的原因,無法使用CREATE TABLE AS SELECT建立一個包含虛拟列的表。解決方法是CREATE TABLE AS SELECT結束後通過ALTER TABLE添加虛拟列。虛拟列還存在一個問題,當虛拟列的值一旦被實體化,那麼虛拟清單達式發生變化會造成實體化結果與虛拟列不一緻。簡單的說就是虛拟列的結果是在查詢的時候确定的,如果修改了虛拟列的表達式,下次執行查詢時,虛拟列的值就會發生變化。但是一旦對虛拟列建立了索引,或者對包含虛拟列的表建立了物化視圖,那麼虛拟列的數值就被實際的存儲下來,當虛拟列的表達式發生修改後,會導緻索引或物化視圖中已有的資料與目前虛拟列結果不一緻。這個問題的解決方法隻有删除索引并重建,或者将物化視圖完全重新整理。
SQL> create index YU_TEST2_IDX on YU_TEST2 (c_vl_2);
Index created
SQL> ALTER TABLE YU_TEST2 MODIFY c_vl_2 AS (UPPER(c_vl_1));
ALTER TABLE YU_TEST2 MODIFY c_vl_2 AS (UPPER(c_vl_1))
ORA-54022: 無法更改虛拟清單達式, 因為在列上定義了索引
雖然建立了索引後Oracle會禁止虛拟列發生修改,但是Oracle并不禁止虛拟列參考的函數的修改,修改方式見前面。