什么是存儲過程
存儲過程是一組為了完成特定功能的SQL語句,經編譯后存在數據庫,存儲過程是數據庫中的一個重要對象。
ps:有人說:任何一個設計良好的數據庫應用程序都應該用到存儲過程,我覺得這個不一定,其實很多互聯網公司的WEB應用程序都沒有用到存儲過程,難道就能說他們的應用程序設計的不好嗎?現實中,銀行、金融相關的公司使用存儲過程多一些。
存儲過程的優缺點
優點
- 效率高:創建的時候就會編譯,調用時不需要重新編譯,而直接寫SQL每次執行都先編譯再執行。
- 安全:與直接寫SQL語句相比,存儲過程不存在SQL注入的問題。可以向用戶授予存儲過程的訪問權限,而不是直接授予表的訪問權限,控制對特定數據的訪問。
- 降低網絡流量:存儲過程直接存儲在數據庫中,所以不會產生大量的SQL代碼流量。
缺點
- 可移植性很差:應用程序更換其他數據庫,需要將原有的存儲過程重新寫一遍,并且如果你的存儲過程中包含大量的業務邏輯,這樣就會更加麻煩。
- 代碼可復用性很差:面向對象的思維在這里完全沒有用,兩個很相近的功能,需要寫兩個存儲過程,雖然可以相互調用,但是不能利用繼承等面向對象的功能。
語法
create or replace procedure <存儲過程名>
[(
參數列表:p_name1 in <輸入參數數據類型>,p_name2 out <輸出參數數據類型>
,p_name3 in out <參數數據類型>
)]
as或is
[定義變量:v_name1 <變量數據類型>]
begin
? <執行特定的操作或者任務,可以進行增刪改查>
? end;
注意
如果沒有參數()要去掉,存儲過程主體不能為空(begin和end之間不能什么都不做)
說明
- in表示輸入參數,out表示輸出參數,可以同時使用in和out表示既是輸入參數又是輸出參數。
- 聲明變量時可以使用 列名+%type 來聲明類型
- 存儲過程主體做select查詢時,必須使用into將結果集保存到變量中
- :=表示賦值操作
DEMO
登錄Oracle數據庫scott用戶,初始密碼tigger
1.定義一個存儲過程,用來查詢指定部門的員工的人數和平均工資,如果部門不存在,就引發一個自定義異常,顯示“部門編號不存在!”(部門編號作為存儲過程的輸入參數,在調用存儲過程前由用戶輸入,該部門員工的人數和平均工資需要返回)
創建
create or replace procedure proc1(
p_deptno in emp.deptno%type,
p_count_emp out number ,
p_avg_sal out emp.sal%type
)
is
v_row_num number:=0;
error1 exception;
beginselect count(*) into p_count_emp from emp e where e.deptno=p_deptno;if p_count_emp!=0 thenselect avg(e.sal) into p_avg_sal from emp e where e.deptno=p_deptno;else raise error1;end if;exception when error1 thendbms_output.put_line('部門編號不存在');
end;
語句塊調用
declare
a emp.deptno%type;
b number:=0;
c emp.sal%type:=0;
begina:=&部門編號;proc1(a,b,c);dbms_output.put_line('部門編號為'||a||'的員工人數為' ||b||',平均工資為:'||c);end;
2.存儲過程相互調用
創建
--根據員工編號刪除員工
create or replace procedure proc3
(p_empno in emp.empno%type
)
as
begindelete from emp e where e.empno=p_empno;commit;end;--首先調用上面的那個存儲過程,然后用輸出參數返回剩下的員工人數
create or replace procedure proc4(
p_empno in emp.empno%type,
p_nums_afterdelete out number)
as
beginproc3(p_empno);select count(*) into p_nums_afterdelete from emp;end;
語句塊調用
declare
p_empno emp.empno%type:=&請輸入員工編號;
p_nums_afterdelete number:=0;
beginproc4(p_empno,p_nums_afterdelete);dbms_output.put_line('已成功刪除員工編號為'||p_empno||'的員工'||',當前還剩的員工總數為'||p_nums_afterdelete);end;
3.使用call關鍵字直接調用存儲過程
create or replace procedure proc2(a in number,b in number)
as
c number;
beginc :=a+b;dbms_output.put_line(c);end;call proc2(1,1);
JAVA調用存儲過程
首先需要下載classes12.jar、ojdbc14.jar,并導入到自己的工程里。
插入數據
package java_procedure;import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;//只需ojdbcjar包就行
/*--java調用存儲過程
create table tb_student(
stu_id varchar2(20),
stu_name varchar2(20)
);
--①沒有返回值的存儲過程
create or replace procedure TESTA(
p_stu_id in tb_student.stu_id%type,
p_stu_name in tb_student.stu_name%type)
as
begininsert into tb_student(stu_id, stu_name)values(p_stu_id, p_stu_name);
end TESTA;
/*/
public class TestProcedureOne {public static void main(String[] args) {CallableStatement cs=null;Connection conn=null;String driver="oracle.jdbc.driver.OracleDriver";String url="jdbc:oracle:thin:@localhost:1521:orcl";String userName="a3";String pwd="a123";try {Class.forName(driver);conn=DriverManager.getConnection(url,userName,pwd);cs=conn.prepareCall("{call testa(?,?)}");cs.setString(1,"001");cs.setString(2, "zhangsan");cs.execute();} catch (ClassNotFoundException e) {e.printStackTrace();}catch (SQLException e) {e.printStackTrace();}finally{try {if (cs!=null) {cs.close();}if (conn!=null) {conn.close();}} catch (SQLException e) {e.printStackTrace();}}}}
條件查詢
package java_procedure;import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;//只需ojdbcjar包就行
/*
--②有返回值的存儲過程
CREATE OR REPLACE PROCEDURE TESTB(
p_stu_id in tb_student.stu_id%type,
p_stu_name out tb_student.stu_name%type) AS
BEGINSELECT stu_name INTO p_stu_name FROM tb_student WHERE stu_id = p_stu_id;
END TESTB;
/*/
public class TestProcedureTwo {public static void main(String[] args) {CallableStatement cs=null;Connection conn=null;String driver="oracle.jdbc.driver.OracleDriver";String url="jdbc:oracle:thin:@localhost:1521:orcl";String userName="a3";String pwd="a123";try {Class.forName(driver);conn=DriverManager.getConnection(url,userName,pwd);cs=conn.prepareCall("{call testb(?,?)}");cs.setString(1,"001");cs.registerOutParameter(2, oracle.jdbc.OracleTypes.VARCHAR);//registerOutParameter()方法的第二個參數:如果是MySQL的驅動,使用Types.VARCHARcs.execute();String stuName=cs.getString(2);System.out.println(stuName);} catch (ClassNotFoundException e) {e.printStackTrace();}catch (SQLException e) {e.printStackTrace();}finally{try {if (cs!=null) {cs.close();}if (conn!=null) {conn.close();}} catch (SQLException e) {e.printStackTrace();}}}}
查詢所有
package java_procedure;import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;//要用classes12.jar,ojdbcjar包有bug
/*
--返回一個列表的存儲過程
--建一個程序包
CREATE OR REPLACE PACKAGE TESTPACKAGE ASTYPE Test_CURSOR IS REF CURSOR;
end TESTPACKAGE;
--建立存儲過程
CREATE OR REPLACE PROCEDURE TESTC(
p_CURSOR out TESTPACKAGE.Test_CURSOR) IS
BEGINOPEN p_CURSOR FOR SELECT * FROM tb_student;
END TESTC;*/
public class TestProcedureThree {public static void main(String[] args) {CallableStatement cs=null;Connection conn=null;ResultSet rs=null;String driver="oracle.jdbc.driver.OracleDriver";String url="jdbc:oracle:thin:@localhost:1521:orcl";String userName="a3";String pwd="a123";try {Class.forName(driver);conn=DriverManager.getConnection(url,userName,pwd);cs=conn.prepareCall("{call testc(?)}");cs.registerOutParameter(1,oracle.jdbc.OracleTypes.CURSOR);cs.execute();rs=(ResultSet)cs.getObject(1);//上面一行代碼可以這樣寫: rs=((OracleCallableStatement)cs).getCursor(1);while(rs.next()){System.out.println("ID:"+rs.getString(1)+"\t"+"姓名:"+rs.getString(2));}} catch (ClassNotFoundException e) {e.printStackTrace();}catch (SQLException e) {e.printStackTrace();}finally{try {if (rs!=null) {rs.close();}if (cs!=null) {cs.close();}if (conn!=null) {conn.close();}} catch (SQLException e) {e.printStackTrace();}}}}
存儲過程返回游標類型
- 創建新表,使用bulk collect into
create or replace procedure getHighSalPerson(p_emp out sys_refcursor)
is
v_avg_sal number;
beginselect avg(sal) into v_avg_sal from emp ;open p_emp for select empno,ename,job,sal from emp where sal>=v_avg_sal;
end;create table t_emp_high_sal(empno varchar2(100 byte),ename varchar2(100 byte),job varchar2(100 byte),sal number);declare
type row_type is table of t_emp_high_sal%rowtype;
emp_cursor sys_refcursor;
emp_row row_type;
begingetHighSalPerson(emp_cursor);fetch emp_cursor bulk collect into emp_row;for i in 1..emp_row.count loopdbms_output.put_line('員工編號:'||emp_row(i).empno||',員工姓名:'||emp_row(i).ename||',職位:'||emp_row(i).job||',工資:'||emp_row(i).sal);end loop;close emp_cursor;
end;
- 定義包類型
create or replace procedure getHighSalPerson(p_emp out getHighsalperson_package.type_cursor)
is
v_avg_sal number;
beginselect avg(sal) into v_avg_sal from emp ;open p_emp for select empno,ename,job,sal from emp where sal>=v_avg_sal;
end;create or replace package getHighsalperson_package
istype type_cursor is ref cursor;type type_record is record(empno varchar2(100 byte),ename varchar2(100 byte),job varchar2(100 byte),sal number);
end;declare
emp_cursor getHighsalperson_package.type_cursor;
emp_row getHighsalperson_package.type_record;
begingetHighSalPerson(emp_cursor);loop fetch emp_cursor into emp_row;exit when emp_cursor%notfound;dbms_output.put_line('員工編號:'||emp_row.empno||',員工姓名:'||emp_row.ename||',職位:'||emp_row.job||',工資:'||emp_row.sal);end loop;close emp_cursor;
end;
- 直接用空結果集的游標
create or replace procedure getHighSalPerson(p_emp out sys_refcursor)
is
v_avg_sal number;
beginselect avg(sal) into v_avg_sal from emp ;open p_emp for select empno,ename,job,sal from emp where sal>=v_avg_sal;
end;declare
cursor emp_cur is select empno,ename,job,sal from emp where 1=2;
emp_cursor sys_refcursor;
emp_row emp_cur%rowtype;
begingetHighSalPerson(emp_cursor);loop fetch emp_cursor into emp_row;exit when emp_cursor%notfound;dbms_output.put_line('員工編號:'||emp_row.empno||',員工姓名:'||emp_row.ename||',職位:'||emp_row.job||',工資:'||emp_row.sal);end loop;close emp_cursor;
end;
?