Oracle 數據庫提供了表、索引、視圖、函數、存儲過程、包/包體、序列、觸發器、作業等數十種模式對象。實際上除表以外,其它對象都不是(關系型數據庫)應用所必須的,大部分對象都是在某種特殊場景下才會被用到。
1、視圖
1.1、創建、刪除及調用普通視圖
創建視圖:普通 Oracle 視圖不方便注釋,寫在頭部的注釋執行之后再打開定義就沒了。后來我發現把注釋寫到尾部就不會被清除,但總感覺怪怪的,工作中我也是偶爾才這么用,暫時還不知道是否有其它影響。
CREATE OR REPLACE VIEW v_staff2 AS
SELECT t1.staff_id,t1.staff_name,DECODE(t1.gender,1,'男',0,'女','未知') gender,
EXTRACT(YEAR FROM SYSDATE)-EXTRACT(YEAR FROM t1.birthday) age,t2.enum_name dept_name
FROM demo.t_staff t1,demo.t_field_enum t2
WHERE t1.dept_code=t2.enum_code AND t2.field_code='DEPT' AND t1.is_disabled=0
-- 在職員工基本信息
;
刪除視圖:深入了解請參考《Oracle Database SQL Reference:DROP VIEW》。
DROP VIEW v_staff2;
調用視圖:調用視圖的 CRUD 語法與調用表的基本相同,只是會多一些限制,這里就不贅述了。
1.2、高級視圖介紹
如果從視圖的性質和用途等角度,還可以將視圖區分為:帶約束的視圖、可更新的視圖、連接視圖、只讀視圖、對象視圖、基於 XMLType 表的視圖等。如要進一步了解 Oracle 視圖可參考:《Oracle Database Concepts:Overview of Views》、《.Net程序員學用Oracle系列(23):視圖理論、物化視圖》和《.Net程序員學用Oracle系列(24):數據字典、死鎖》。
創建只讀視圖:更多類型視圖的創建語法參見《Oracle Database SQL Reference:CREATE VIEW》
CREATE OR REPLACE VIEW v_staff3(staff_id,staff_name,gender,age) AS
SELECT t1.staff_id,t1.staff_name,DECODE(t1.gender,1,'男',0,'女','未知'),
EXTRACT(YEAR FROM SYSDATE)-EXTRACT(YEAR FROM t1.birthday)
FROM demo.t_staff t1
WHERE t1.is_disabled=0
WITH READ ONLY;
2、函數
2.1、系統函數介紹
Oracle 數據庫中內置了大量 SQL 函數,一般把這些函數稱之為系統函數。在使用系統函數時,如果給定參數的數據類型和預期數據類型不同,則 Oracle 會在執行系統函數之前嘗試將該參數轉換為預期的數據類型。萬一轉換失敗則可能會直接報錯。如果你使用 NULL 做為參數,那么系統函數將自動返回 NULL,僅有的幾個不遵守此規則的函數是:CONCAT
、NVL
、REPLACE
、REGEXP_REPLACE
。
鑒於系統函數過多,這里不便展開,本人寫了三篇專門介紹系統函數的文章,分別是:《.Net程序員學用Oracle系列(9):系統函數(上)》、《.Net程序員學用Oracle系列(10):系統函數(中)》、《.Net程序員學用Oracle系列(11):系統函數(下)》。如果你還想進一步了解 Oracle 的系統函數,請參考:《Oracle Database SQL Reference:SQL Functions》。
2.2、創建、刪除及調用自定義函數
創建自定義函數:深入了解請參考《Oracle Database SQL Reference:CREATE FUNCTION》。
CREATE OR REPLACE FUNCTION fn_today2
RETURN DATE IS
v_today DATE;
BEGIN
v_today:=TO_DATE('2017-01-10','yyyy-mm-dd');
RETURN v_today;
END;
刪除自定義函數:
DROP VIEW v_staff2;
調用自定義函數:
示例(方式一):
SELECT fn_today res FROM DUAL; -- res:2017-01-10
示例(方式二):
BEGIN
DBMS_OUTPUT.PUT_LINE('res:'||TO_CHAR(fn_today,'yyyy-mm-dd')); -- res:2017-01-10
END;
示例(方式三):
DECLARE
v_today DATE;
BEGIN
v_today:=fn_today;
DBMS_OUTPUT.PUT_LINE('res:'||TO_CHAR(fn_today,'yyyy-mm-dd')); -- res:2017-01-10
END;
3、存儲過程
存儲過程是存放在數據字典中的程序塊,它可以在不同用戶和應用程序間共享,並可實現程序的優化和重用。
3.1、創建、修改及刪除存儲過程
語法:
CREATE [OR REPLACE] PROCEDURE [SCHEMA.]procedureName[(param1 mode1 dataType1,...n)]
IS|AS
var1 type1;
var2 type2;
...
BEGIN
statements; /* 過程體,要執行的操作 */
END;
其中,mode1 表示參數的類型,跟方法的參數一樣,有 in、out 和 in out 三種類型,dataType1 表示參數的數據類型。
創建存儲過程:
創建一個帶自制事物的存儲過程,示例:
CREATE OR REPLACE PROCEDURE sp_sync_staff90
AS
v_sql VARCHAR2(200); -- SQL語句
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
v_sql:='TRUNCATE TABLE t_staff_young'; -- 清空 90 后職員表
EXECUTE IMMEDIATE v_sql; -- PLSQL 中不能直接執行 DDL 語句
COMMIT;
INSERT INTO t_staff_young
SELECT t1.staff_id,t1.staff_name,t1.dept_code,t1.gender
FROM t_staff t1
WHERE t1.birthday>=TO_DATE('1990-01-01','yyyy-mm-dd');
COMMIT;
END;
創建一個帶返回值的存儲過程,示例:
CREATE OR REPLACE PROCEDURE sp_staff_status
(
p_staff_id NUMBER, -- 職員ID
p_result OUT VARCHAR2 -- 返回職員狀態信息
)
AS
v_staff_status NUMBER(1);
BEGIN
IF (p_staff_id IS NULL OR p_staff_id<0) THEN
p_result:='查無此員工!';
ELSE
SELECT t.is_disabled INTO v_staff_status FROM demo.t_staff t WHERE t.staff_id=p_staff_id;
END IF;
-- 如果用戶沒有對應權限則給出具體提示
IF v_staff_status=0 THEN
p_result:='該員工在職!';
ELSE
p_result:='該員工已離職!';
END IF;
END;
創建存儲過程的時候有一個小細節需要注意一下,那就是參數名不能與程序中要訪問的庫表的字段重名;不過即使重名了也還是能通過編譯,但等到實際調用的時候就會出問題了,因為 Oracle 會把這個參數當成庫表中的字段,即便你在程序中用表別名限定了實際的字段,Oracle 也還是識別不了。如果不知道這個細節的話,是很難解決復雜存儲過程中這類問題的,所以寫存儲過程的時候還是要遵照《.Net程序員學用Oracle系列(3):數據庫編程規范》,養成一個好的編碼習慣,避免因壞習慣而入坑,白白浪費時間。
刪除存儲過程:
DROP PROCEDURE sp_sync_staff90;
3.2、調用存儲過程
在 PLSQL 的 SQL 窗口中調用無參存儲過程的示例:
BEGIN
sp_sync_staff90; -- 這里必須加分號,否則有語法錯誤
END;
在 PLSQL 的 SQL 窗口中調用有參數存儲過程的示例:
DECLARE res VARCHAR2(100);
BEGIN
sp_staff_status(2,res);
DBMS_OUTPUT.PUT_LINE('res:'||res); -- res:該員工在職!
END;
在命令窗口調用存儲過程的語法:
exec[ute] procedureName[(param1,...n)]
4、包/包體
Oracle 中的包就像是一個容器,可以將一組存儲過程、函數、變量、常量和游標等 PL/SQL 程序設計元素放到一起。包由包規范和包體兩個部分組成,包規范用於定義公用的常量、變量、存儲過程和函數,包體用於存放存儲過程和函數的定義。包頭可以沒有包體而獨立存在。
關於包的好處,網上大多是從程序模塊化管理的角度來闡述的,比如說方便查詢和維護存儲過程和函數等等。我本人從未系統學過 Oracle,原本也不知道 Oracle 中包的存在,后來為了寫一個觸發器來實現在一個批量操作(忘記是新增、修改或刪除)之后,同時更新另一個表中兩個不同緯度字段,更新條件里還需要對第一個表中的數據做聚合。
如果我沒記錯的話,應該是添加可以更新,修改和刪除更新不了,如果更新語句執行之后立即提交,語法上又通不過,怎么寫都不行。后來我分析這個操作本身就是矛盾的,於是我把我的分析給經理講了一下,他也覺得我說的有道理。接下來我們開始查資料、找解決方案,經理先找到包變量的用法,並做了個測試認為可行,於是我依葫蘆畫瓢,把已經刪掉或改過的數據外鍵保存到包變量中,再到觸發器中去取,結果還真行得通。
4.1、創建包/包體
創建一個包含包變量的包規范,示例:
CREATE OR REPLACE PACKAGE pkg_staff AS -- 這里還可以用 IS 代替 AS
staff_id NUMBER(10); -- 包變量,職員ID
staff_name VARCHAR2(20); -- 包變量,職員名稱
END;
創建一個包含函數的包規范pkg_case
,示例:
CREATE OR REPLACE PACKAGE pkg_case AS
FUNCTION fn_today RETURN DATE; -- 定義函數 fn_today
END;
創建包規范pkg_case
的包體,示例:
CREATE OR REPLACE PACKAGE BODY pkg_case AS
FUNCTION fn_today
RETURN DATE IS
v_today DATE;
BEGIN
v_today:=TO_DATE('2017-01-10','yyyy-mm-dd');
RETURN v_today;
END;
END;
注意:必須先創建包規范,然后再創建包體。
4.2、調用包/包體
調用包里的數據庫對象與調用普通的數據庫對象方式相同,唯一的區別就是要帶上包名前綴(包名.包成員名)。如要調用pkg_case
包里的fn_today
函數,示例:
SELECT pkg_case.fn_today FROM DUAL; -- res:2017-01-10
4.3、刪除包/包體
DROP PACKAGE BODY pkg_case; -- 僅刪除 pkg_case 包的包體
DROP PACKAGE pkg_case; -- 同時刪除包和包體
5、總結
本文主要介紹了視圖、函數、存儲過程和包/包體 4 個比較常用的模式對象的創建、刪除及調用的 PL/SQL 寫法,並做了少量理論分析以便讀者能更好的理解。
細心的讀者可能已經發現了,上文中所有創建對象定義的語句中均包含OR REPLACE
子句,該子句的作用是用來修改對象定義的,所以重新執行上文中創建對象的語句,就相當於在修改對象定義。事實上創建對象定義的語句中可以不要OR REPLACE
子句,那該語句就只能創建對象而不能修改對象了。
當模式對象所引用的基對象發生變化時,就可能會導致該對象編譯無效,如果該對象此時被調用,那么 Oracle 數據庫就會在運行時隱式的重新編譯它。其實,Oracle 提供了顯示重新編譯對象的功能,可以在對象被引用之前手動重新編譯它,以防止相關的運行時編譯錯誤和性能開銷。
當ALTER
某張表時,表示修改表定義,但上文所介紹的四個對象不同,ALTER
它們則表示重新編譯它們。示例:
ALTER VIEW demo.v_staff COMPILE; -- 編譯 demo.v_staff 視圖
ALTER FUNCTION demo.fn_now COMPILE; -- 編譯 demo.fn_now 函數
ALTER PROCEDURE demo.sp_staff_status COMPILE; -- 編譯 demo.sp_staff_status 存儲過程
ALTER PACKAGE demo.pkg_staff COMPILE; -- 編譯 demo.pkg_staff 包規范和包體
ALTER PACKAGE demo.pkg_staff COMPILE PACKAGE; -- 僅編譯 demo.pkg_staff 包規范
ALTER PACKAGE demo.pkg_staff COMPILE BODY; -- 僅編譯 demo.pkg_staff 包體
ALTER TRIGGER demo.trg_staff_id COMPILE; -- 編譯 demo.trg_staff_id 觸發器
本文鏈接:http://www.cnblogs.com/hanzongze/p/oracle-view-procedure.html
版權聲明:本文為博客園博主 韓宗澤 原創,作者保留署名權!歡迎通過轉載、演繹或其它傳播方式來使用本文,但必須在明顯位置給出作者署名和本文鏈接!本人初寫博客,水平有限,若有不當之處,敬請批評指正,謝謝!