本篇主要內容以下:html
6.1 引言程序員
6.2 建立函數sql
6.3 存儲過程數據庫
6.3.1建立過程express
6.3.2調用存儲過程安全
6.3.3 AUTHID模塊化
6.3.4 PRAGMA AUTONOMOUS_TRANSACTION函數
6.3.5開發存儲過程步驟工具
6.3.6刪除過程和函數性能
6.3.7過程與函數的比較
過程與函數(另外還有包與觸發器)是命名的PL/SQL塊(也是用戶的方案對象),被編譯後存儲在數據庫中,以備執行。所以,其它PL/SQL塊能夠按名稱來使用他們。因此,能夠將商業邏輯、企業規則寫成函數或過程保存到數據庫中,以便共享。
過程和函數統稱爲PL/SQL子程序,他們是被命名的PL/SQL塊,均存儲在數據庫中,並經過輸入、輸出參數或輸入/輸出參數與其調用者交換信息。過程和函數的惟一區別是函數總向調用者返回數據,而過程則不返回數據。在本節中,主要介紹:
1. 建立存儲過程和函數。
2. 正確使用系統級的異常處理和用戶定義的異常處理。
3. 創建和管理存儲過程和函數。
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關鍵字,不然容易刪除有用的函數。
例1. 獲取某部門的工資總和:
--獲取某部門的工資總和 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. 函數的調用
函數聲明時所定義的參數稱爲形式參數,應用程序調用時爲函數傳遞的參數稱爲實際參數。應用程序在調用函數時,可使用如下三種方法向函數傳遞參數:
第一種參數傳遞格式:位置表示法。
即在調用時按形參的排列順序,依次寫出實參的名稱,而將形參與實參關聯起來進行傳遞。用這種方法進行調用,形參與實參的名稱是相互獨立,沒有關係,強調次序纔是重要的。
格式爲:
argument_value1[,argument_value2 …]
例2:計算某部門的工資總和:
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;
第二種參數傳遞格式:名稱表示法。
即在調用時按形參的名稱與實參的名稱,寫出實參對應的形參,而將形參與實參關聯起來進行傳遞。這種方法,形參與實參的名稱是相互獨立的,沒有關係,名稱的對應關係纔是最重要的,次序並不重要。
格式爲:
argument => parameter [,…]
其中:argument 爲形式參數,它必須與函數定義時所聲明的形式參數名稱相同parameter 爲實際參數。
在這種格式中,形勢參數與實際參數成對出現,相互間關係惟一肯定,因此參數的順序能夠任意排列。
例3:計算某部門的工資總和:
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;
第三種參數傳遞格式:組合傳遞。
即在調用一個函數時,同時使用位置表示法和名稱表示法爲函數傳遞參數。採用這種參數傳遞方法時,使用位置表示法所傳遞的參數必須放在名稱表示法所傳遞的參數前面。也就是說,不管函數具備多少個參數,只要其中有一個參數使用名稱表示法,其後全部的參數都必須使用名稱表示法。
例4:
--注意VARCHAR2不能給精度,如:VARCHAR2(10),其它相似 Age INTEGER, CREATE OR REPLACE FUNCTION demo_fun(Name VARCHAR2, Sex VARCHAR2) 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, 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關鍵字爲輸入參數指定默認值。
例5:
CREATE OR REPLACE FUNCTION demo_fun(Name VARCHAR2,Age INTEGER, Sex VARCHAR2DEFAULT'男') 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;
創建存儲過程
在 ORACLE SERVER上創建存儲過程,能夠被多個應用程序調用,能夠向存儲過程傳遞參數,也能夠向存儲過程傳回參數.
建立過程語法:
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;
說明:相關參數說明參見函數的語法說明。
例6.用戶鏈接登記記錄;
CREATE TABLE logtable (userid VARCHAR2(10), logdate date); CREATE OR REPLACE PROCEDURE logexecution IS BEGIN INSERT INTO logtable (userid, logdate) VALUES (USER, SYSDATE); END;
例7.刪除指定員工記錄;
CREATE OR REPLACE PROCEDURE DelEmp (v_empno IN employees.employee_id%TYPE) AS No_result EXCEPTION; BEGIN DELETEFROM employees WHERE employee_id = v_empno; IF SQL%NOTFOUND THEN RAISE no_result; ENDIF; DBMS_OUTPUT.PUT_LINE('編碼爲'||v_empno||'的員工已被刪除!'); EXCEPTION WHEN no_result THEN DBMS_OUTPUT.PUT_LINE('舒適提示:你須要的數據不存在!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM); END DelEmp;
例8.插入員工記錄:
CREATE OR REPLACE PROCEDURE InsertEmp(v_empno in employees.employee_id%TYPE, v_firstname in employees.first_name%TYPE, v_lastname in employees.last_name%TYPE, v_deptno in employees.department_id%TYPE ) AS 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(v_empno, v_firstname,v_lastname, sysdate, v_deptno); DBMS_OUTPUT.PUT_LINE('舒適提示:插入數據記錄成功!'); EXCEPTION WHEN empno_remaining THEN DBMS_OUTPUT.PUT_LINE('舒適提示:違反數據完整性約束!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM); END InsertEmp;
例9.使用存儲過程向departments表中插入數據。
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; */
存儲過程創建完成後,只要經過受權,用戶就能夠在SQLPLUS 、ORACLE開發工具或第三方開發工具中來調用運行。對於參數的傳遞也有三種:按位置傳遞、按名稱傳遞和組合傳遞,傳遞方法與函數的同樣。ORACLE 使用EXECUTE 語句來實現對存儲過程的調用:
例10:
例11:查詢指定員工記錄;
CREATE OR REPLACE PROCEDURE QueryEmp (v_empno IN employees.employee_id%TYPE, v_ename OUT employees.first_name%TYPE, v_sal OUT employees.salary%TYPE) AS BEGIN SELECT last_name || last_name, salary INTO v_ename, v_sal FROM employees WHERE employee_id = v_empno; DBMS_OUTPUT.PUT_LINE('舒適提示:編碼爲'||v_empno||'的員工已經查到!'); EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('舒適提示:你須要的數據不存在!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM); END QueryEmp; --調用 DECLARE v1 employees.first_name%TYPE; v2 employees.salary%TYPE; BEGIN QueryEmp(100, v1, v2); DBMS_OUTPUT.PUT_LINE('姓名:'||v1); DBMS_OUTPUT.PUT_LINE('工資:'||v2); QueryEmp(103, v1, v2); DBMS_OUTPUT.PUT_LINE('姓名:'||v1); DBMS_OUTPUT.PUT_LINE('工資:'||v2); QueryEmp(104, v1, v2); DBMS_OUTPUT.PUT_LINE('姓名:'||v1); DBMS_OUTPUT.PUT_LINE('工資:'||v2); END;
例12.計算指定部門的工資總和,並統計其中的職工數量。
CREATE OR REPLACE PROCEDURE proc_demo ( dept_no NUMBERDEFAULT10, 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; DECLARE V_num NUMBER; V_sum NUMBER(8, 2); 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;
在PL/SQL 程序中還能夠在塊內創建本地函數和過程,這些函數和過程不存儲在數據庫中,但能夠在建立它們的PL/SQL 程序中被重複調用。本地函數和過程在PL/SQL 塊的聲明部分定義,它們的語法格式與存儲函數和過程相同,但不能使用CREATE OR REPLACE 關鍵字。
例13:創建本地過程,用於計算指定部門的工資總和,並統計其中的職工數量;
DECLARE V_num NUMBER; V_sum NUMBER(8, 2); PROCEDURE proc_demo ( Dept_no NUMBERDEFAULT10, Sal_sum OUT NUMBER, Emp_count OUT NUMBER ) IS BEGIN SELECTSUM(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;
過程當中的AUTHID 指令能夠告訴ORACLE ,這個過程使用誰的權限運行.默任狀況下,存儲過程會做爲調用者的過程運行,可是具備設計者的特權.這稱爲設計者權利運行.
例14:創建過程,使用AUTOID DEFINER;
Connect HR/qaz DROPTABLE logtable; CREATEtable logtable (userid VARCHAR2(10), logdate date); CREATEORREPLACEPROCEDURE logexecution AUTHID DEFINER ISBEGIN INSERTINTO logtable (userid, logdate) VALUES (USER, SYSDATE); END; GRANTEXECUTEON logexecution TOPUBLIC; CONNECT /AS SYSDBA GRANT CONNECT TO testuser1 IDENTIFIED BY userpwd1; CONNECT testuser1/userpwd1 INSERTINTO HR.LOGTABLE VALUES (USER, SYSDATE); EXECUTE HR.logexecution CONNECT HR/qaz SELECT*FROM HR.logtable;
例15:創建過程,使用AUTOID CURRENT_USER;
CONNECT HR/qaz CREATE OR REPLACE PROCEDURE logexecution AUTHID CURRENT_USER IS BEGIN INSERT INTO logtable (userid, logdate) VALUES (USER, SYSDATE); END; GRANTEXECUTEON logexecution TOPUBLIC; CONNECT testuser1/userpwd1 INSERTINTO HR.LOGTABLE VALUES (USER, SYSDATE); EXECUTE HR.logexecution
ORACLE8i 能夠支持事務處理中的事務處理的概念.這種子事務處理能夠完成它本身的工做,獨立於父事務處理進行提交或者回滾.經過使用這種方法,開發者就可以這樣的過程,不管父事務處理是提交仍是回滾,它均可以成功執行.
例16:創建過程,使用自動事務處理進行日誌記錄;
DROPTABLE logtable; CREATETABLE logtable( Username varchar2(20), Dassate_time date, Mege varchar2(60) ); CREATETABLE temp_table( N number ); CREATE OR REPLACE PROCEDURE log_message(p_message varchar2) AS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN INSERTINTO logtable VALUES ( user, sysdate, p_message ); COMMIT; END log_message; BEGIN Log_message (‘About toinsertinto temp_table‘); INSERTINTO temp_table VALUES (1); Log_message (‘Rollbacktoinsertinto temp_table‘); ROLLBACK; END; SELECT*FROM logtable; SELECT*FROM temp_table;
例17:創建過程,沒有使用自動事務處理進行日誌記錄;
開發存儲過程、函數、包及觸發器的步驟以下:
使用文字編輯處理軟件編輯存儲過程源碼,要用相似WORD 文字處理軟件進行編輯時,要將源碼存爲文本格式。
在SQLPLUS或用調試工具將存儲過程程序進行解釋;
在SQL>下調試,可用START 或GET 等ORACLE命令來啓動解釋。如:
SQL>START c:\stat1.sql
若是使用調式工具,可直接編輯和點擊相應的按鈕便可生成存儲過程。
咱們不能保證所寫的存儲過程達到一次就正確。因此這裏的調式是每一個程序員必須進行的工做之一。在SQLPLUS下來調式主要用的方法是:
l 使用 SHOW ERROR命令來提示源碼的錯誤位置;
l 使用 user_errors 數據字典來查看各存儲過程的錯誤位置。
若是調式正確的存儲過程沒有進行受權,那就只有創建者本人才能夠運行。因此做爲應用系統的一部分的存儲過程也必須進行受權才能達到要求。在SQL*PLUS下能夠用GRANT命令來進行存儲過程的運行受權。
GRANT語法:
GRANT system_privilege | role TOuser| role |PUBLIC[WITH ADMIN OPTION] GRANT object_privilege |ALLONschema.object TOuser| role |PUBLIC[WITH GRANT OPTION] --例子:CREATEORREPLACEPUBLIC SYNONYM dbms_job FOR dbms_job GRANTEXECUTEON dbms_job TOPUBLICWITHGRANTOPTION
USER_SOURCE, ALL_SOURCE, DBA_SOURCE, USER_ERRORS,
ALL_PROCEDURES,USER_OBJECTS,ALL_OBJECTS,DBA_OBJECTS
相關的權限:
CREATE ANY PROCEDURE
DROP ANY PROCEDURE
在SQL*PLUS 中,能夠用DESCRIBE 命令查看過程的名字及其參數表。
DESC[RIBE] Procedure_name;
1.刪除過程
可使用DROP PROCEDURE命令對不須要的過程進行刪除,語法以下:
DROP PROCEDURE [user.]Procudure_name;
2.刪除函數
可使用DROP FUNCTION 命令對不須要的函數進行刪除,語法以下:
使用過程與函數具備以下優勢:
1、共同使用的代碼能夠只須要被編寫和測試一次,而被須要該代碼的任何應用程序(如:.NET、C++、JAVA、VB程序,也能夠是DLL庫)調用。
2、這種集中編寫、集中維護更新、你們共享(或重用)的方法,簡化了應用程序的開發和維護,提升了效率與性能。
3、這種模塊化的方法,使得能夠將一個複雜的問題、大的程序逐步簡化成幾個簡單的、小的程序部分,進行分別編寫、調試。所以使程序的結構清晰、簡單,也容易實現。
4、能夠在各個開發者之間提供處理數據、控制流程、提示信息等方面的一致性。
5、節省內存空間。它們以一種壓縮的形式被存儲在外存中,當被調用時才被放入內存進行處理。而且,若是多個用戶要執行相同的過程或函數時,就只須要在內存中加載一個該過程或函數。
6、提升數據的安全性與完整性。經過把一些對數據的操做放到過程或函數中,就能夠經過是否授予用戶有執行該過程或的權限,來限制某些用戶對數據進行這些操做。
過程與函數的相同功能有:
一、 都使用IN模式的參數傳入數據、OUT模式的參數返回數據。
二、 輸入參數均可以接受默認值,均可以傳值或傳引導。
三、 調用時的實際參數均可以使用位置表示法、名稱表示法或組合方法。
四、 都有聲明部分、執行部分和異常處理部分。
五、 其管理過程都有建立、編譯、受權、刪除、顯示依賴關係等。
使用過程與函數的原則:
1、若是須要返回多個值和不返回值,就使用過程;若是隻須要返回一個值,就使用函數。
2、過程通常用於執行一個指定的動做,函數通常用於計算和返回一個值。
3、能夠SQL語句內部(如表達式)調用函數來完成複雜的計算問題,但不能調用過程。因此這是函數的特點。
參考文章:Oracle之函數和索引