Oracle進階(一)存儲過程


1、名詞釋義

存儲過程(Stored Procedure)是一組為了完成特定功能的SQL語句集,是由流程控制和SQL語句書寫的命名語句塊。
Oracle存儲過程包含三部分:過程聲明,執行過程部分,存儲過程異常。

2、基本語法

 1 --創建語法
 2 CREATE [OR REPLACE] PROCEDURE 存儲過程名(PARAM1 IN TYPE,PARAM2 OUT TYPE)
 3 AS   --as和is任選一個,在這沒有區別
 4 變量1 類型(值范圍);
 5 變量2 類型(值范圍);
 6 BEGIN
 7     SELECT COUNT(*) INTO 變量1 FROM 表A WHERE列名=PARAM1;
 8  
 9     IF (判斷條件) THEN
10        SELECT 列名 INTO 變量2 FROM 表A WHERE列名=PARAM1;
11        DBMS_OUTPUT.PUT_LINE(‘打印信息’);
12     ELSIF (判斷條件) THEN
13        DBMS_OUTPUT.PUT_LINE(‘打印信息’);
14     ELSE
15        RAISE 異常名(NO_DATA_FOUND);
16     END IF;
17 EXCEPTION
18     WHEN OTHERS THEN
19        ROLLBACK;
20 END;
21  
22 --調用語法一
23 BEGIN
24   存儲過程名();
25 END;
26  
27 --調用語法二
28 CALL 存儲過程名();
29  
30 --刪除語法
31 DROP PROCEDURE 存儲過程名;

3、異常釋義

 ACCESS_INTO_NULL 未定義對象

CASE_NOT_FOUND CASE 中若未包含相應的 WHEN ,並且沒有設置ELSE 時
COLLECTION_IS_NULL 集合元素未初始化
CURSER_ALREADY_OPEN 游標已經打開
DUP_VAL_ON_INDEX 唯一索引對應的列上有重復的值
INVALID_CURSOR 在不合法的游標上進行操作
INVALID_NUMBER 內嵌的 SQL 語句不能將字符轉換為數字
NO_DATA_FOUND 使用 select into 未返回行,或應用索引表未初始化的
TOO_MANY_ROWS 執行 select into 時,結果集超過一行
ZERO_DIVIDE  除數為 0
SUBSCRIPT_BEYOND_COUNT 元素下標超過嵌套表或 VARRAY 的最大值
SUBSCRIPT_OUTSIDE_LIMIT 使用嵌套表或 VARRAY 時,將下標指定為負數
VALUE_ERROR 賦值時,變量長度不足以容納實際數據
LOGIN_DENIED PL/SQL 應用程序連接到 oracle 數據庫時,提供了不正確的用戶名或密碼
NOT_LOGGED_ON PL/SQL 應用程序在沒有連接 oralce 數據庫的情況下訪問數據
PROGRAM_ERROR PL/SQL 內部問題,可能需要重裝數據字典& pl./SQL系統包
ROWTYPE_MISMATCH 宿主游標變量與 PL/SQL 游標變量的返回類型不兼容
SELF_IS_NULL 使用對象類型時,在 null 對象上調用對象方法
STORAGE_ERROR 運行 PL/SQL 時,超出內存空間
SYS_INVALID_ID 無效的 ROWID 字符串
TIMEOUT_ON_RESOURCE Oracle 在等待資源時超時

4、存儲過程

4.1、無參存儲過程

 1 --1)無參存儲過程語法
 2  
 3 CREATE OR REPLACE PROCEDURE PRO_1_NOPAR
 4  AS --聲明
 5  
 6  BEGIN --執行
 7      --SELECT * FROM D_DEPT D;
 8 DBMS_OUTPUT.PUT_LINE('無參存儲過程');
 9  EXCEPTION--異常
10     WHEN OTHERS THEN
11        ROLLBACK;
12  END;
13  
14 --調用
15 BEGIN
16   PRO_1_NOPAR;
17 END;

4.2、帶參數存儲過程

IN表示輸入參數,是參數的默認模式。
OUT表示返回值參數,類型可以使用任意Oracle中的合法類型。
OUT模式定義的參數只能在過程體內部賦值,表示該參數可以將某個值傳遞回調。
IN OUT表示該參數可以向該過程中傳遞值,也可以將某個值傳出去。

 1 --2)帶參數存儲過程含賦值方式
 2 CREATE OR REPLACE PROCEDURE PRO_2_PAR   
 3     (VAL IN NUMBER,   
 4      SNAME OUT VARCHAR,  
 5      DT_CODE IN OUT VARCHAR)  
 6  AS   
 7     ICOUNT NUMBER;  
 8  BEGIN  
 9       SELECT COUNT(*) INTO ICOUNT FROM D_DEPT WHERE DT_ID>VAL;  
10       IF ICOUNT=1 THEN  
11         SNAME:= 'MLB事業部1' ||VAL;
12                 DT_CODE:= 'MLB1'||DT_CODE;
13       ELSE  
14       SNAME:= 'MLB事業部2'||VAL;
15                 DT_CODE:= 'MLB2'||DT_CODE;
16      END IF;  
17 EXCEPTION  
18      WHEN TOO_MANY_ROWS THEN  
19      DBMS_OUTPUT.PUT_LINE('返回值多於1行');  
20      WHEN OTHERS THEN  
21      DBMS_OUTPUT.PUT_LINE('在PRO_1_PAR過程中出錯!');  
22 END;  
23  
24 --調用
25 declare  
26      REVAL NUMBER;  
27      RENAME varchar(40);  
28      RECODE varchar(40);  
29 begin    --過程調用開始  
30      REVAL:=20210421;  
31      RENAME:='';  
32      RECODE:='JUNIOR';  
33      --指定值對應變量順序可變  
34      PRO_2_PAR(SNAME=>RENAME,VAL=>REVAL,DT_CODE=>RECODE);           
35     DBMS_OUTPUT.PUT_LINE(RENAME||'   '||RECODE);  
36 END;  --過程調用結束

4.3、帶if的存儲過程

 1 --3)帶if的存儲過程
 2 CREATE OR REPLACE PROCEDURE PRO_3_IF(A IN NUMBER, B IN NUMBER,RS OUT NUMBER)
 3 AS
 4     TEMP NUMBER;
 5 BEGIN
 6     TEMP:=A;
 7     IF A < B THEN
 8         TEMP := B;
 9     END IF;
10     RS:=TEMP;
11 END;
12  
13 --調用
14 DECLARE
15     RS NUMBER;
16 BEGIN
17     PRO_3_IF(10,20,RS);
18     DBMS_OUTPUT.PUT_LINE('RS='||RS);
19 END;

4.4、帶if else的存儲過程

 1 CREATE OR REPLACE PROCEDURE PRO_4_IFELSE(A IN NUMBER, B IN NUMBER,RS OUT NUMBER)
 2 AS
 3 BEGIN
 4     IF A > B THEN
 5         RS := A;
 6     ELSE
 7         RS := B;
 8     END IF;
 9 END;
10  
11 --調用
12 DECLARE
13     RS NUMBER;
14 BEGIN
15     PRO_4_IFELSE(30,20,RS);
16     DBMS_OUTPUT.PUT_LINE('RS='||RS);
17 END; 

4.5、帶elsif的存儲過程

 1 CREATE OR REPLACE PROCEDURE PRO_5_ELSEIF(Y IN NUMBER)
 2 AS
 3 BEGIN
 4     IF Y=2020 THEN
 5         DBMS_OUTPUT.PUT_LINE('2020年');
 6     ELSIF Y =2021 THEN
 7         DBMS_OUTPUT.PUT_LINE('2021年');
 8     ELSE
 9         DBMS_OUTPUT.PUT_LINE('未知年份');
10     END IF;
11 END;
12  
13 --存儲過程調用
14 BEGIN
15   PRO_5_ELSEIF(Y => 2021);
16 END;

 

4.6、帶while循環的存儲過程

 1 --6)帶while循環的存儲過程
 2 CREATE OR REPLACE PROCEDURE PRO_6_WHILE(I IN NUMBER)
 3 AS
 4     J NUMBER;
 5 BEGIN
 6     J := 1;
 7     WHILE J <= I LOOP
 8         DBMS_OUTPUT.PUT_LINE('J='||J);
 9         J := J + 1;
10     END LOOP;
11 END;
12  
13 --存儲過程調用
14 BEGIN
15   PRO_6_WHILE(I=> 100);
16 END;

4.7、帶select into的存儲過程

在利用SELECT…INTO…語法時,必須先確保數據庫中有該條記錄,否則會報出"NO_DATA_FOUND"異常。
可先利用SELECT COUNT(*) FROM 查看數據庫中是否存在該記錄,存在則使用SELECT…INTO。
在存儲過程中,別名不能和字段名稱相同,否則雖然編譯可以通過,但在運行階段會報錯。

 1 --7)帶SELECT INTO的存儲過程
 2 CREATE OR REPLACE PROCEDURE PRO_7_SELINTO(ID IN NUMBER)
 3 AS
 4 M VARCHAR(50);
 5 G VARCHAR(50);
 6 BEGIN
 7   SELECT MONTH,ORG_OID INTO M,G FROM D_DEPT where DT_ID=ID;
 8    DBMS_OUTPUT.PUT_LINE('M'||M||'G'||G);
 9   EXCEPTION
10   WHEN NO_DATA_FOUND THEN
11       DBMS_OUTPUT.PUT_LINE('NO_DATA_FOUND異常');
12 END;
13  
14  
15 --存儲過程調用
16 BEGIN
17   PRO_7_SELINTO(ID=> 21);
18 END;

4.8、帶for的存儲過程

 1 --8)帶for循環的存儲過程
 2  
 3 CREATE OR REPLACE PROCEDURE PRO_8_FOR
 4 AS
 5 BEGIN
 6  FOR D IN (SELECT * FROM D_LESSON) LOOP
 7      IF (D.LN_ID>10) THEN
 8         DBMS_OUTPUT.PUT_LINE(D.LN_ID);
 9       END IF;
10   END LOOP;
11 COMMIT;
12 END;
13  
14 --調用方式一
15 BEGIN
16   PRO_8_FOR();
17 END;
18  
19 --調用方式二
20 CALL PRO_8_FOR();
21  
22 --刪除儲存過程
23 DROP PROCEDURE PRO_8_FOR;

4.9、帶immediate的存儲過程

 1 --1、給動態SQL傳值(USING 子句)
 2  
 3 CREATE OR REPLACE PROCEDURE PRO_LOOP_BYDATE(V_STARTDATE IN DATE,
 4                                               V_ENDDATE   IN DATE) IS
 5   V_DATE    DATE;
 6   V_ERR_MSG VARCHAR2(2000) := '-1';
 7  
 8 BEGIN
 9   V_DATE := V_STARTDATE;
10   WHILE V_DATE < V_ENDDATE LOOP
11     EXECUTE IMMEDIATE 'BEGIN PRO_DW_PRO_D_LIST_V(:V_DATE,:V_ERR_MSG); END;'
12           --傳入開始日期  返回錯誤信息
13       USING IN V_DATE, OUT V_ERR_MSG; --黓認為IN類型,其它類型必須顯式指定
14     V_DATE := V_DATE + 1;
15   END LOOP;
16  
17 END PRO_LOOP_BYDATE;
18  
19 --調用
20 DECLARE
21 STARTDATE DATE;
22 ENDDATE DATE; 
23 BEGIN
24    STARTDATE:=TO_DATE(20210423,'YYYYMMDD');  
25    ENDDATE:=TO_DATE(20210423,'YYYYMMDD');  
26   PRO_LOOP_BYDATE(V_STARTDATE => STARTDATE,
27                       V_ENDDATE => ENDDATE);
28 END;
29  
30  
31 --2、傳遞並檢索值.INTO子句用在USING子句前
32  
33 CREATE OR REPLACE PROCEDURE PRO_INTO_USING
34   IS 
35    LN_ID    PLS_INTEGER := 41;   
36    LN_NAME      VARCHAR2(256);   
37    LN_DESC      VARCHAR2(256);   
38 BEGIN   
39    EXECUTE IMMEDIATE 'SELECT LN_NAME, LN_DESC FROM D_LESSON WHERE LN_ID = :1'   
40      INTO LN_NAME, LN_DESC  --返回動態SQL中的LN_NAME, LN_DESC值
41      USING LN_ID ;  -- 把參數LN_ID 傳入到動態SQL
42          
43           DBMS_OUTPUT.PUT_LINE('ID:'||LN_ID||'LN_NAME:'||LN_NAME||'LN_DESC'||LN_DESC);
44 END; 
45  
46 --調用
47 CALL PRO_INTO_USING();

4.10 帶游標的存儲過程

4.10.1 游標語法與屬性

 1 --游標創建語法
 2 DECLARE 
 3 ---聲明CURSOR,創建和命名一個SQL工作區
 4 CURSOR CURSOR_NAME IS 
 5     SELECT ENAME FROM EMP;
 6     V_REALNAME VARCHAR2(20);
 7 BEGIN
 8     OPEN CURSOR_NAME;---打開CURSOR,執行SQL語句產生的結果集
 9     FETCH CURSOR_NAME INTO V_REALNAME;--提取CURSOR,提取結果集中的記錄
10     DBMS_OUTPUT.PUT_LINE(V_REALNAME);
11     CLOSE CURSOR_NAME;--關閉CURSOR
12 END;
13  
14 --游標的屬性:
15    %ISOPEN        是否打開        BOOLEAN類型
16    %ROWCOUNT    影響的行數    不是總行數,例如總數100,已經取了10條,那么這個數為10
17    %FOUND         是否找到        BOOLEAN類型
18    %NOTFOUND    是否沒找到     BOOLEAN類型

4.10.2 無參游標存儲過程

 1 --使用無參CURSOR,查詢所有員工的姓名和工資
 2 CREATE OR REPLACE PROCEDURE PROC_10_CURSOR_NOPAR
 3 AS
 4 BEGIN
 5 DECLARE
 6     --定義游標
 7     CURSOR CEMP IS SELECT ENAME,SAL FROM EMP;
 8     --定義變量
 9     VENAME EMP.ENAME%TYPE;
10     VSAL   EMP.SAL%TYPE;
11 BEGIN
12     --打開游標,這時游標位於第一條記錄之前
13     OPEN CEMP;
14     --循環
15     LOOP
16        --向下移動游標一次
17        FETCH CEMP INTO VENAME,VSAL;
18        --退出循環,當游標下移一次后,找不到記錄時,則退出循環
19        EXIT WHEN CEMP%NOTFOUND;
20        --輸出結果
21        DBMS_OUTPUT.PUT_LINE(VENAME||''||VSAL);
22     END LOOP;
23     --關閉游標
24     CLOSE CEMP;
25 END;
26 END;
27  
28 --調用
29 BEGIN
30   PROC_10_CURSOR_NOPAR;
31 END;

4.10.3 帶參游標存儲過程

 1 --使用帶參CURSOR,查詢10號部門的員工姓名和工資
 2 CREATE OR REPLACE PROCEDURE PROC_10_CURSOR_PAR(DEPTNO NUMBER)
 3 AS
 4 BEGIN
 5 DECLARE
 6     CURSOR CEMP(PDEPTNO EMP.DEPTNO%TYPE) IS SELECT ENAME,SAL FROM EMP WHERE DEPTNO=PDEPTNO;
 7     PENAME EMP.ENAME%TYPE;
 8     PSAL EMP.SAL%TYPE;
 9 BEGIN
10     OPEN CEMP(DEPTNO);
11     LOOP
12         FETCH CEMP INTO PENAME,PSAL;    
13         EXIT WHEN CEMP%NOTFOUND;
14         DBMS_OUTPUT.PUT_LINE(PENAME||'的工資是'||PSAL);
15     END LOOP;
16     CLOSE CEMP;
17 END;
18 END;
19  
20 --調用
21  
22 DECLARE        
23 DEPTNO NUMBER(10):=&EMPNO;   
24 BEGIN     
25 PROC_10_CURSOR_PAR(DEPTNO);   
26 END; 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM