ORACLE PL/SQL編程總結(二)

----------異常錯誤處理---------html

即便是寫得最好的PL/SQL程序也會遇到錯誤或未預料到的事件。一個優秀的程序都應該可以正確處理各類出錯狀況,並儘量從錯誤中恢復。任何ORACLE錯誤(報告爲ORA-xxxxx形式的Oracle錯誤號)、PL/SQL運行錯誤或用戶定義條件(不一寫是錯誤),均可以。固然了,PL/SQL編譯錯誤不能經過PL/SQL異常處理來處理,由於這些錯誤發生在PL/SQL程序執行以前。程序員

ORACLE 提供異常狀況(EXCEPTION)和異常處理(EXCEPTION HANDLER)來實現錯誤處理。算法

5.1異常處理概念

異常狀況處理(EXCEPTION)是用來處理正常執行過程當中未預料的事件,程序塊的異常處理預約義的錯誤和自定義錯誤,因爲PL/SQL程序塊一旦產生異常而沒有指出如何處理時,程序就會自動終止整個程序運行. sql

有三種類型的異常錯誤:數據庫

    1. 預約義 ( Predefined )錯誤express

  ORACLE預約義的異常狀況大約有24個。對這種異常狀況的處理,無需在程序中定義,由ORACLE自動將其引起。編程

    2. 非預約義 ( Predefined )錯誤c#

   即其餘標準的ORACLE錯誤。對這種異常狀況的處理,須要用戶在程序中定義,而後由ORACLE自動將其引起。安全

    3. 用戶定義(User_define) 錯誤服務器

 程序執行過程當中,出現編程人員認爲的非正常狀況。對這種異常狀況的處理,須要用戶在程序中定義,而後顯式地在程序中將其引起。

異常處理部分通常放在 PL/SQL 程序體的後半部,結構爲:

EXCEPTION
   WHEN first_exception THEN  <code to handle first exception >
   WHEN second_exception THEN  <code to handle second exception >
   WHEN OTHERS THEN  <code to handle others exception >
END;

異常處理能夠按任意次序排列,但 OTHERS 必須放在最後。

 

5.1.1  預約義的異常處理

 

 預約義說明的部分 ORACLE 異常錯誤

 

錯誤號

異常錯誤信息名稱

說明

ORA-0001

Dup_val_on_index

         違反了惟一性限制

ORA-0051

Timeout-on-resource

               在等待資源時發生超時

ORA-0061

Transaction-backed-out

               因爲發生死鎖事務被撤消

ORA-1001

Invalid-CURSOR

               試圖使用一個無效的遊標

ORA-1012

Not-logged-on

               沒有鏈接到ORACLE

ORA-1017

Login-denied

               無效的用戶名/口令

ORA-1403

No_data_found

              SELECT INTO沒有找到數據

ORA-1422

Too_many_rows

               SELECT INTO 返回多行

ORA-1476

Zero-divide

               試圖被零除

ORA-1722

Invalid-NUMBER

               轉換一個數字失敗

ORA-6500

Storage-error

               內存不夠引起的內部錯誤

ORA-6501

Program-error

               內部錯誤

ORA-6502

Value-error

               轉換或截斷錯誤

ORA-6504

Rowtype-mismatch

               宿主遊標變量與 PL/SQL變 量有不兼容行類型

ORA-6511

CURSOR-already-OPEN

               試圖打開一個已處於打開狀 態的遊標

ORA-6530

Access-INTO-null

               試圖爲null 對象的屬性賦值

ORA-6531

Collection-is-null

               試圖將Exists 之外的集合( collection)方法應用於一個null pl/sql 表上或varray上

ORA-6532

Subscript-outside-limit

               對嵌套或varray索引得引用超出聲明範圍之外

ORA-6533

Subscript-beyond-count

               對嵌套或varray 索引得引用大於集合中元素的個數.

 

 

對這種異常狀況的處理,只需在PL/SQL塊的異常處理部分,直接引用相應的異常狀況名,並對其完成相應的異常錯誤處理便可。

例1:更新指定員工工資,如工資小於1500,則加100;

DECLARE
   v_empno employees.employee_id%TYPE := &empno;
   v_sal   employees.salary%TYPE;
BEGIN
   SELECT salary INTO v_sal FROM employees WHERE employee_id = v_empno;
   IF v_sal<=1500 THEN 
        UPDATE employees SET salary = salary + 100 WHERE employee_id=v_empno; 
        DBMS_OUTPUT.PUT_LINE('編碼爲'||v_empno||'員工工資已更新!');     
   ELSE
        DBMS_OUTPUT.PUT_LINE('編碼爲'||v_empno||'員工工資已經超過規定值!');
   END IF;
EXCEPTION
   WHEN NO_DATA_FOUND THEN  
      DBMS_OUTPUT.PUT_LINE('數據庫中沒有編碼爲'||v_empno||'的員工');
   WHEN TOO_MANY_ROWS THEN
      DBMS_OUTPUT.PUT_LINE('程序運行錯誤!請使用遊標');
   WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
END; 

 

5.1.2  非預約義的異常處理

 

對於這類異常狀況的處理,首先必須對非定義的ORACLE錯誤進行定義。步驟以下:

        1. 在PL/SQL 塊的定義部分定義異常狀況:

<異常狀況>  EXCEPTION;

         2. 將其定義好的異常狀況,與標準的ORACLE錯誤聯繫起來,使用EXCEPTION_INIT語句:

      PRAGMA EXCEPTION_INIT(<異常狀況>, <錯誤代碼>);

         3. 在PL/SQL 塊的異常狀況處理部分對異常狀況作出相應的處理。

例2:刪除指定部門的記錄信息,以確保該部門沒有員工。

INSERT INTO departments VALUES(50, 'FINANCE', 'CHICAGO');

DECLARE
   v_deptno departments.department_id%TYPE := &deptno;
   deptno_remaining EXCEPTION;
   PRAGMA EXCEPTION_INIT(deptno_remaining, -2292);
   /* -2292 是違反一致性約束的錯誤代碼 */
BEGIN
   DELETE FROM departments WHERE department_id = v_deptno;
EXCEPTION
   WHEN deptno_remaining THEN 
      DBMS_OUTPUT.PUT_LINE('違反數據完整性約束!');
   WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
END;

 

5.1.3 用戶自定義的異常處理

 

當與一個異常錯誤相關的錯誤出現時,就會隱含觸發該異常錯誤。用戶定義的異常錯誤是經過顯式使用 RAISE 語句來觸發。當引起一個異常錯誤時,控制就轉向到 EXCEPTION塊異常錯誤部分,執行錯誤處理代碼。

 對於這類異常狀況的處理,步驟以下:

    1. 在PL/SQL 塊的定義部分定義異常狀況:

<異常狀況>  EXCEPTION;

    2. RAISE <異常狀況>;

    3. 在PL/SQL 塊的異常狀況處理部分對異常狀況作出相應的處理。

例3:更新指定員工工資,增長100;

DECLARE
   v_empno employees.employee_id%TYPE :=&empno;
   no_result  EXCEPTION;
BEGIN
   UPDATE employees SET salary = salary+100 WHERE employee_id = v_empno;
   IF SQL%NOTFOUND THEN
      RAISE no_result;
   END IF;
EXCEPTION
   WHEN no_result THEN 
      DBMS_OUTPUT.PUT_LINE('你的數據更新語句失敗了!');
   WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
END;

 

5.1.4  用戶定義的異常處理

 

調用DBMS_STANDARD(ORACLE提供的包)包所定義的RAISE_APPLICATION_ERROR過程,能夠從新定義異常錯誤消息,它爲應用程序提供了一種與ORACLE交互的方法。

RAISE_APPLICATION_ERROR 的語法以下:

    RAISE_APPLICATION_ERROR(error_number,error_message,[keep_errors] );

    這裏的error_number 是從 –20,000 到 –20,999 之間的參數,

    error_message 是相應的提示信息(< 2048 字節),

   keep_errors 爲可選,若是keep_errors =TRUE ,則新錯誤將被添加到已經引起的錯誤列表中。若是keep_errors=FALSE(缺省),則新錯誤將替換當前的錯誤列表。

例4:定義觸發器,使用RAISE_APPLICATION_ERROR阻止沒有員工姓名的新員式記錄插入:

CREATE OR REPLACE TRIGGER tr_insert_emp
BEFORE INSERT ON employees
FOR EACH ROW
BEGIN
  IF :new.first_name IS NULL OR :new.last_name is null THEN
    RAISE_APPLICATION_ERROR(-20000,'Employee must have a name.');
  END IF;
END;

 

5.2 異常錯誤傳播

因爲異常錯誤能夠在聲明部分和執行部分以及異常錯誤部分出現,於是在不一樣部分引起的異常錯誤也不同。

5.2.1  在執行部分引起異常錯誤

 

當一個異常錯誤在執行部分引起時,有下列狀況:

l 若是當前塊對該異常錯誤設置了處理,則執行它併成功完成該塊的執行,而後控制轉給包含塊。

l 若是沒有對當前塊異常錯誤設置定義處理器,則經過在包含塊中引起它來傳播異常錯誤。而後對該包含塊執行步驟1)。

 

5.2.2  在聲明部分引起異常錯誤

若是在聲明部分引發異常狀況,即在聲明部分出現錯誤,那麼該錯誤就能影響到其它的塊。好比在有以下的PL/SQL程序:

DECLARE
    name varchar2(12):='EricHu';
    其它語句
BEGIN
    其它語句
EXCEPTION
    WHEN OTHERS THEN 
    其它語句
END;

 

        例子中,因爲Abc number(3)=’abc’; 出錯,儘管在EXCEPTION中說明了WHEN OTHERS THEN語句,但WHEN OTHERS THEN也不會被執行。 可是若是在該錯誤語句塊的外部有一個異常錯誤,則該錯誤能被抓住,如:

BEGIN
    DECLARE
    name varchar2(12):='EricHu';
    其它語句
   BEGIN
    其它語句
   EXCEPTION
    WHEN OTHERS THEN 
    其它語句
    END;
EXCEPTION
WHEN OTHERS THEN 
    其它語句
END;

 

5.3 異常錯誤處理編程

       在通常的應用處理中,建議程序人員要用異常處理,由於若是程序中不聲明任何異常處理,則在程序運行出錯時,程序就被終止,而且也不提示任何信息。下面是使用系統提供的異常來編程的例子。

 

5.4  在 PL/SQL 中使用 SQLCODE,SQLERRM異常處理函數

 

       因爲ORACLE 的錯信息最大長度是512字節,爲了獲得完整的錯誤提示信息,咱們可用 SQLERRM和 SUBSTR 函數一塊兒獲得錯誤提示信息,方便進行錯誤,特別是若是WHEN OTHERS異常處理器時更爲方便。

SQLCODE  返回遇到的Oracle錯誤號,

SQLERRM  返回遇到的Oracle錯誤信息.

如:  SQLCODE=-100   è SQLERRM=’no_data_found ‘

 SQLCODE=0      è SQLERRM=’normal, successfual completion’

例1:將ORACLE錯誤代碼及其信息存入錯誤代碼表

CREATE TABLE errors (errnum NUMBER(4), errmsg VARCHAR2(100));

DECLARE
   err_msg  VARCHAR2(100);
BEGIN
   /*  獲得全部 ORACLE 錯誤信息  */
   FOR err_num IN -100 .. 0 LOOP
      err_msg := SQLERRM(err_num);
      INSERT INTO errors VALUES(err_num, err_msg);
   END LOOP;
END;
DROP TABLE errors;

 

例2:查詢ORACLE錯誤代碼;

BEGIN
   INSERT INTO employees(employee_id, first_name,last_name,hire_date,department_id)
   VALUES(2222, 'Eric','Hu', SYSDATE, 20);
   DBMS_OUTPUT.PUT_LINE('插入數據記錄成功!');
   
   INSERT INTO employees(employee_id, first_name,last_name,hire_date,department_id)
   VALUES(2222, '胡','勇', SYSDATE, 20);
   DBMS_OUTPUT.PUT_LINE('插入數據記錄成功!');
EXCEPTION
   WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
END;

 

例3: 利用ORACLE錯誤代碼,編寫異常錯誤處理代碼;

DECLARE
   empno_remaining EXCEPTION;
   PRAGMA EXCEPTION_INIT(empno_remaining, -1);
   /* -1 是違反惟一約束條件的錯誤代碼 */
BEGIN
   INSERT INTO employees(employee_id, first_name,last_name,hire_date,department_id)
   VALUES(3333, 'Eric','Hu', SYSDATE, 20);
   DBMS_OUTPUT.PUT_LINE('插入數據記錄成功!');
   
   INSERT INTO employees(employee_id, first_name,last_name,hire_date,department_id)
   VALUES(3333, '胡','勇',SYSDATE, 20);
   DBMS_OUTPUT.PUT_LINE('插入數據記錄成功!');
EXCEPTION
   WHEN empno_remaining THEN 
      DBMS_OUTPUT.PUT_LINE('違反數據完整性約束!');
   WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
END;

 

***********************************************

-----------把過程與函數說透-----------

 

 6.1 引言

 

過程與函數(另外還有包與觸發器)是命名的PL/SQL塊(也是用戶的方案對象),被編譯後存儲在數據庫中,以備執行。所以,其它PL/SQL塊能夠按名稱來使用他們。因此,能夠將商業邏輯、企業規則寫成函數或過程保存到數據庫中,以便共享。

過程和函數統稱爲PL/SQL子程序,他們是被命名的PL/SQL塊,均存儲在數據庫中,並經過輸入、輸出參數或輸入/輸出參數與其調用者交換信息。過程和函數的惟一區別是函數總向調用者返回數據,而過程則不返回數據。在本節中,主要介紹:

      1.   建立存儲過程和函數。

      2.   正確使用系統級的異常處理和用戶定義的異常處理。

      3.   創建和管理存儲過程和函數。

 

 

6.2 建立函數

1. 建立函數(語法以下:)

 

CREATE  [ OR  REPLACE FUNCTION  function_name
  (arg1 [ {  IN  OUT  IN  OUT  }] type1 [ DEFAULT  value1],
  [arg2 [ {  IN  OUT  IN  OUT  }] type2 [ DEFAULT  value1]],
  ......
  [argn [ {  IN  OUT  IN  OUT  }] typen [ DEFAULT  valuen]])
  [ AUTHID DEFINER |  CURRENT_USER  ]
RETURN  return_type
  IS  AS
     <類型.變量的聲明部分>
BEGIN
     執行部分
     RETURN  expression
EXCEPTION
     異常處理部分
END  function_name;

 

l         IN,OUT,IN OUT是形參的模式。若省略,則爲IN模式。IN模式的形參只能將實參傳遞給形參,進入函數內部,但只能讀不能寫,函數返回時實參的值不變。OUT模式的形參會忽略調用時的實參值(或說該形參的初始值老是NULL),但在函數內部能夠被讀或寫,函數返回時形參的值會賦予給實參。IN OUT具備前兩種模式的特性,即調用時,實參的值老是傳遞給形參,結束時,形參的值傳遞給實參。調用時,對於IN模式的實參能夠是常量或變量,但對於OUT和IN OUT模式的實參必須是變量。

  l         通常,只有在確認function_name函數是新函數或是要更新的函數時,才使用OR REPALCE關鍵字,不然容易刪除有用的函數。

例子:獲取某部門的工資總和:

 

--獲取某部門的工資總和
CREATE  OR  REPLACE
FUNCTION  get_salary(
   Dept_no NUMBER,
   Emp_count  OUT  NUMBER)
   RETURN  NUMBER
IS
   V_sum NUMBER;
BEGIN
   SELECT  SUM (SALARY),  count (*)  INTO  V_sum, emp_count
     FROM  EMPLOYEES  WHERE  DEPARTMENT_ID=dept_no;
   RETURN  v_sum;
EXCEPTION
    WHEN  NO_DATA_FOUND  THEN
       DBMS_OUTPUT.PUT_LINE( '你須要的數據不存在!' );
    WHEN  OTHERS  THEN
       DBMS_OUTPUT.PUT_LINE(SQLCODE|| '---' ||SQLERRM);
END  get_salary;

 

 

 2. 函數的調用

  函數聲明時所定義的參數稱爲形式參數,應用程序調用時爲函數傳遞的參數稱爲實際參數。應用程序在調用函數時,可使用如下三種方法向函數傳遞參數:

  第一種參數傳遞格式:位置表示法。

  即在調用時按形參的排列順序,依次寫出實參的名稱,而將形參與實參關聯起來進行傳遞。用這種方法進行調用,形參與實參的名稱是相互獨立,沒有關係,強調次序纔是重要的。

  格式爲:

1
argument_value1[,argument_value2 …]

例子:計算某部門的工資總和:

 

DECLARE
   V_num NUMBER;
   V_sum NUMBER;
BEGIN
   V_sum :=get_salary(10, v_num);
   DBMS_OUTPUT.PUT_LINE( '部門號爲:10的工資總和:' ||v_sum|| ',人數爲:' ||v_num);
END ;

 

第二種參數傳遞格式:名稱表示法。

  即在調用時按形參的名稱與實參的名稱,寫出實參對應的形參,而將形參與實參關聯起來進行傳遞。這種方法,形參與實參的名稱是相互獨立的,沒有關係,名稱的對應關係纔是最重要的,次序並不重要。

  格式爲:

1
argument => parameter [,…]

         其中:argument 爲形式參數,它必須與函數定義時所聲明的形式參數名稱相同parameter 爲實際參數。

 在這種格式中,形勢參數與實際參數成對出現,相互間關係惟一肯定,因此參數的順序能夠任意排列。

例子:計算某部門的工資總和:

 

DECLARE
   V_num NUMBER;
     V_sum NUMBER;
BEGIN
     V_sum :=get_salary(emp_count => v_num, dept_no => 10);
     DBMS_OUTPUT.PUT_LINE( '部門號爲:10的工資總和:' ||v_sum|| ',人數爲:' ||v_num);
END ;

 

第三種參數傳遞格式:組合傳遞。

即在調用一個函數時,同時使用位置表示法和名稱表示法爲函數傳遞參數。採用這種參數傳遞方法時,使用位置表示法所傳遞的參數必須放在名稱表示法所傳遞的參數前面。也就是說,不管函數具備多少個參數,只要其中有一個參數使用名稱表示法,其後全部的參數都必須使用名稱表示法。

 

CREATE  OR  REPLACE  FUNCTION  demo_fun(
   Name  VARCHAR2, --注意VARCHAR2不能給精度,如:VARCHAR2(10),其它相似
   Age  INTEGER ,
   Sex VARCHAR2)
   RETURN  VARCHAR2
AS
   V_var VARCHAR2(32);
BEGIN
   V_var :=  name || ':' ||TO_CHAR(age)|| '歲.' ||sex;
   RETURN  v_var;
END ;
 
DECLARE
   Var  VARCHAR (32);
BEGIN
   Var := demo_fun( 'user1' , 30, sex =>  '男' );
   DBMS_OUTPUT.PUT_LINE(var);
 
   Var := demo_fun( 'user2' , age => 40, sex =>  '男' );
   DBMS_OUTPUT.PUT_LINE(var);
 
   Var := demo_fun( 'user3' , sex =>  '女' , age => 20);
   DBMS_OUTPUT.PUT_LINE(var);
END ;

 

    不管採用哪種參數傳遞方法,實際參數和形式參數之間的數據傳遞只有兩種方法:傳址法和傳值法。所謂傳址法是指在調用函數時,將實際參數的地址指針傳遞給形式參數,使形式參數和實際參數指向內存中的同一區域,從而實現參數數據的傳遞。這種方法又稱做參照法,即形式參數參照實際參數數據。輸入參數均採用傳址法傳遞數據。

傳值法是指將實際參數的數據拷貝到形式參數,而不是傳遞實際參數的地址。默認時,輸出參數和輸入/輸出參數均採用傳值法。在函數調用時,ORACLE將實際參數數據拷貝到輸入/輸出參數,而當函數正常運行退出時,又將輸出形式參數和輸入/輸出形式參數數據拷貝到實際參數變量中。 

  3. 參數默認值

   在CREATE OR REPLACE FUNCTION 語句中聲明函數參數時可使用DEFAULT關鍵字爲輸入參數指定默認值。

 

CREATE  OR  REPLACE  FUNCTION  demo_fun(
   Name  VARCHAR2,
   Age  INTEGER ,
   Sex VARCHAR2  DEFAULT  '男' )
   RETURN  VARCHAR2
AS
   V_var VARCHAR2(32);
BEGIN
   V_var :=  name || ':' ||TO_CHAR(age)|| '歲.' ||sex;
   RETURN  v_var;
END ;

    具備默認值的函數建立後,在函數調用時,若是沒有爲具備默認值的參數提供實際參數值,函數將使用該參數的默認值。但當調用者爲默認參數提供實際參數時,函數將使用實際參數值。在建立函數時,只能爲輸入參數設置默認值,而不能爲輸入/輸出參數設置默認值。

 

DECLARE
  varVARCHAR(32);
BEGIN
  Var := demo_fun( 'user1' , 30);
  DBMS_OUTPUT.PUT_LINE(var);
  Var := demo_fun( 'user2' , age => 40);
  DBMS_OUTPUT.PUT_LINE(var);
  Var := demo_fun( 'user3' , sex =>  '女' , age => 20);
  DBMS_OUTPUT.PUT_LINE(var);
END ;

 

6.3 存儲過程

6.3.1  建立過程

 

創建存儲過程

  在 ORACLE SERVER上創建存儲過程,能夠被多個應用程序調用,能夠向存儲過程傳遞參數,也能夠向存儲過程傳回參數.

  建立過程語法:

1
2
3
4
5
6
7
8
9
10
11
12
13
CREATE  [ OR  REPLACE PROCEDURE  procedure_name
([arg1 [  IN  OUT  IN  OUT  ]] type1 [ DEFAULT  value1],
  [arg2 [  IN  OUT  IN  OUT  ]] type2 [ DEFAULT  value1]],
  ......
  [argn [  IN  OUT  IN  OUT  ]] typen [ DEFAULT  valuen])
     [ AUTHID DEFINER |  CURRENT_USER  ]
IS  AS  }
   <聲明部分>
BEGIN
   <執行部分>
EXCEPTION
   <可選的異常錯誤處理程序>
END  procedure_name;

說明:相關參數說明參見函數的語法說明。

例子:使用存儲過程向departments表中插入數據。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
CREATE  OR  REPLACE
PROCEDURE  insert_dept
   (v_dept_id  IN  departments.department_id%TYPE,
    v_dept_name  IN  departments.department_name%TYPE,
    v_mgr_id  IN  departments.manager_id%TYPE,
    v_loc_id  IN  departments.location_id%TYPE)
IS
    ept_null_error EXCEPTION;
    PRAGMA EXCEPTION_INIT(ept_null_error, -1400);
    ept_no_loc_id EXCEPTION;
    PRAGMA EXCEPTION_INIT(ept_no_loc_id, -2291);
BEGIN
    INSERT  INTO  departments
    (department_id, department_name, manager_id, location_id)
    VALUES
    (v_dept_id, v_dept_name, v_mgr_id, v_loc_id);
    DBMS_OUTPUT.PUT_LINE( '插入部門' ||v_dept_id|| '成功' );
EXCEPTION
    WHEN  DUP_VAL_ON_INDEX  THEN
       RAISE_APPLICATION_ERROR(-20000,  '部門編碼不能重複' );
    WHEN  ept_null_error  THEN
       RAISE_APPLICATION_ERROR(-20001,  '部門編碼、部門名稱不能爲空' );
    WHEN  ept_no_loc_id  THEN
       RAISE_APPLICATION_ERROR(-20002,  '沒有該地點' );
END  insert_dept;
 
/*調用實例一:
DECLARE
    ept_20000 EXCEPTION;
    PRAGMA EXCEPTION_INIT(ept_20000, -20000);
    ept_20001 EXCEPTION;
    PRAGMA EXCEPTION_INIT(ept_20001, -20001);
    ept_20002 EXCEPTION;
    PRAGMA EXCEPTION_INIT(ept_20002, -20002);
BEGIN
    insert_dept(300, '部門300', 100, 2400);
    insert_dept(310, NULL, 100, 2400);
    insert_dept(310, '部門310', 100, 900);
EXCEPTION
    WHEN ept_20000 THEN
       DBMS_OUTPUT.PUT_LINE('ept_20000部門編碼不能重複');
    WHEN ept_20001 THEN
       DBMS_OUTPUT.PUT_LINE('ept_20001部門編碼、部門名稱不能爲空');
    WHEN ept_20002 THEN
       DBMS_OUTPUT.PUT_LINE('ept_20002沒有該地點');
    WHEN OTHERS THEN
       DBMS_OUTPUT.PUT_LINE('others出現了其餘異常錯誤');
END;
 
調用實例二:
DECLARE
    ept_20000 EXCEPTION;
    PRAGMA EXCEPTION_INIT(ept_20000, -20000);
    ept_20001 EXCEPTION;
    PRAGMA EXCEPTION_INIT(ept_20001, -20001);
    ept_20002 EXCEPTION;
    PRAGMA EXCEPTION_INIT(ept_20002, -20002);
BEGIN
    insert_dept(v_dept_name => '部門310', v_dept_id => 310,
                v_mgr_id => 100, v_loc_id => 2400);
    insert_dept(320, '部門320', v_mgr_id => 100, v_loc_id => 900);
EXCEPTION
    WHEN ept_20000 THEN
       DBMS_OUTPUT.PUT_LINE('ept_20000部門編碼不能重複');
    WHEN ept_20001 THEN
       DBMS_OUTPUT.PUT_LINE('ept_20001部門編碼、部門名稱不能爲空');
    WHEN ept_20002 THEN
       DBMS_OUTPUT.PUT_LINE('ept_20002沒有該地點');
    WHEN OTHERS THEN
       DBMS_OUTPUT.PUT_LINE('others出現了其餘異常錯誤');
END;
*/

 

6.3.2  調用存儲過程

 

存儲過程創建完成後,只要經過受權,用戶就能夠在SQLPLUS 、ORACLE開發工具或第三方開發工具中來調用運行。對於參數的傳遞也有三種:按位置傳遞、按名稱傳遞和組合傳遞,傳遞方法與函數的同樣。ORACLE 使用EXECUTE 語句來實現對存儲過程的調用: 

EXEC[UTE] procedure_name( parameter1, parameter2…);

例:EXECUTE logexecution;

    在PL/SQL 程序中還能夠在塊內創建本地函數和過程,這些函數和過程不存儲在數據庫中,但能夠在建立它們的PL/SQL 程序中被重複調用。本地函數和過程在PL/SQL 塊的聲明部分定義,它們的語法格式與存儲函數和過程相同,但不能使用CREATE OR REPLACE 關鍵字。

例子:創建本地過程,用於計算指定部門的工資總和,並統計其中的職工數量;

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
DECLARE
V_num NUMBER;
V_sum NUMBER(8, 2);
PROCEDURE  proc_demo
   (
     Dept_no NUMBER  DEFAULT  10,
     Sal_sum  OUT  NUMBER,
     Emp_count  OUT  NUMBER
   )
IS
BEGIN
     SELECT  SUM (salary),  COUNT (*)  INTO  sal_sum, emp_count
     FROM  employees  WHERE  department_id=dept_no;
EXCEPTION
    WHEN  NO_DATA_FOUND  THEN
       DBMS_OUTPUT.PUT_LINE( '你須要的數據不存在!' );
    WHEN  OTHERS  THEN
       DBMS_OUTPUT.PUT_LINE(SQLCODE|| '---' ||SQLERRM);
END  proc_demo;
--調用方法:
BEGIN
     Proc_demo(30, v_sum, v_num);
DBMS_OUTPUT.PUT_LINE( '30號部門工資總和:' ||v_sum|| ',人數:' ||v_num);
     Proc_demo(sal_sum => v_sum, emp_count => v_num);
DBMS_OUTPUT.PUT_LINE( '10號部門工資總和:' ||v_sum|| ',人數:' ||v_num);
END ;

 

6.3.3  AUTHID

    過程當中的AUTHID 指令能夠告訴ORACLE ,這個過程使用誰的權限運行.默任狀況下,存儲過程會做爲調用者的過程運行,可是具備設計者的特權.這稱爲設計者權利運行。

例子:創建過程,使用AUTOID CURRENT_USER;

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CONNECT  HR/qaz
 
CREATE  OR  REPLACE  PROCEDURE  logexecution
   AUTHID  CURRENT_USER
IS
BEGIN
    INSERT  INTO  logtable (userid, logdate)  VALUES  ( USER , SYSDATE);
END ;
 
GRANT  EXECUTE  ON  logexecution  TO  PUBLIC ;
 
CONNECT  testuser1/userpwd1
INSERT  INTO  HR.LOGTABLE  VALUES  ( USER , SYSDATE);
EXECUTE  HR.logexecution

 

6.3.4  PRAGMA AUTONOMOUS_TRANSACTION

        ORACLE8i 能夠支持事務處理中的事務處理的概念.這種子事務處理能夠完成它本身的工做,獨立於父事務處理進行提交或者回滾.經過使用這種方法,開發者就可以這樣的過程,不管父事務處理是提交仍是回滾,它均可以成功執行。

 

1創建過程,使用自動事務處理進行日誌記錄;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
DROP  TABLE  logtable;
 
CREATE  TABLE  logtable(
   Username varchar2(20),
   Dassate_time  date ,
   Mege varchar2(60)
);
 
CREATE  TABLE  temp_table( N number );
 
CREATE  OR  REPLACE  PROCEDURE  log_message(p_message varchar2)
   AS
   PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
   INSERT  INTO  logtable  VALUES  user , sysdate, p_message );
   COMMIT ;
END  log_message;
 
BEGIN
   Log_message (‘About  to  insert  into  temp_table‘);
   INSERT  INTO  temp_table  VALUES  (1);
   Log_message (‘ Rollback  to  insert  into  temp_table‘);
   ROLLBACK ;
END ;
 
SELECT  FROM  logtable;
SELECT  FROM  temp_table;

2創建過程,沒有使用自動事務處理進行日誌記錄; 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CREATE  OR  REPLACE  PROCEDURE  log_message(p_message varchar2)
   AS
BEGIN
   INSERT  INTO  logtable  VALUES  user , sysdate, p_message );
   COMMIT ;
END  log_message;
 
BEGIN
   Log_message ( 'About to insert into temp_table' );
   INSERT  INTO  temp_table  VALUES  (1);
   Log_message ( 'Rollback to insert into temp_table' );
   ROLLBACK ;
END ;
 
SELECT  FROM  logtable;
SELECT  FROM  temp_table;

 

6.3.5  開發存儲過程步驟

 開發存儲過程、函數、包及觸發器的步驟以下: 

6.3.5.1 使用文字編輯處理軟件編輯存儲過程源碼

    使用文字編輯處理軟件編輯存儲過程源碼,要用相似WORD 文字處理軟件進行編輯時,要將源碼存爲文本格式。 

6.3.5.2 在SQLPLUS或用調試工具將存儲過程程序進行解釋

    在SQLPLUS或用調試工具將存儲過程程序進行解釋;

    在SQL>下調試,可用START 或GET 等ORACLE命令來啓動解釋。如:

SQL>START c:\stat1.sql

    若是使用調式工具,可直接編輯和點擊相應的按鈕便可生成存儲過程。 

6.3.5.3 調試源碼直到正確

    咱們不能保證所寫的存儲過程達到一次就正確。因此這裏的調式是每一個程序員必須進行的工做之一。在SQLPLUS下來調式主要用的方法是:

l         使用 SHOW ERROR命令來提示源碼的錯誤位置;

l         使用 user_errors 數據字典來查看各存儲過程的錯誤位置。 

6.3.5.4 受權執行權給相關的用戶或角色

若是調式正確的存儲過程沒有進行受權,那就只有創建者本人才能夠運行。因此做爲應用系統的一部分的存儲過程也必須進行受權才能達到要求。在SQL*PLUS下能夠用GRANT命令來進行存儲過程的運行受權。 

  GRANT語法: 

1
2
3
4
5
6
7
8
9
10
11
GRANT  system_privilege | role
TO  user  | role |  PUBLIC  [ WITH  ADMIN  OPTION ]
 
GRANT  object_privilege |  ALL  ON  schema .object
TO  user  | role |  PUBLIC  [ WITH  GRANT  OPTION ]
 
--例子:
 
CREATE  OR  REPLACE  PUBLIC  SYNONYM dbms_job  FOR  dbms_job
 
GRANT  EXECUTE  ON  dbms_job  TO  PUBLIC  WITH  GRANT  OPTION

 

6.3.6  與過程相關數據字典

 

USER_SOURCE, ALL_SOURCE, DBA_SOURCE, USER_ERRORS,

ALL_PROCEDURES,USER_OBJECTS,ALL_OBJECTS,DBA_OBJECTS 

相關的權限:

1
2
CREATE  ANY  PROCEDURE
DROP  ANY  PROCEDURE

在SQL*PLUS 中,能夠用DESCRIBE 命令查看過程的名字及其參數表。 

1
DESC [RIBE] Procedure_name;

 

6.3.7  刪除過程函數

1.刪除過程

  可使用DROP PROCEDURE命令對不須要的過程進行刪除,語法以下:

1
DROP  PROCEDURE  [ user .]Procudure_name;

  2.刪除函數

  可使用DROP FUNCTION 命令對不須要的函數進行刪除,語法以下: 

1
2
3
4
5
6
7
8
9
10
11
12
DROP  FUNCTION  [ user .]Function_name;
 
--刪除上面實例建立的存儲過程與函數
DROP  PROCEDURE  logexecution;
DROP  PROCEDURE  delemp;
DROP  PROCEDURE  insertemp;
DROP  PROCEDURE  fireemp;
DROP  PROCEDURE  queryemp;
DROP  PROCEDURE  proc_demo;
DROP  PROCEDURE  log_message;
DROP  FUNCTION  demo_fun;
DROP  FUNCTION  get_salary;

 

6.3.8  過程與函數的比較

 

使用過程與函數具備以下優勢: 

一、共同使用的代碼能夠只須要被編寫和測試一次,而被須要該代碼的任何應用程序(如:.NET、C++、JAVA、VB程序,也能夠是DLL庫)調用。

二、這種集中編寫、集中維護更新、你們共享(或重用)的方法,簡化了應用程序的開發和維護,提升了效率與性能。

三、這種模塊化的方法,使得能夠將一個複雜的問題、大的程序逐步簡化成幾個簡單的、小的程序部分,進行分別編寫、調試。所以使程序的結構清晰、簡單,也容易實現。

四、能夠在各個開發者之間提供處理數據、控制流程、提示信息等方面的一致性。

五、節省內存空間。它們以一種壓縮的形式被存儲在外存中,當被調用時才被放入內存進行處理。而且,若是多個用戶要執行相同的過程或函數時,就只須要在內存中加載一個該過程或函數。

六、提升數據的安全性與完整性。經過把一些對數據的操做放到過程或函數中,就能夠經過是否授予用戶有執行該過程或的權限,來限制某些用戶對數據進行這些操做。 

過程與函數的相同功能有:

一、 都使用IN模式的參數傳入數據、OUT模式的參數返回數據。

二、 輸入參數均可以接受默認值,均可以傳值或傳引導。

三、 調用時的實際參數均可以使用位置表示法、名稱表示法或組合方法。

四、 都有聲明部分、執行部分和異常處理部分。

五、 其管理過程都有建立、編譯、受權、刪除、顯示依賴關係等。 

使用過程與函數的原則:

一、若是須要返回多個值和不返回值,就使用過程;若是隻須要返回一個值,就使用函數。

二、過程通常用於執行一個指定的動做,函數通常用於計算和返回一個值。

三、能夠SQL語句內部(如表達式)調用函數來完成複雜的計算問題,但不能調用過程。因此這是函數的特點。

 

*******************************************************************

---------程序包的建立與應用-----------

 

7.1  程序包簡介

 

      程序包(PACKAGE,簡稱包)是一組相關過程、函數、變量、常量和遊標等PL/SQL程序設計元素的組合,做爲一個完整的單元存儲在數據庫中,用名稱來標識包。它具備面向對象程序設計語言的特色,是對這些PL/SQL 程序設計元素的封裝。包相似於c#和JAVA語言中的類,其中變量至關於類中的成員變量,過程和函數至關於類方法。把相關的模塊歸類成爲包,可以使開發人員利用面向對象的方法進行存儲過程的開發,從而提升系統性能。

       與高級語言中的類相同,包中的程序元素也分爲公用元素和私用元素兩種,這兩種元素的區別是他們容許訪問的程序範圍不一樣,即它們的做用域不一樣。公用元素不只能夠被包中的函數、過程所調用,也能夠被包外的PL/SQL程序訪問,而私有元素只能被包內的函數和過程序所訪問。

固然,對於不包含在程序包中的過程、函數是獨立存在的。通常是先編寫獨立的過程與函數,待其較爲完善或通過充分驗證無誤後,再按邏輯相關性組織爲程序包

程序包的優勢

u       簡化應用程序設計:程序包的說明部分和包體部分能夠分別建立各編譯。主要體現     在如下三個方面:

1)        能夠在設計一個應用程序時,只建立各編譯程序包的說明部分,而後再編寫引用該                     程序包的PL/SQL塊。

2)        當完成整個應用程序的總體框架後,再回頭來定義包體部分。只要不改變包的說明部分,就能夠單獨調試、增長或替換包體的內容,這不會影響其餘的應用程序。

3)        更新包的說明後必須從新編譯引用包的應用程序,但更新包體,則不需從新編譯引用包的應用程序,以快速進行進行應用程序的原形開發。

u       模塊化:可將邏輯相關的PL/SQL塊或元素等組織在一塊兒,用名稱來惟一標識程序包。把一個大的功能模塊劃分人適當個數小的功能模塊,分別完成各自的功能。這樣組織的程序包都易於編寫,易於理解更易於管理。

u       信息隱藏:由於包中的元素能夠分爲公有元素和私有元素。公有元素可被程序包內的過程、函數等的訪問,還能夠被包外的PL/SQL訪問。但對於私有元素只能被包內的過程、函數等訪問。對於用戶,只需知道包的說明,不用瞭解包休的具體細節。

u       效率高:程序包在應用程序第一次調用程序包中的某個元素時,ORACLE將把整個程序包加載到內存中,當第二次訪問程序包中的元素時,ORACLE將直接從內在中讀取,而不須要進行磁盤I/O操做而影響速度,同時位於內在中的程序包可被同一會話期間的其它應用程序共享。所以,程序包增長了重用性並改善了多用戶、多應用程序環境的效率。

對程序包的優勢可總結以下:在PL/SQL程序設計中,使用包不只可使程序設計模塊化,對外隱藏包內所使用的信息(經過使用私用變量),而寫能夠提升程序的執行效率。由於,當程序首次調用包內函數或過程時,ORACLE將整個包調入內存,當再次訪問包內元素時,ORACLE直接從內存中讀取,而不須要進行磁盤I/O操做,從而使程序執行效率獲得提升。

    一個包由兩個分開的部分組成:

    包說明(PACKAGE):包說明部分聲明包內數據類型、變量、常量、遊標、子程序和異常錯誤處理等元素,這些元素爲包的公有元素。

    包主體(PACKAGE BODY):包主體則是包定義部分的具體實現,它定義了包定義部分所聲明的遊標和子程序,在包主體中還能夠聲明包的私有元素。

    包說明和包主體分開編譯,並做爲兩部分分開的對象存放在數據庫字典中,可查看數據字典user_source, all_source, dba_source,分別瞭解包說明與包主體的詳細信息。

 

 

7.2  程序包的定義

 

程序包的定義分爲程序包說明定義和程序包主體定義兩部分組成。

程序包說明用於聲明包的公用組件,如變量、常量、自定義數據類型、異常、過程、函數、遊標等。包說明中定義的公有組件不只能夠在包內使用,還能夠由包外其餘過程、函數。但須要說明與注意的是,咱們爲了實現信息的隱藏,建議不要將全部組件都放在包說明處聲明,只應把公共組件放在包聲明部分。包的名稱是惟一的,但對於兩個包中的公有組件的名稱能夠相同,這種用「包名.公有組件名「加以區分。

包體是包的具體實現細節,其實如今包說明中聲明的全部公有過程、函數、遊標等。固然也能夠在包體中聲明僅屬於本身的私有過程、函數、遊標等。建立包體時,有如下幾點須要注意:

u       包體只能在包說明被建立或編譯後才能進行建立或編譯。

u       在包體中實現的過程、函數、遊標的名稱必須與包說明中的過程、函數、遊標一致,包括名稱、參數的名稱以及參數的模式(IN、OUT、IN OUT)。並建設按包說明中的次序定義包體中具體的實現。

u       在包體中聲明的數據類型、變量、常量都是私有的,只能在包體中使用而不能被印刷體外的應用程序訪問與使用。

u       在包體執行部分,可對包說明,包體中聲明的公有或私有變量進行初始化或其它設置。

建立程序包說明語法格式:

CREATE [OR REPLACE] PACKAGE package_name
  [AUTHID {CURRENT_USER | DEFINER}]
  {IS | AS}
  [公有數據類型定義[公有數據類型定義]…]
  [公有遊標聲明[公有遊標聲明]…]
  [公有變量、常量聲明[公有變量、常量聲明]…]
  [公有函數聲明[公有函數聲明]…]
  [公有過程聲明[公有過程聲明]…]
END [package_name];

 

其中:AUTHIDCURRENT_USER和AUTHIDDEFINER選項說明應用程序在調用函數時所使用的權限模式,它們與CREATEFUNCTION語句中invoker_right_clause子句的做用相同。

 

建立程序包主體語法格式:

CREATE [OR REPLACE] PACKAGE BODY package_name
  {IS | AS}
  [私有數據類型定義[私有數據類型定義]…]
  [私有變量、常量聲明[私有變量、常量聲明]…]
  [私有異常錯誤聲明[私有異常錯誤聲明]…]
  [私有函數聲明和定義[私有函數聲明和定義]…]
  [私有函過程聲明和定義[私有函過程聲明和定義]…]
  [公有遊標定義[公有遊標定義]…]
  [公有函數定義[公有函數定義]…]
  [公有過程定義[公有過程定義]…]
BEGIN
  執行部分(初始化部分)
END package_name;

其中:在包主體定義公有程序時,它們必須與包定義中所聲明子程序的格式徹底一致。

 

 

7.3  包的開發步驟

 

與開發存儲過程相似,包的開發須要幾個步驟:

1.   將每一個存儲過程調式正確;

2.   用文本編輯軟件將各個存儲過程和函數集成在一塊兒;

3.   按照包的定義要求將集成的文本的前面加上包定義;

4.   按照包的定義要求將集成的文本的前面加上包主體;

5.   使用SQLPLUS或開發工具進行調式。

 

 

7.4  包定義的說明

 對包內共有元素的調用格式爲:包名.元素名稱

 

 

7.5  子程序重載

       PL/SQL 容許對包內子程序和本地子程序進行重載。所謂重載時指兩個或多個子程序有相同的名稱,但擁有不一樣的參數變量、參數順序或參數數據類型。

 

 

7.6  加密實用程序

 

ORACLE 提供了一個實用工具來加密或者包裝用戶的PL/SQL,它會將用戶的PL/SQL改變爲只有ORACLE可以解釋的代碼版本.

WRAP 實用工具位於$ORACLE_HOME/BIN。 

格式爲:

WRAP INAME=<input_file_name> [ONAME=<output_file_name>]

wrap iname=e:\sample.txt

注意:在加密前,請將PL/SQL程序先保存一份,以備後用。

 

 

7.7  刪除包

可使用 DROP PACKAGE 命令對不須要的包進行刪除,語法以下:

DROP PACKAGE [BODY] [user.]package_name;

DROP PROCEDURE OpenCurType; --刪除存儲過程
--刪除咱們實例中建立的各個包
DROP PACKAGE demo_pack;
DROP PACKAGE demo_pack1;
DROP PACKAGE emp_mgmt;
DROP PACKAGE emp_package;

 

 

7.8  包的管理

 

包與過程、函數同樣,也是存儲在數據庫中的,能夠隨時查看其源碼。如有須要,在建立包時能夠隨時查看更詳細的編譯錯誤。不須要的包也能夠刪除。

一樣,爲了不調用的失敗,在更新表的結構後,必定要記得從新編譯依賴於它的程序包。在更新了包說明或包體後,也應該從新編譯包說明與包體。語法以下:

ALTER PACKAGE package_name COMPILE [PACKAGE|BODY|SPECIFICATION];

也能夠經過如下數據字典視圖查看包的相關。

DBA_SOURCE, USER_SOURCE, USER_ERRORS,DBA-OBJECTS 

 

******************************************************************

----------把觸發器說透----------

    觸發器是許多關係數據庫系統都提供的一項技術。在ORACLE系統裏,觸發器相似過程和函數,都有聲明,執行和異常處理過程的PL/SQL塊。

 

8.1 觸發器類型

    觸發器在數據庫裏以獨立的對象存儲,它與存儲過程和函數不一樣的是,存儲過程與函數須要用戶顯示調用才執行,而觸發器是由一個事件來啓動運行。即觸發器是當某個事件發生時自動地隱式運行。而且,觸發器不能接收參數。因此運行觸發器就叫觸發或點火(firing)。ORACLE事件指的是對數據庫的表進行的INSERT、UPDATE及DELETE操做或對視圖進行相似的操做。ORACLE將觸發器的功能擴展到了觸發ORACLE,如數據庫的啓動與關閉等。因此觸發器經常使用來完成由數據庫的完整性約束難以完成的複雜業務規則的約束,或用來監視對數據庫的各類操做,實現審計的功能。

8.1.1  DML觸發器

ORACLE能夠在DML語句進行觸發,能夠在DML操做前或操做後進行觸發,而且能夠對每一個行或語句操做上進行觸發。

8.1.2  替代觸發器

    因爲在ORACLE裏,不能直接對由兩個以上的表創建的視圖進行操做。因此給出了替代觸發器。它就是ORACLE 8專門爲進行視圖操做的一種處理方法。

8.1.3  系統觸發器

 

ORACLE 8i 提供了第三種類型的觸發器叫系統觸發器。它能夠在ORACLE數據庫系統的事件中進行觸發,如ORACLE系統的啓動與關閉等。 

  觸發器組成: 

  l   觸發事件:引發觸發器被觸發的事件。 例如:DML語句(INSERT, UPDATE, DELETE語句對錶或視圖執行數據處理操做)、DDL語句(如CREATE、ALTER、DROP語句在數據庫中建立、修改、刪除模式對象)、數據庫系統事件(如系統啓動或退出、異常錯誤)、用戶事件(如登陸或退出數據庫)。

  l   觸發時間:即該TRIGGER 是在觸發事件發生以前(BEFORE)仍是以後(AFTER)觸發,也就是觸發事件和該TRIGGER 的操做順序。

  l   觸發操做:即該TRIGGER 被觸發以後的目的和意圖,正是觸發器自己要作的事情。 例如:PL/SQL 塊。

  l   觸發對象:包括表、視圖、模式、數據庫。只有在這些對象上發生了符合觸發條件的觸發事件,纔會執行觸發操做。

  l   觸發條件:由WHEN子句指定一個邏輯表達式。只有當該表達式的值爲TRUE時,遇到觸發事件纔會自動執行觸發器,使其執行觸發操做。

  l   觸發頻率:說明觸發器內定義的動做被執行的次數。即語句級(STATEMENT)觸發器和行級(ROW)觸發器。

語句級(STATEMENT)觸發器:是指當某觸發事件發生時,該觸發器只執行一次;

行級(ROW)觸發器:是指當某觸發事件發生時,對受到該操做影響的每一行數據,觸發器都單獨執行一次。


編寫觸發器時,須要注意如下幾點:

 

    ​    ​    ​l  觸發器不接受參數。

    ​    ​    ​l   一個表上最多可有12個觸發器,但同一時間、同一事件、同一類型的觸發器只能有一個。並各觸發器之間不能有矛盾。

    ​    ​    ​l  在一個表上的觸發器越多,對在該表上的DML操做的性能影響就越大。

    ​    ​    ​l  觸發器最大爲32KB。若確實須要,能夠先創建過程,而後在觸發器中用CALL語句進行調用。

    ​    ​    ​l  在觸發器的執行部分只能用DML語句(SELECT、INSERT、UPDATE、DELETE),不能使用DDL語句(CREATE、ALTER、DROP)

    ​    ​    ​l 觸發器中不能包含事務控制語句(COMMIT,ROLLBACK,SAVEPOINT)。由於觸發器是觸發語句的一部分,觸發語句被提交、回退時,觸發器也被提交、回退了。

    ​    ​    ​l  在觸發器主體中調用的任何過程、函數,都不能使用事務控制語句。

    ​    ​    ​l  在觸發器主體中不能申明任何Long和blob變量。新值new和舊值old也不能向表中的任何long和blob列。

    ​    ​    ​l  不一樣類型的觸發器(如DML觸發器、INSTEAD OF觸發器、系統觸發器)的語法格式和做用有較大區別。 

 

8.2 建立觸發器

 

建立觸發器的通常語法是: 

1
2
3
4
5
6
7
8
9
CREATE  [ OR  REPLACE TRIGGER  trigger_name
{BEFORE |  AFTER  }
{ INSERT  DELETE  UPDATE  [ OF  column  [,  column  …]]}
[ OR  { INSERT  DELETE  UPDATE  [ OF  column  [,  column  …]]}...]
ON  [ schema .]table_name | [ schema .]view_name
[REFERENCING {OLD [ AS ] old | NEW [ AS ] new| PARENT  as  parent}]
[ FOR  EACH ROW ]
[ WHEN  condition]
PL/SQL_BLOCK | CALL procedure_name;

其中:

BEFORE 和AFTER指出觸發器的觸發時序分別爲前觸發和後觸發方式,前觸發是在執行觸發事件以前觸發當前所建立的觸發器,後觸發是在執行觸發事件以後觸發當前所建立的觸發器。

       FOR EACH ROW選項說明觸發器爲行觸發器。行觸發器和語句觸發器的區別表如今:行觸發器要求當一個DML語句操走影響數據庫中的多行數據時,對於其中的每一個數據行,只要它們符合觸發約束條件,均激活一次觸發器;而語句觸發器將整個語句操做做爲觸發事件,當它符合約束條件時,激活一次觸發器。當省略FOR EACH ROW 選項時,BEFORE 和AFTER 觸發器爲語句觸發器,而INSTEAD OF 觸發器則只能爲行觸發器。

            REFERENCING 子句說明相關名稱,在行觸發器的PL/SQL塊和WHEN 子句中可使用相關名稱參照當前的新、舊列值,默認的相關名稱分別爲OLD和NEW。觸發器的PL/SQL塊中應用相關名稱時,必須在它們以前加冒號(:),但在WHEN子句中則不能加冒號。

WHEN 子句說明觸發約束條件。Condition 爲一個邏輯表達時,其中必須包含相關名稱,而不能包含查詢語句,也不能調用PL/SQL 函數。WHEN 子句指定的觸發約束條件只能用在BEFORE 和AFTER 行觸發器中,不能用在INSTEAD OF 行觸發器和其它類型的觸發器中。

    當一個基表被修改( INSERT, UPDATE, DELETE)時要執行的存儲過程,執行時根據其所依附的基表改動而自動觸發,所以與應用程序無關,用數據庫觸發器能夠保證數據的一致性和完整性。

 

每張表最多可創建12 種類型的觸發器,它們是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
BEFORE  INSERT
BEFORE  INSERT  FOR  EACH ROW
AFTER  INSERT
AFTER  INSERT  FOR  EACH ROW
  
 
BEFORE  UPDATE
BEFORE  UPDATE  FOR  EACH ROW
AFTER  UPDATE
AFTER  UPDATE  FOR  EACH ROW
  
 
BEFORE  DELETE
BEFORE  DELETE  FOR  EACH ROW
AFTER  DELETE
AFTER  DELETE  FOR  EACH ROW 

 

8.2.1  觸發器觸發次序

1.        執行 BEFORE語句級觸發器;

2.        對與受語句影響的每一行:

l         執行 BEFORE行級觸發器

l         執行 DML語句

l         執行 AFTER行級觸發器 

3.        執行 AFTER語句級觸發器

 

8.2.2  建立DML觸發器

 

    ​    ​   ​觸發器名與過程名和包的名字不同,它是單獨的名字空間,於是觸發器名能夠和表或過程有相同的名字,但在一個模式中觸發器名不能相同。 

    ​    ​    ​DML觸發器的限制

    ​    ​    ​l         CREATE TRIGGER語句文本的字符長度不能超過32KB;

    ​    ​    ​l         觸發器體內的SELECT 語句只能爲SELECT … INTO …結構,或者爲定義遊標所使用的SELECT 語句。

    ​    ​    ​l         觸發器中不能使用數據庫事務控制語句 COMMIT; ROLLBACK, SVAEPOINT 語句;

    ​    ​    ​l         由觸發器所調用的過程或函數也不能使用數據庫事務控制語句;

    ​    ​    ​l         觸發器中不能使用LONG, LONG RAW 類型;

    ​    ​    ​l         觸發器內能夠參照LOB 類型列的列值,但不能經過 :NEW 修改LOB列中的數據; 

    ​    ​    ​DML觸發器基本要點

    ​    ​    ​l         觸發時機:指定觸發器的觸發時間。若是指定爲BEFORE,則表示在執行DML操做以前觸發,以便防止某些錯誤操做發生或實現某些業務規則;若是指定爲AFTER,則表示在執行DML操做以後觸發,以便記錄該操做或作某些過後處理。

    ​    ​    ​l         觸發事件:引發觸發器被觸發的事件,即DML操做(INSERT、UPDATE、DELETE)。既能夠是單個觸發事件,也能夠是多個觸發事件的組合(只能使用OR邏輯組合,不能使用AND邏輯組合)。

    ​    ​    ​l         條件謂詞:當在觸發器中包含多個觸發事件(INSERT、UPDATE、DELETE)的組合時,爲了分別針對不一樣的事件進行不一樣的處理,須要使用ORACLE提供的以下條件謂詞。

    ​    ​    ​1)。INSERTING當觸發事件是INSERT時,取值爲TRUE,不然爲FALSE。

    ​    ​    ​2)。UPDATING [column_1,column_2,…,column_x]當觸發事件是UPDATE      時,若是修改了column_x列,則取值爲TRUE,不然爲FALSE。其中column_x是可選的。

    ​    ​    ​3)。DELETING當觸發事件是DELETE時,則取值爲TRUE,不然爲FALSE。

    ​    ​    ​解發對象:指定觸發器是建立在哪一個表、視圖上。

    ​    ​    ​l         觸發類型:是語句級仍是行級觸發器。

    ​    ​    ​l         觸發條件:由WHEN子句指定一個邏輯表達式,只容許在行級觸發器上指定觸發條件,指定UPDATING後面的列的列表。 

    ​    ​    ​  問題:當觸發器被觸發時,要使用被插入、更新或刪除的記錄中的列值,有時要使用操做前、        後列的值.

    ​    ​    ​  實現:  :NEW 修飾符訪問操做完成後列的值

 

 :OLD 修飾符訪問操做完成前列的值 

特性

INSERT

UPDATE

DELETE

OLD

NULL

實際值

實際值

NEW

實際值

實際值

NULL

例1: 創建一個觸發器, 當職工表 emp 表被刪除一條記錄時,把被刪除記錄寫到職工表刪除日誌表中去。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
CREATE  TABLE  emp_his  AS  SELECT  FROM  EMP  WHERE  1=2;
CREATE  OR  REPLACE  TRIGGER  tr_del_emp
    BEFORE  DELETE  --指定觸發時機爲刪除操做前觸發
    ON  scott.emp
    FOR  EACH ROW    --說明建立的是行級觸發器
BEGIN
    --將修改前數據插入到日誌記錄表 del_emp ,以供監督使用。
    INSERT  INTO  emp_his(deptno , empno, ename , job ,mgr , sal , comm , hiredate )
        VALUES ( :old.deptno, :old.empno, :old.ename , :old.job,:old.mgr, :old.sal, :old.comm, :old.hiredate );
END ;
DELETE  emp  WHERE  empno=7788;
DROP  TABLE  emp_his;
DROP  TRIGGER  del_emp;

例2:在觸發器中調用過程。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
CREATE  OR  REPLACE  PROCEDURE  add_job_history
  ( p_emp_id          job_history.employee_id%type
    , p_start_date      job_history.start_date%type
   , p_end_date        job_history.end_date%type
    , p_job_id          job_history.job_id%type
    , p_department_id   job_history.department_id%type
    )
IS
BEGIN
  INSERT  INTO  job_history (employee_id, start_date, end_date,
                            job_id, department_id)
   VALUES (p_emp_id, p_start_date, p_end_date, p_job_id, p_department_id);
END  add_job_history;
 
--建立觸發器調用存儲過程...
CREATE  OR  REPLACE  TRIGGER  update_job_history
  AFTER  UPDATE  OF  job_id, department_id  ON  employees
  FOR  EACH ROW
BEGIN
  add_job_history(:old.employee_id, :old.hire_date, sysdate,
                   :old.job_id, :old.department_id);
END ;

 

8.2.3  建立替代(INSTEAD OF)觸發器

 

建立觸發器的通常語法是: 

1
2
3
4
5
6
7
8
9
CREATE  [ OR  REPLACE TRIGGER  trigger_name
INSTEAD  OF
{ INSERT  DELETE  UPDATE  [ OF  column  [,  column  …]]}
[ OR  { INSERT  DELETE  UPDATE  [ OF  column  [,  column  …]]}...]
ON  [ schema .] view_name  --只能定義在視圖上
[REFERENCING {OLD [ AS ] old | NEW [ AS ] new| PARENT  as  parent}]
[ FOR  EACH ROW ]  --由於INSTEAD OF觸發器只能在行級上觸發,因此沒有必要指定
[ WHEN  condition]
PL/SQL_block | CALL procedure_name;

其中:

            INSTEAD OF 選項使ORACLE激活觸發器,而不執行觸發事件。只能對視圖和對象視圖創建INSTEAD OF觸發器,而不能對錶、模式和數據庫創建INSTEAD OF 觸發器。

            FOR EACH ROW選項說明觸發器爲行觸發器。行觸發器和語句觸發器的區別表如今:行觸發器要求當一個DML語句操走影響數據庫中的多行數據時,對於其中的每一個數據行,只要它們符合觸發約束條件,均激活一次觸發器;而語句觸發器將整個語句操做做爲觸發事件,當它符合約束條件時,激活一次觸發器。當省略FOR EACH ROW 選項時,BEFORE 和AFTER 觸發器爲語句觸發器,而INSTEAD OF 觸發器則爲行觸發器。

            REFERENCING 子句說明相關名稱,在行觸發器的PL/SQL塊和WHEN 子句中可使用相關名稱參照當前的新、舊列值,默認的相關名稱分別爲OLD和NEW。觸發器的PL/SQL塊中應用相關名稱時,必須在它們以前加冒號(:),但在WHEN子句中則不能加冒號。

WHEN 子句說明觸發約束條件。Condition 爲一個邏輯表達時,其中必須包含相關名稱,而不能包含查詢語句,也不能調用PL/SQL 函數。WHEN 子句指定的觸發約束條件只能用在BEFORE 和AFTER 行觸發器中,不能用在INSTEAD OF 行觸發器和其它類型的觸發器中。 

    INSTEAD_OF 用於對視圖的DML觸發,因爲視圖有多是由多個表進行聯結(join)而成,於是並不是是全部的聯結都是可更新的。但能夠按照所需的方式執行更新,例以下面狀況:

 

1 

1
2
3
CREATE  OR  REPLACE  VIEW  emp_view  AS
SELECT  deptno,  count (*) total_employeer,  sum (sal) total_salary
FROM  emp  GROUP  BY  deptno;

  在此視圖中直接刪除是非法: 

1
2
SQL> DELETE  FROM  emp_view  WHERE  deptno=10;
DELETE  FROM  emp_view  WHERE  deptno=10

   ERROR 位於第 1 行:

  ORA-01732: 此視圖的數據操縱操做非法 

  可是咱們能夠建立INSTEAD_OF觸發器來爲 DELETE 操做執行所需的處理,即刪除EMP表中全部基準行:  

1
2
3
4
5
6
7
8
9
10
11
CREATE  OR  REPLACE  TRIGGER  emp_view_delete
    INSTEAD  OF  DELETE  ON  emp_view  FOR  EACH ROW
BEGIN
    DELETE  FROM  emp  WHERE  deptno= :old.deptno;
END  emp_view_delete;
 
DELETE  FROM  emp_view  WHERE  deptno=10;
 
DROP  TRIGGER  emp_view_delete;
 
DROP  VIEW  emp_view;

  例2建立複雜視圖,針對INSERT操做建立INSTEAD OF觸發器,向複雜視圖插入數據。

  l         建立視圖:

 

1
2
3
4
5
6
7
8
9
CREATE  OR  REPLACE  FORCE  VIEW  "HR" . "V_REG_COU"  ( "R_ID" "R_NAME" "C_ID" "C_NAME" )
AS
  SELECT  r.region_id,
     r.region_name,
     c.country_id,
     c.country_name
  FROM  regions r,
     countries c
  WHERE  r.region_id = c.region_id;

  l         建立觸發器: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
CREATE  OR  REPLACE  TRIGGER  "HR" . "TR_I_O_REG_COU"  INSTEAD  OF
  INSERT  ON  v_reg_cou  FOR  EACH ROW  DECLARE  v_count NUMBER;
BEGIN
  SELECT  COUNT (*)  INTO  v_count  FROM  regions  WHERE  region_id = :new.r_id;
  IF v_count = 0  THEN
     INSERT  INTO  regions
       (region_id, region_name
       VALUES
       (:new.r_id, :new.r_name
       );
  END  IF;
 
  SELECT  COUNT (*)  INTO  v_count  FROM  countries  WHERE  country_id = :new.c_id;
  IF v_count = 0  THEN
     INSERT
     INTO  countries
       (
         country_id,
         country_name,
         region_id
       )
       VALUES
       (
         :new.c_id,
         :new.c_name,
         :new.r_id
       );
  END  IF;
END ;

    ​    ​建立INSTEAD OF觸發器須要注意如下幾點:

    ​    ​l         只能被建立在視圖上,而且該視圖沒有指定WITH CHECK OPTION選項。

    ​    ​l         不能指定BEFORE 或 AFTER選項。

    ​    ​l         FOR EACH ROW子但是可選的,即INSTEAD OF觸發器只能在行級上觸發、或只能是行級觸發器,沒有必要指定。

    ​    ​l         沒有必要在針對一個表的視圖上建立INSTEAD OF觸發器,只要建立DML觸發器就能夠了。


8.2.4  建立系統事件觸發器

 

ORACLE10G提供的系統事件觸發器能夠在DDL或數據庫系統上被觸發。DDL指的是數據定義語言,如CREATE 、ALTER及DROP 等。而數據庫系統事件包括數據庫服務器的啓動或關閉,用戶的登陸與退出、數據庫服務錯誤等。建立系統觸發器的語法以下: 

  建立觸發器的通常語法是: 

1
2
3
4
5
6
CREATE  OR  REPLACE  TRIGGER  [sachema.]trigger_name
{BEFORE| AFTER }
{ddl_event_list | database_event_list}
ON  DATABASE  | [ schema .] SCHEMA  }
[ WHEN  condition]
PL/SQL_block | CALL procedure_name;

  其中: ddl_event_list:一個或多個DDL 事件,事件間用 OR 分開;

           database_event_list:一個或多個數據庫事件,事件間用 OR 分開; 

            系統事件觸發器既能夠創建在一個模式上,又能夠創建在整個數據庫上。當創建在模式(SCHEMA)之上時,只有模式所指定用戶的DDL操做和它們所致使的錯誤才激活觸發器, 默認時爲當前用戶模式。當創建在數據庫(DATABASE)之上時,該數據庫全部用戶的DDL操做和他們所致使的錯誤,以及數據庫的啓動和關閉都可激活觸發器。要在數據庫之上創建觸發器時,要求用戶具備ADMINISTER DATABASE TRIGGER權限。 

  下面給出系統觸發器的種類和事件出現的時機(前或後):

事件

容許的時機

說明

STARTUP

AFTER

啓動數據庫實例以後觸發

SHUTDOWN

BEFORE

關閉數據庫實例以前觸發(非正常關閉不觸發)

SERVERERROR

AFTER

數據庫服務器發生錯誤以後觸發

LOGON

AFTER

成功登陸鏈接到數據庫後觸發

LOGOFF

BEFORE

開始斷開數據庫鏈接以前觸發

CREATE

BEFORE,AFTER

在執行CREATE語句建立數據庫對象以前、以後觸發

DROP

BEFORE,AFTER

在執行DROP語句刪除數據庫對象以前、以後觸發

ALTER

BEFORE,AFTER

在執行ALTER語句更新數據庫對象以前、以後觸發

DDL

BEFORE,AFTER

在執行大多數DDL語句以前、以後觸發

GRANT

BEFORE,AFTER

執行GRANT語句授予權限以前、以後觸發

REVOKE

BEFORE,AFTER

執行REVOKE語句收權限以前、以後觸犯發

RENAME

BEFORE,AFTER

執行RENAME語句更改數據庫對象名稱以前、以後觸犯發

AUDIT NOAUDIT

BEFORE,AFTER

執行AUDITNOAUDIT進行審計或中止審計以前、以後觸發

 

8.2.5  系統觸發器事件屬性

 

事件屬性\事件

Startup/Shutdown

Servererror

Logon/Logoff

DDL

DML

事件名稱

數據庫名稱

       

數據庫實例號

       

錯誤號

 

     

用戶名

   

 

模式對象類型

     

模式對象名稱

     

       

   除DML語句的列屬性外,其他事件屬性值可經過調用ORACLE定義的事件屬性函數來讀取。

函數名稱

數據類型

說    明

Ora_sysevent

VARCHAR2(20)

激活觸發器的事件名稱

Instance_num

NUMBER

數據庫實例名

Ora_database_name

VARCHAR2(50)

數據庫名稱

Server_error(posi)

NUMBER

錯誤信息棧中posi指定位置中的錯誤號

 

 

Is_servererror(err_number)

 

 

BOOLEAN

檢查err_number指定的錯誤號是否在錯誤信息棧中,若是在則返回TRUE,不然返回FALSE。在觸發器內調用此函數能夠判斷是否發生指定的錯誤。

Login_user

VARCHAR2(30)

登錄或註銷的用戶名稱

Dictionary_obj_type

VARCHAR2(20)

DDL語句所操做的數據庫對象類型

Dictionary_obj_name

VARCHAR2(30)

DDL語句所操做的數據庫對象名稱

Dictionary_obj_owner

VARCHAR2(30)

DDL語句所操做的數據庫對象全部者名稱

Des_encrypted_password

VARCHAR2(2)

正在建立或修改的通過DES算法加密的用戶口令

 

1建立觸發器,存放有關事件信息。 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
DESC  ora_sysevent
DESC  ora_login_user
 
--建立用於記錄事件用的表
 
CREATE  TABLE  ddl_event
(crt_date  timestamp  PRIMARY  KEY ,
  event_name VARCHAR2(20),
  user_name VARCHAR2(10),
  obj_type VARCHAR2(20),
  obj_name VARCHAR2(20));
 
--建立觸犯發器
CREATE  OR  REPLACE  TRIGGER  tr_ddl
AFTER  DDL  ON  SCHEMA
BEGIN
    INSERT  INTO  ddl_event  VALUES
    (systimestamp,ora_sysevent, ora_login_user,
     ora_dict_obj_type, ora_dict_obj_name);
END  tr_ddl;

  例2建立登陸、退出觸發器。 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CREATE  TABLE  log_event
(user_name VARCHAR2(10),
  address VARCHAR2(20),
  logon_date  timestamp ,
  logoff_date  timestamp );
 
--建立登陸觸發器
CREATE  OR  REPLACE  TRIGGER  tr_logon
AFTER  LOGON  ON  DATABASE
BEGIN
    INSERT  INTO  log_event (user_name, address, logon_date)
    VALUES  (ora_login_user, ora_client_ip_address, systimestamp);
END  tr_logon;
--建立退出觸發器
CREATE  OR  REPLACE  TRIGGER  tr_logoff
BEFORE LOGOFF  ON  DATABASE
BEGIN
    INSERT  INTO  log_event (user_name, address, logoff_date)
    VALUES  (ora_login_user, ora_client_ip_address, systimestamp);
END  tr_logoff;

 

8.2.6  使用觸發器謂詞

 

 ORACLE 提供三個參數INSERTING, UPDATING, DELETING 用於判斷觸發了哪些操做。

謂詞

行爲

INSERTING

若是觸發語句是 INSERT 語句,則爲TRUE,不然爲FALSE

UPDATING

若是觸發語句是 UPDATE語句,則爲TRUE,不然爲FALSE

DELETING

若是觸發語句是 DELETE 語句,則爲TRUE,不然爲FALSE


8.2.7  從新編譯觸發器

 

若是在觸發器內調用其它函數或過程,當這些函數或過程被刪除或修改後,觸發器的狀態將被標識爲無效。當DML語句激活一個無效觸發器時,ORACLE將從新編譯觸發器代碼,若是編譯時發現錯誤,這將致使DML語句執行失敗。

在PL/SQL程序中能夠調用ALTER TRIGGER語句從新編譯已經建立的觸發器,格式爲:      

1
ALTER  TRIGGER  [ schema .] trigger_name COMPILE [ DEBUG]

其中:DEBUG 選項要器編譯器生成PL/SQL 程序條使其所使用的調試代碼。

 

8.3 刪除和使能觸發器

 

l         刪除觸發器: 

 

1
DROP  TRIGGER  trigger_name;

 

  當刪除其餘用戶模式中的觸發器名稱,須要具備DROP ANY TRIGGER系統權限,當刪除創建在數據庫上的觸發器時,用戶須要具備ADMINISTER DATABASE TRIGGER系統權限。

此外,當刪除表或視圖時,創建在這些對象上的觸發器也隨之刪除。 

l         禁用或啓用觸發器

數據庫TRIGGER 的狀態:

有效狀態(ENABLE):當觸發事件發生時,處於有效狀態的數據庫觸發器TRIGGER 將被觸發。

無效狀態(DISABLE):當觸發事件發生時,處於無效狀態的數據庫觸發器TRIGGER 將不會被觸發,此時就跟沒有這個數據庫觸發器(TRIGGER) 同樣。

數據庫TRIGGER的這兩種狀態能夠互相轉換。格式爲: 

1
2
3
ALTER  TIGGER trigger_name [DISABLE | ENABLE ];
 
--例:ALTER TRIGGER emp_view_delete DISABLE;

   ALTER TRIGGER語句一次只能改變一個觸發器的狀態,而ALTER TABLE語句則一次可以改變與指定表相關的全部觸發器的使用狀態。格式爲: 

1
2
3
ALTER  TABLE  [ schema .]table_name {ENABLE|DISABLE}  ALL  TRIGGERS;
--例:使表EMP 上的全部TRIGGER 失效:
ALTER  TABLE  emp DISABLE  ALL  TRIGGERS;

 

 

8.4 觸發器和數據字典

 

相關數據字典:USER_TRIGGERS、ALL_TRIGGERS、DBA_TRIGGERS 

1
2
3
4
SELECT  TRIGGER_NAME, TRIGGER_TYPE, TRIGGERING_EVENT,
TABLE_OWNER, BASE_OBJECT_TYPE, REFERENCING_NAMES,
STATUS, ACTION_TYPE
FROM  user_triggers;

8.5   數據庫觸發器的應用舉例

 

 例建立INSTEAD OF 觸發器。首先建立一個視圖myview, 因爲該視圖是複合查詢所產生的視圖,因此不能執行DML語句。根據用戶對視圖所插入的數據判斷須要將數據插入到哪一個視圖基表中,而後對該基表執行插入操做。 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
DECLARE
     No  NUMBER;
     Name  VARCHAR2(20);
BEGIN
     DBMS_UTILITY.EXEC_DDL_STATEMENT( '
         CREATE OR REPLACE VIEW myview AS
             SELECT empno, ename, ' 'E' ' type FROM emp
             UNION
             SELECT dept.deptno, dname, ' 'D' ' FROM dept
     ' );
     -- 建立INSTEAD OF 觸發器trigger3;
     DBMS_UTILITY.EXEC_DDL_STATEMENT( '
         CREATE OR REPLACE TRIGGER trig3
             INSTEAD OF INSERT ON myview
             REFERENCING NEW n
             FOR EACH ROW
         DECLARE
             Rows INTEGER;
         BEGIN
             DBMS_OUTPUT.PUT_LINE(' '正在執行trig3觸發器…' ');
             IF :n.type = ' 'D' ' THEN
                 SELECT COUNT(*) INTO rows
                     FROM dept WHERE deptno = :n.empno;
                 IF rows = 0 THEN
                     DBMS_OUTPUT.PUT_LINE(' '向dept表中插入數據…' ');
                     INSERT INTO dept(deptno, dname, loc)
                         VALUES (:n.empno, :n.ename, ' 'none’’);
                 ELSE
                     DBMS_OUTPUT.PUT_LINE(' '編號爲' '|| :n.empno||
                      ' '的部門已存在,插入操做失敗!' ');
                  END IF;
             ELSE
                 SELECT COUNT(*) INTO rows
                     FROM emp WHERE empno = :n.empno;
                 IF rows = 0 THEN
                     DBMS_OUTPUT.PUT_LINE(' ’向emp表中插入數據…’’);
                     INSERT  INTO  emp(empno, ename)
                         VALUES (:n.empno, :n.ename);
                 ELSE
                     DBMS_OUTPUT.PUT_LINE( '' 編號爲 '' || :n.empno||
                       '' 的人員已存在,插入操做失敗! '' );
                 END  IF;
             END  IF;
         END ;
     ');
 
     INSERT INTO myview VALUES (70, ' demo ', ' D ');
     INSERT INTO myview VALUES (9999, USER, ' E ');
     SELECT deptno, dname INTO no, name FROM dept WHERE deptno=70;
     DBMS_OUTPUT.PUT_LINE(' 員工編號: '||TO_CHAR(no)||' 姓名: '||name);
     SELECT empno, ename INTO no, name FROM emp WHERE empno=9999;
     DBMS_OUTPUT.PUT_LINE(' 部門編號: '||TO_CHAR(no)||' 姓名: '||name);
   DELETE FROM emp WHERE empno=9999;
   DELETE FROM dept WHERE deptno=70;
     DBMS_UTILITY.EXEC_DDL_STATEMENT(' DROP  TRIGGER  trig3');
END ;

 

8.6   數據庫觸發器的應用實例

 

用戶可使用數據庫觸發器實現各類功能:

  l         複雜的審計功能;

l         加強數據的完整性管理;

l         幫助實現安全控制;

 

    ​    ​ l         管理複雜的表複製;

    ​    ​ l         防止非法的事務發生;

    ​    ​ l         自動生成派生的列值;



 

 

謝謝各位博友的支持!

未經博主贊成不得隨意轉載。

如需轉載請註明出處:http://www.cnblogs.com/ZRJ-boke/p/6602452.html

相關文章
相關標籤/搜索