塊結構
PL/SQL程序可劃分成稱爲塊的結構,每一個塊都包含有PL/SQL和SQL語句。
典型的PL/SQL塊包含如下結構:
[DECLARE
declaration_statements 聲明PL/SQL塊其餘部分使用的變量。DECLARE是可選的。
]
BEGIN
executable_statements 塊中實際可執行的語句,其中可能包括諸如循環、條件邏輯等任務的語句
[EXCEPTION
exception_handing_statements 負責處理當塊運行時可能發生的任何可執行錯誤。EXCEPTION是可選的。
]
END;
/
例子:根據給出的矩形面積和高度計算其寬度
SET SERVEROUTPUT ON 打開服務器輸出以便在SQL*Plus中運行腳本時可以在屏幕上看到由DBMS_OUTPUT.PUT_LINE()產生的行。
DECLARE
v_width INTEGER;
v_height INTEGER;
v_area INTEGER;
BEGIN
--set the width equal to the area divided by the height 註釋
v_width := v_area / v_height;
DBMS_OUTPUT.PUT_LINE('v_width = ' || v_width); DBMS_OUTPUT是Oracle數據庫內置的代碼包,DBMS_OUTPUT包含允許將值輸出到屏幕上的過程。
EXCEPTION
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('Division by zero');
END;
/ PL/SQL塊結束
變量和類型
可通過%TYPE關鍵字來定義變量的類型,使用與表中指定列相同的類型。
v_product_price products.price%TYPE 這個變量的類型與products表的price列類型相同。
條件邏輯
IF condition1 THEN
statements1
ELSEIF condition2 THEN
statements2
ELSE
statements3
END IF;
循環
簡單循環 直到顯式結束循環之前一直運行。
LOOP
statements
END LOOP; 要結束簡單循環,可以使用EXIT立即結束循環,EXIT WHEN 在指定條件出現時結束循環。
例:
v_counter := 0;
LOOP
v_counter := v_counter + 1;
EXIT WHEN v_counter = 5;
END LOOP; EXIT WHEN語句可以出現在循環代碼中的任何位置。
可以使用CONTINUE或CONTINUE WHEN語句結束循環的當前迭代。CONTINUE無條件結束循環當前迭代,並繼續執行下一個迭代。CONTINUE WHEN在滿足特定條件時結束循環的當前迭代,然後繼續執行下一個迭代。
例:
v_counter := 0;
LOOP
-- after the CONTINUE statement is executed, control returns here
v_counter := v_counter + 1;
IF v_counter = 3 THEN
CONTINUE; -- end current iteration unconditionally
END IF;
EXIT WHEN v_counter = 5;
END LOOP; CONTINUE或CONTINUE WHEN 不能跨過過程、函數或方法的邊界。
WHILE循環 在某個特定條件出現之前一直運行
WHILE condition LOOP
statements
END LOOP;
FOR 循環 運行預先確定的次數,給循環變量指定下限和上限來確定循環運行的次數,然後,循環變量在每次循環中遞增(或遞減)。
FOR loop_variable IN [REVERSE] lower_bound.. upper_bound LOOP
statements
END LOOP;在沒有使用REVERSE的情況下,循環變量初始化爲下限值。使用REVERSE,循環變量初始化爲上限值。
例:
FOR v_counter2 IN 1..5 LOOP
DBMS_OUTPUT.PUT_LINE(v_counter);
END LOOP;
遊標 獲取查詢返回的記錄。在通過查詢將記錄檢索到遊標中後,可以一次從遊標中取出一行。
使用遊標步驟一:聲明用於保存列值的變量
DECLARE
v_product_id products.product_id%TYPE;
v_name products.name%TYPE;
v_price products.price%TYPE;
步驟二:聲明遊標,遊標的聲明由遊標名和希望執行的查詢組成。
CURSOR cursor_name IS
select_statement; 查詢在打開遊標前並不會真正運行。
步驟三:打開遊標
OPEN v_product_cursor
步驟四:從遊標中取得記錄
FETCH cursor_name
INTO variable[, variable ...]
當遊標包含多條記錄,要通過循環一次讀取每一條記錄。爲了確定循環是否結束,可以使用布爾變量v_product_cursor%NOTFOUND。當從v_product_cursor中不能再讀出記錄時,這個變量就爲真。
步驟五:關閉遊標 釋放系統資源
CLOSE v_product_cursor;
完整示例:
SET SERVEROUTPUT ON
DECLARE
--step 1: declare the variables
v_product_id products.product_id%TYPE;
v_name products.name%TYPE;
v_price products.price%TYPE;
--step 2:declare the cursor
CURSOR v_product_cursor IS
SELECT product_id, name, price
FROM products
ORDER BY product_id
BEGIN
--step 3:open the cursor
OPEN v_product_cursor;
LOOP
--step 4 :fetch the rows from the cursor
FETCH v_product_cursor
INTO v_product_id,v_name,v_price;
EXIT WHEN v_product_cursor%NOTFOUND
--use DBMS_OUTPUT.PUT_LINE() to display the variables
DBMS_OUTPUT.PUT_LINE(
'v_product_id = ' || v_product_id || ', v_name = name' || v_name ||
', v_space = ' v_space
);
END LOOP;
--step 5: close the cursor
CLOSE v_product_cursor;
END
/
遊標與FOR循環
利用FOR循環可以訪問遊標中的記錄。當使用FOR循環時,可以不顯式的打開和關閉遊標。
FOR循環會自動執行這些操作
OPEN-FOR語句
可以將遊標分配給不同的查詢,因此可以更加靈活的處理遊標。
無約束遊標
前面介紹的遊標都有具體的返回類型,這些遊標稱爲約束遊標。約束遊標的返回類型必須與遊標運行的查詢中的列相匹配。無約束遊標沒有返回類型,因此可以運行任何查詢。
異常
ZERO_DIVIDE異常 試圖用一個數字除以零的時候發生
DUP_VAL_ON_INDEX試圖向具有唯一性索引約束的列中插入重複的值。
INVALID_NUMBER試圖將無效的字符串轉換成數字時。
ORTHERS異常可以處理所有異常。
過程
過程包含一組SQL和PL/SQL語句。過程可將業務邏輯集中在數據庫中,任何能夠訪問數據庫的程序都可以使用過程。
CREATE PROCEDURE [OR REPLACE] procedure_name
[ ( parameter_name [IN | OUT | IN OUT ] type [, ...]) ]
{ IS | AS}
BEGIN
procedure_body;
END procedure_name;
例子:
CREATE PROCEDURE update_product_price (
p_product_id IN products.product_id%TYPE,
p_factor IN NUMBER
) AS
v_product_count INTEGER;
BEGIN
SELECT COUNT(*)
INTO v_product_count
FROM products
WHERE product_id = p_product_id
IF v_product_count = 1THEN
UPDATE products
SET price = price * p_factor
WHERE product_id = p_product_id;
COMMIT;
END IF;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END update_product_price
/
調用過程:
位置表示法CALL update_product_price(1,1.5);
命名錶示法CALL update_product_price(p_factor => 1.3,p_product_id => 2);
混合表示法CALL update_product_price(3,p_factor => 1.7);
獲取有關過程的信息:user_procedures(all_procedures)視圖
刪除過程DROP PROCEDURE update_product_price;
查看過程中的錯誤:SHOW ERRORS
函數
函數與過程很相似,唯一區別在於函數必須向調用它的語句返回一個值。
存儲過程和函數有時合起來被稱爲存儲子程序。
創建函數
CREATE FUNCTION [OR REPLACE] function_name
[ (parameter_name [ IN | out | IN OUT] type [, ...] ) ]
RETURN type
{ IS | AS }
BEGIN
function_body
END function_name
例子1:
CREATE FUNCTION circle_area (
p_radius IN NUMBER
) RETURN NUMBER AS
v_pi NUMBER := 3.1415926;
v_area NUMBER;
BEGIN
v_area := v_pi * POWER(p_radius,2);
RETURN v_area;
END circle_area;
/
例子2:
CREATE FUNCTION average_product_price (
p_product_type_id IN INTEGER
) RETURN NUMBER AS
v_average_product_price NUMBER;
BEGIN
SELECT AVG(price)
INTO v_average_product_price
FROM products
WHERE product_type_id = p_product_type_id;
RETURN v_average_product_price;
END average_product_price;
/
調用函數
SELECT circle_area(2) FROM dual;
命名錶示法: SELECT circle_area(p_radius => 4) FROM dual;
SELECT average_product_price(1) FROM dual;
獲取有關函數的信息:user_procedures(all_procedures)視圖。
刪除函數:DROP FUNCTION function_name;
包(package)
包可以將彼此相關的功能劃分到一個自包含的單元中。通過這種方式將PL/SQL代碼模塊化,可以構建供其他編程人員重用的代碼庫。
事實上,ORACLE數據庫提供了包庫,可用來訪問外部文件,管理數據庫、生成HTML等等。
包通常由兩部分組成:規範(specification)和包體(body)。
包的規範列出可用的過程、函數、類型和對象。所有的數據庫用戶都可以訪問這些條目,因此這些條目稱爲公有項目(只有具有包訪問權限的用戶才能使用)。規範中通常不包括構成這些過程和函數的代碼,包體中才包含實際的代碼。
包體中任何在規範中未列出的項目對於這個包體都是私有對象。私有項目只能用在包體內。通過將公有項目和私有項目結合起來,可以構建出非常複雜的包結構,這種複雜性外部是看不到。這也是所有編程工作的主要目標之一:對用戶隱藏複雜性。
創建包規範
CREATE [ OR REPLACE ] PACKAGE package_name
{ IS | AS}
package_specification
END package_name;
例子:
CREATE PACKAGE product_package AS
TYPE t_ref_cursor IS REF CURSOR;
FUNCTION get_products_ref_cursor RETURN t_ref_cursor;
PROCEDURE update_product_price (
p_product_id IN products.product_id%TYPE,
p_factor IN NUMBER
);
END product_package;
/
創建包體
CREATE [OR REPLACE] PACKAGE BODY package_name
{IS | AS }
package_body
END package_name;
例子:
CREATE PACKAGE BODY product_package AS
FUNCTION get_products_ref_cursor
RETURN t_ref_cursor IS
v_products_ref_cursor t_ref_cursor;
BEGIN
OPEN v_products_ref_cursor FOR
SELECT products_id,name,price
FROM products;
RETURN v_products_ref_cursor;
END get_products_ref_cursor;
PROCEDURE update_product_price (
p_product_id IN products.product_id%TYPE,
p_factor IN NUMBER
) AS
v_product_count INTEGER;
BEGIN
SELECT COUNT(*)
INTO v_product_count
FROM products
WHERE product_id = p_product_id;
IF v_product_count = 1 THEN
UPDATE products
SET price = price * p_factor
WHERE product_id = p_product_id;
COMMIT;
END IF;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END update_product_price;
END product_package;
/
調用包中的函數和過程
SELECT product_package.get_products_ref_cursor FROM dual;
CALL product_package.update_product_price(3,1.25);
獲取有關包中函數和過程的信息:(user_procedures)
刪除包:DROP PACKAGE product_package;
觸發器trigger
觸發器是當特定的SQL DML 語句,如INSERT,UPDATE或DELETE語句在特定的數據庫表上運行時,由數據庫自動激活的過程。
觸發器對於實現表中某些列值的高級變更審計等功能非常有用。
觸發器運行的時機
trigger可以在DML語句運行之前和之後被激活。 行級觸發器(每行觸發一次)與語句級觸發器(DML語句觸發一次)
創建觸發器
CREATE [ OR REPLACE ] TRIGGER trigger_name
{BEFORE | AFTER | INSTEAD OF | FOR} trigger_event
ON table_name
[ FOR EACH ROW ]
[{FORWARD | REVERSE } CROSSEDITION ]
[{FOLLOWS | PRECEDES} schema.other_trigger}
[{ENABLE | DISABLE}]
[WHEN trigger_condition]]
BEGIN
trigger_body
END trigger_name;
例子:
CREATE TRIGGER before_product_price_update
BEFORE UPDATE OF price
ON products
FOR EACH ROW WHEN (new.price < old.price * 0.75)
BEGIN
dbms_output.put_line('pruduct_id = ' || :old.product_id);
dbms_output.put_line('Old price = ' || :old.price);
dbms_output.put_line('New price = ' || :new.price);
dbms_output.put_line('The price reduction is more than 25%');
INSERT INTO product_price_audit (
product_id,old_price,new_price
) VALUES (
:old.product_id, :old.price, :new.price
);
END before_product_price_update;
/
激活觸發器
SET SERVEROUTPUT ON
UPDATE products
SET price = price * .7
WHERE product_id IN (5,10);
獲得有關觸發器的信息user_trigger(all_trigger)視圖。
禁用和啓用觸發器
ALTER TRIGGER before_product_price_update DISABLE;
ALTER TRIGGER before_product_price_update ENABLE;
刪除觸發器
DROP TRIGGER before_product_price_update ;
Oracle Darabase 11g新增加的PL/SQL特性。
SIMPLE_INTEGER類型,當不需要存儲NULL且對計算中產生的溢出截斷不介意時,應該在PL/SQL中使用SIMPLE_INTEGER。否則就應該使用BINARY_INTEGER。
在PL/SQL中使用序列。
PL/SQL本地機器代碼生成
默認情況下,每個PL/SQL程序單元都被編譯成中間格式,即機器可讀的代碼。這些代碼存儲在數據庫中,每次代碼運行時需要進行解釋。使用PL/SQL本地編譯,PL/SQL被轉換爲本地代碼,並存儲在共享庫中。
本地代碼的運行速度比中間代碼的運行速度快得多,因爲本地代碼在運行之前不需要解釋。