PL/SQL(Procedural Language/SQL)編程簡介 遊標,過程,函數,包,觸發器

塊結構

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被轉換爲本地代碼,並存儲在共享庫中。

本地代碼的運行速度比中間代碼的運行速度快得多,因爲本地代碼在運行之前不需要解釋。










發佈了53 篇原創文章 · 獲贊 16 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章