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;