存儲過程(學習筆記)


 

存儲過程指的是在大型數據庫系統中專門定義的一組SQL語句集,它能夠定義用戶操做參數,而且存在於數據庫中,當使用時直接調用便可
存儲過程=過程的聲明+PL/SQL塊
定義過程語法:
CREATE [OR REPLACE] PROCEDURE 過程名稱([參數名稱[參數模式] NOCOPY 數據類型 [參數名稱 [參數模式] NOCOPY 數據類型,...]])
[AUTHID [DEFINER | CURRENT_USER]]
AS || IS
[PRAGMA AUTONOMOUS_TRANSACTION;]
聲明部分;
BEGIN
程序部分;
EXCEPTION
導常處理;
END;
參數中定義參數模式表示過程的數據的接收操做,通常分爲IN,OUT,IN OUT 3類
CREATE [OR REPLACE]:表示建立或者替換過程,若是過程存在則替換,若是不存在就建立一個新的
AUTHID子句定義了一個過程的全部者權限,DEFINER(默認)表示定義者權限執行,或者用CURRENT_USER覆蓋程序的默認行爲,變爲使用者權限
PRAGMA AUTONOMOUS_TRANSACTION:表示過程啓動一個自治事務,自治事務可讓主事掛起,在過程當中執行完SQL後,由用戶處理提交或者回滾自治事務,
而後恢復主事務
EXECUTE 過程名 來調用過程
或者EXEC 過程名sql

在sqlplus中設置過程顯示
SET serveroutput ON數據庫

 

 

 

示例1、定義一個簡單的過程app

 

CREATE OR REPLACE PROCEDURE bdqn_proc
AS
BEGIN
  dbms_output.put_line('學習使用存儲過程!');
END;
執行
EXEC bdqn_proc;

 

示例2、定義一個簡的過程函數

CREATE OR REPLACE PROCEDURE FIND_EMP(PNO EMP.EMPNO%TYPE) AS
  V_ENAME EMP.ENAME%TYPE;
  V_JOB   EMP.JOB%TYPE;
  V_COUNT NUMBER;
BEGIN
  SELECT COUNT(EMPNO) INTO V_COUNT FROM EMP; --查詢表中的記錄總數
  IF V_COUNT = 0 THEN
    --判斷是否有記錄0表示沒有
    RETURN; --結束
  END IF;
  SELECT ENAME, JOB INTO V_ENAME, V_JOB FROM EMP WHERE EMPNO = PNO; --查尋姓名和職位,並將值傳給變量
  DBMS_OUTPUT.PUT_LINE('員工編號: ' || PNO || ' 員工姓名: ' || V_ENAME ||
                       ' 員工職位: ' || V_JOB);
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('沒有找到數據');
END FIND_EMP;
執行
EXECUTE FIND_EMP(&pnd)

示例3、使用過程增長部門性能

CREATE OR REPLACE PROCEDURE dpetadd_proc(

v_deptno               dept.deptno%TYPE,
v_dname                dept.dname%TYPE,
v_loc                  dept.loc%TYPE)

AS
 v_count                NUMBER;
BEGIN
  SELECT COUNT(deptno) INTO v_count FROM dept WHERE deptno=v_deptno;          --統計
  IF v_count>0 THEN
    raise_application_error(-20888,'增長失敗,這個部門已經存在');
    ELSE
      INSERT INTO dept(deptno,dname,loc)VALUES(v_deptno,v_dname,v_loc);
      dbms_output.put_line('新部門增長成功');
      COMMIT;
  END IF;
EXCEPTION 
  WHEN OTHERS THEN
    dbms_output.put_line('SQLERRM='||SQLERRM);
    ROLLBACK;
END;
執行
EXEC dpetadd_proc(10,'北大青鳥','北京')
EXEC dpetadd_proc(90,'北大青鳥','北京')
SELECT * FROM dept;


示例4、使用過程增長部門學習

CREATE OR REPLACE PROCEDURE dpetadd2_proc(

v_deptno               dept.deptno%TYPE:=&deptno,
v_dname                dept.dname%TYPE:='&dname',
v_loc                  dept.loc%TYPE:='&loc')

AS
 v_count                NUMBER;
BEGIN
  SELECT COUNT(deptno) INTO v_count FROM dept WHERE deptno=v_deptno;          --統計
  IF v_count>0 THEN
    raise_application_error(-20888,'增長失敗,這個部門已經存在');
    ELSE
      INSERT INTO dept(deptno,dname,loc)VALUES(v_deptno,v_dname,v_loc);
      dbms_output.put_line('新部門增長成功');
      COMMIT;
  END IF;
EXCEPTION 
  WHEN OTHERS THEN
    dbms_output.put_line('SQLERRM='||SQLERRM);
    ROLLBACK;
END;
執行
EXEC dpetadd2_proc(10,'北大青鳥','北京')
EXEC dpetadd2_proc(16,'北大青鳥','北京')

示例5、使用過程查詢1981年入職的員工的工資和公司平均工資比較小於輸出低工資,等於工資還行,高於輸出高工資測試

CREATE OR REPLACE PROCEDURE SEARCH_PRO AS
  V_EMPNO    EMP.EMPNO%TYPE;
  V_NAME     EMP.ENAME%TYPE;
  V_JOB      EMP.JOB%TYPE;
  V_HIREDATE EMP.HIREDATE%TYPE;
  V_GRADE    SALGRADE.GRADE%TYPE;
  V_AVG      NUMBER;
  V_DNAME    DEPT.DNAME%TYPE;
  V_SAL      EMP.SAL%TYPE;
  CUR_S      SYS_REFCURSOR;
BEGIN
  SELECT AVG(SAL) INTO V_AVG FROM EMP;
  OPEN CUR_S FOR
    SELECT E.EMPNO, E.ENAME, E.JOB, E.HIREDATE, E.SAL, S.GRADE, D.DNAME
      FROM EMP E, SALGRADE S, DEPT D
     WHERE E.SAL BETWEEN S.LOSAL AND S.HISAL
       AND E.DEPTNO = D.DEPTNO(+);
  LOOP
    FETCH CUR_S
      INTO V_EMPNO, V_NAME, V_JOB, V_HIREDATE, V_SAL, V_GRADE, V_DNAME;
    EXIT WHEN CUR_S%NOTFOUND;
    IF V_SAL < V_AVG THEN
      DBMS_OUTPUT.PUT_LINE(CUR_S%ROWCOUNT || '員工編號:' || V_EMPNO || ' 姓名:' ||
                           V_NAME || ' 職位:' || V_JOB || ' 入職日期:' ||
                           V_HIREDATE || ' 工資:||v_sal' || ' 工資等級' ||
                           V_GRADE || ' 部門名稱:' || V_DNAME);
      DBMS_OUTPUT.PUT_LINE('工資過低了');
    ELSIF V_SAL = V_AVG THEN
      DBMS_OUTPUT.PUT_LINE(CUR_S%ROWCOUNT || '員工編號:' || V_EMPNO || ' 姓名:' ||
                           V_NAME || ' 職位:' || V_JOB || ' 入職日期:' ||
                           V_HIREDATE || ' 工資:||v_sal' || ' 工資等級' ||
                           V_GRADE || ' 部門名稱:' || V_DNAME);
      DBMS_OUTPUT.PUT_LINE('工資還行');
    ELSE
       DBMS_OUTPUT.PUT_LINE(CUR_S%ROWCOUNT || '員工編號:' ||
                                              V_EMPNO || ' 姓名:' || V_NAME ||
                                              ' 職位:' || V_JOB || ' 入職日期:' ||
                                              V_HIREDATE || ' 工資:||v_sal' ||
                                              ' 工資等級' || V_GRADE ||
                                              ' 部門名稱:' || V_DNAME);
      DBMS_OUTPUT.PUT_LINE('工資高了');
    END IF;
  END LOOP;
  CLOSE CUR_S;
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
執行
EXEC SEARCH_PRO

參數模式 spa

IN模式3d

示例1、定義過程使用IN默承認以不寫code

CREATE OR REPLACE PROCEDURE in_proc(
     p_a IN VARCHAR2,                     --明肯定義IN參數模式
     p_b IN VARCHAR2                      --默認的參數模式爲in
) 
AS
BEGIN
  dbms_output.put_line('執行in_proc()過程: p_a='||p_a);
  dbms_output.put_line('執行in_proc()過程: p_b='||p_b);
END;

執行
DECLARE
    v_a VARCHAR2(50):='Java開發實戰經典';
    v_b VARCHAR2(50):='Oracle開發實戰經典';
BEGIN
    in_proc(v_a,v_b);
END;
結果:
執行in_proc()過程: p_a=Java開發實戰經典
執行in_proc()過程: p_b=Oracle開發實戰經典

示例2、定義過程使用default定義參數默認值

CREATE OR REPLACE PROCEDURE in_proc(
     p_a IN VARCHAR2 DEFAULT '好好學習JAVA',                   --明肯定義IN參數模式
     p_b IN VARCHAR2 DEFAULT '努力看Oracle'                   --默認的參數模式爲in
) 
AS
BEGIN
  dbms_output.put_line('執行in_proc()過程: p_a='||p_a);
  dbms_output.put_line('執行in_proc()過程: p_b='||p_b);
END;

DECLARE
    v_a VARCHAR2(50):='Java開發實戰經典';
   
BEGIN
    in_proc(v_a);
END;
結果:
執行in_proc()過程: p_a=Java開發實戰經典
執行in_proc()過程: p_b=努力看Oracle
使用了第二個參數沒有寫,使用了默認值,若是有傳遞參數則使用傳遞的參數

OUT模式

示例1、定義過程使用OUT

CREATE OR REPLACE PROCEDURE out_proc(
     p_a OUT VARCHAR2,                     --明肯定義out參數模式
     p_b OUT VARCHAR2)                       --明肯定義out參數模式

AS
BEGIN
  dbms_output.put_line('執行out_proc()過程: p_a='||p_a);
  dbms_output.put_line('執行out_proc()過程: p_b='||p_b);
   p_a :='Java開發實戰經典';               --將此值返回給實參
   p_b :='Oracle開發實戰經典';
END;

執行
DECLARE
    v_a VARCHAR2(50):='好好學習';
    v_b VARCHAR2(50):='每天向上';
BEGIN
    out_proc(v_a,v_b);
    dbms_output.put_line('調用out_proc()過程: v_a='||v_a);
  dbms_output.put_line('調用out_proc()過程: v_b='||v_b);
    
END;

結果:
執行out_proc()過程: p_a=
執行out_proc()過程: p_b=
調用out_proc()過程: v_a=Java開發實戰經典
調用out_proc()過程: v_b=Oracle開發實戰經典

OUT模式時,傳入的參數數是無用的,傳入的內容不會傳遞 到過程當中去

inout模式

示例1、定義過程使用INOUT

CREATE OR REPLACE PROCEDURE inout_proc(
     p_a IN OUT VARCHAR2,                     --明肯定義out參數模式
     p_b IN OUT VARCHAR2)                       --明肯定義out參數模式

AS
BEGIN
  dbms_output.put_line('執行inout_proc()過程: p_a='||p_a);
  dbms_output.put_line('執行inout_proc()過程: p_b='||p_b);
   p_a :='Java開發實戰經典';               --將此值返回給實參
   p_b :='Oracle開發實戰經典';
END;

執行
DECLARE
    v_a VARCHAR2(50):='好好學習';
    v_b VARCHAR2(50):='每天向上';
BEGIN
    inout_proc(v_a,v_b);
    dbms_output.put_line('調用inout_proc()過程: v_a='||v_a);
  dbms_output.put_line('調用inout_proc()過程: v_b='||v_b);
    
END;
結果:
執行inout_proc()過程: p_a=好好學習
執行inout_proc()過程: p_b=每天向上
調用inout_proc()過程: v_a=Java開發實戰經典
調用inout_proc()過程: v_b=Oracle開發實戰經典

調用inout_proc過程時,將2個變量v_a,v_b傳入到了過程當中,因爲是INOUT模式,所過程能夠接收到傳遞的變量內容,同時過程對變量作了修改也能夠運回給實參

示例2、利用過程增長部門

CREATE OR REPLACE PROCEDURE DEPTINSER_PROC(
p_DNO    DEPT.DEPTNO%TYPE,
                                           p_DNAME  DEPT.DNAME%TYPE,
                                           p_LOC    DEPT.LOC%TYPE,
                                           P_RESULT OUT NUMBER --此爲標記變量
                                           ) AS
  V_COUNT NUMBER; --保存count函數的結果
BEGIN
  SELECT COUNT(DEPTNO) INTO V_COUNT FROM DEPT WHERE DEPTNO = p_DNO;
  IF V_COUNT > 0 THEN
    P_RESULT := -1;
  ELSE
    INSERT INTO DEPT (DEPTNO, DNAME, LOC) VALUES (p_DNO, p_DNAME, p_LOC);
    P_RESULT := 0;
    COMMIT;
  END IF;
END;
調用
DECLARE
  V_RESULT NUMBER; --定義變量接收結果
BEGIN
  DEPTINSER_PROC(66, 'test', 'China', V_RESULT); --調用過程
  IF V_RESULT = 0 THEN
    DBMS_OUTPUT.PUT_LINE('新部門增長成功');
  ELSE
    DBMS_OUTPUT.PUT_LINE('新部門增長失敗');
  END IF;

END;

 示例3、利用OUT傳遞遊標使用過程是查詢員工ID,姓名,職位,工資

CREATE OR REPLACE PROCEDURE search_pro(
       p_emp OUT SYS_REFCURSOR
)
AS
BEGIN
     OPEN p_emp FOR SELECT e.empno,e.ename,e.job,e.sal
                     FROM emp e;
END;

調用
DECLARE
  V_ID    EMP.EMPNO%TYPE;
  V_NAME  EMP.ENAME%TYPE;
  V_JOB   EMP.JOB%TYPE;
  V_SAL   EMP.SAL%TYPE;
  CUR_EMP SYS_REFCURSOR; --定義弱類型遊標
BEGIN
  SEARCH_PRO(CUR_EMP);
  LOOP
    FETCH CUR_EMP
      INTO V_ID, V_NAME, V_JOB, V_SAL;
    EXIT WHEN CUR_EMP%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE(CUR_EMP%ROWCOUNT || ' 員工編號:' || V_ID || ' 姓名:' ||
                         V_NAME || ' 職位:' || V_JOB || ' 工資:' || V_SAL);
  END LOOP;
  CLOSE CUR_EMP;
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;

示例四 、定義過程,根據員工編號,查詢員工姓名和工資,並輸出參數輸出 

CREATE OR REPLACE PROCEDURE pro_emp_query(
                  v_eno     IN      emp.empno%TYPE,
                  v_name    OUT     emp.ename%TYPE,
                  v_sal     OUT     emp.sal%TYPE
)AS
BEGIN
  SELECT ename,sal INTO v_name,v_sal FROM emp WHERE empno=v_eno;
  EXCEPTION
    WHEN OTHERS THEN
      dbms_output.put_line('沒有這個員工!');
END;
DECLARE
    v_eno       emp.empno%TYPE:=&empno;
    v_ename     emp.ename%TYPE;
    v_sal       emp.sal%TYPE;
BEGIN
  pro_emp_query(v_eno,v_ename,v_sal);
  dbms_output.put_line('編號:'||v_eno||' 姓名:'||v_ename||' 工資:'||v_sal);
  
END;

 

 

示例五 、交換值

CREATE OR REPLACE PROCEDURE swap(
       p1   IN OUT NUMBER,
       p2   IN OUT NUMBER
)AS
v_temp NUMBER;
BEGIN
      v_temp:=p1;
      p1:=p2;
      p2:=v_temp;
END;
--調用
DECLARE
    num1   NUMBER:=10;
    num2   NUMBER:=20;
BEGIN
   dbms_output.put_line(num1||'    '||num2);
    swap(num1,num2);
    dbms_output.put_line(num1||'    '||num2);
END;

 

自治事務

 使用下列語句聲明
PRAGMA AUTONOMOUS_TRANSACTION;

示例1、

CREATE OR REPLACE PROCEDURE dept_insert_proc
AS
       PRAGMA AUTONOMOUS_TRANSACTION;            --自治事務
BEGIN
      INSERT INTO dept(deptno,dname,loc)VALUES(80,'JAVA','北京');
      COMMIT;                                           --提交自治事務
END;
調用
DECLARE
BEGIN
   INSERT INTO dept(deptno,dname,loc)VALUES(60,'Oracl','深圳');
   dept_insert_proc();
   ROLLBACK;                                          --主事務回滾
END;

SELECT * FROM dept;

首先會向部門表中添加一條60部門的信息,此時調用過程,主程序會被掛起,到過程執行完

結果看出80部門已經添加成功,並無受到ROLLBACK的影響

NOCOPY選項

PL/SQL中對於IN模式的傳遞的參數採用孝是引用的傳遞方式,因此其性能較高。

而對於OUT或者IN OUT模式傳遞參數採用的是數值傳遞,在傳遞時須要將實參數據複製一份給形參,但當傳遞的數據較大時(如集合,記錄),那麼這一複製過程就會變長

也會消耗大量的內在空間,

在定義參數時可使用NOCOPY選項,將OUT或者IN OUT的值傳遞變爲引用傳遞

語法:

參數名稱 [參數模式out | in out] NOCOPY數據類型

 

--定義過程
create or replace procedure change_proc(
p_a in out number,p_nocopy in out nocopy NUMBER
)
 AS
begin
  p_a:=100;
  p_nocopy:=100;
  raise_application_error(-20008,'測試NOCOPY');
end change_proc;

調 用

 

--調用過程
DECLARE
 v_a NUMBER:=10;
 v_b NUMBER:=20;
BEGIN
  dbms_output.put_line('調用過程以前 v_a='||v_a||' v_b='||v_b);
  change_proc(v_a,v_b);
  
  EXCEPTION
    WHEN OTHERS THEN
      dbms_output.put_line(SQLERRM);
      dbms_output.put_line('調用過程以後v_a='||v_a||' v_b='||v_b);
END;

結果:

調用過程以前 v_a=10 v_b=20 ORA-20008: 測試NOCOPY 調用過程以後v_a=10 v_b=100

由於過程拋出了導常因此使用NOCOPy定義的參數正確將結果返回了,而沒有使用NOCOPY定義的參數不能正常返回,

子程序權限:

 

過程受權
GRANT EXECUTE ON 過程名 TO 用戶名 --將執行權授給用戶,但用戶不能再受權給其它用戶
GRANT EXECUTE ON 過程名 TO 用戶名 WITH GRANT OPTION; --將執行權授給用戶,但用戶能夠再受權給其它用戶

 

 

 

相關文章
相關標籤/搜索