用了兩年Oracle還沒寫過存儲過程,真是十分慚愧,從今天開始學習Oracle存儲過程,徹底零起點,爭取每日一篇學習筆記,可能開始認識的不全面甚至有錯誤,但堅持下來必定會有收穫。 1. 創建一個存儲過程 create or replace PROCEDURE firstPro IS BEGIN DBMS_OUTPUT.PUT_LINE('Hello World!'); END; 其中IS關鍵字替換爲AS關鍵字結果不會出現任何變化,大多認爲他們是等同的,但也有一種說法解釋爲:通常PACKAGE 或者單獨的FUNCTION, PROCEDURE 都用AS,PACKAGE 中的FUNCTION, PROCEDURE 用IS。 DBMS_OUTPUT.PUT_LINE('Hello World!'); 是一個輸出語句。 2. 執行存儲過程 Oracle返回結果須要使用包,那麼存儲過程彷佛只能在數據庫中執行或被其餘調用,編程語言彷佛並不能直接調用存儲過程返回數據,是否能執行他有待研究。那麼首先在數庫中執行上面的存儲過程。 BEGIN FirstPro();//注意有括號 END; 運行後輸出Hello World。 3. 下面寫一個稍複雜的存儲過程,他定義了變量,進行了運算,輸出一個count操做所用的時間。 CREATE OR REPLACE procedure testtime is n_start number; n_end number; samplenum number; use_time number; begin n_start:=dbms_utility.get_time; select count(*) into samplenum from emp; n_end:=dbms_utility.get_time; use_time:= n_end - n_start; dbms_output.put_line('This statement cost '|| use_time ||' miliseconds'); end; 4. 下面試驗下怎麼能給存儲過程賦值 CREATE OR REPLACE procedure test(num in number) is begin dbms_output.put_line('The input numer is:' || num); end ; 今天的就到這,明天將調用這個存儲過程,並試驗一寫對錶的操做。 1. 首先把昨天帶參的存儲過程執行一下 declare n number; begin n:=1; test(num=>n); end; 注;在調用存儲過程時,=>前面的變量爲存儲過程的形參且必須於存儲過程當中定義的一致,而=>後的參數爲實際參數。固然也不能夠不定義變量保存實參,可寫成以下形式: Begin test(num=>1); end; 這樣咱們就能更清楚得看到給存儲過程賦值的格式了。後面打算用存儲過程操做一些表,按照增刪改查的順序依次創建存儲過程。 2. 插入 CREATE OR REPLACE procedure proc_test_Insert_Single(e_no in number,e_name in varchar ,s in varchar,d in varchar) as begin insert into emp (emp_id,emp_name,salary,birthday) values (e_no,e_name,s,d); end; 調用: DECLARE i NUMBER; n varchar(5); s varchar(11); d varchar(10); BEGIN i:=10; n := 'text11'; s:='3998'; d:='1998-02-02'; PROc_TEST_Insert_single(e_no => i,e_name=>n,s=>s,d=>d); END; 注:調用存儲過程聲明varchar時,必須限定長度,即斜體的部分不能少。同時若是給變量賦值時大於限定的長度了,則會提示ORA-06502: PL/SQL: 數字或值錯誤 : 字符串緩衝區過小。 3. 更新 create or replace procedure proc_test_update_Single(e_no in number,s in varchar) as begin UPDATE emp set salary =s where emp_id=e_no; end; 調用: DECLARE n NUMBER; s varchar(11); BEGIN n := 2; s:=3998; PROc_TEST_UPdate_single(e_no => n,s=>s); END; 4. 號外,今天在開發過程當中正好有個數據庫更新操做可用存儲過程實現,順便練習一下,需求是將一個表中的ID字段,查出來更新到另外一個表中,兩個表經過b_bs和b_kgh關聯。存儲過程以下: create or replace procedure update_yygzdbid as bs varchar(20); kgh varchar(20); bid number; cursor c_db is select b_id,b_bs,b_kgh from pmdcdb; begin for temp in c_db loop update yygz_db set b_id= temp.b_id where g_bs=temp.b_bs and g_bh=temp.b_kgh; end loop; end; 運行這個存儲過程: Begin update_yygzdbid(); end; 說明: (1).在沒有參數的存儲過程定義時存儲過程的名稱不須要括號,寫成update_yygzdbid()是錯誤的, (2). cursor c_db是定義一個遊標,得到查詢語句的結果集, (3). For temp in c_bd loop Begin End; End loop 是循環遊標,其形式相似於C#中的foreach, 得到字段:temp.b_id。 5. 查詢 最後咱們作一個查詢的存儲過程,可以返回一個值,注意不是結果集,結果集是明天的目標。 CREATE OR REPLACE procedure proc_test_Select_Single(t in varchar,r out varchar ) as begin select salary into r from emp where emp_name=t; end; 這個存儲過程使用了2個參數,並分別出現了IN和OUT,in表明輸入,out用於輸出,從下面的語句也能夠看到salary寫入到變量r中了,這個r咱們能夠在調用存儲過程後獲得。 這時編譯後會出現一個Warning(1,48): PLW-07203: 使用 NOCOPY 編譯器提示可能對參數 'R' 有所幫助,那麼nocopy是什麼呢,nocopy主要是針對in|out record/index-by table/varray/varchar2提升效率使用的, 對於number使用nocopy與否基本沒有影響.因此在'enable:performance'狀況下不會對number提示warning. 咱們把第一行改成:procedure proc_test_Select_Single(t in varchar,r out nocopy varchar ) 如今即便對in的varchar沒有使用nocopy也不會提示警告, DECLARE T varchar2(4); R VARCHAR2(4); BEGIN T := 'zz'; PROC_TEST_SELECT_SINGLE(T => T,R => R ); DBMS_OUTPUT.PUT_LINE('R = ' || R); END; 運行後便可在輸出中看到結果了。 3、 1. 今天咱們首先寫一個漲工資的存儲過程,給每一個低於5k工資的人漲點錢。 CREATE OR REPLACE PROCEDURE p_test(forRaise in number) as begin for v_emp in (select * from emp) loop if(v_emp.salary<'5000') then update emp set salary =(v_emp.salary+forRaise) where emp_id=v_emp.emp_id; end if; end loop; end; 調用: DECLARE FORRAISE NUMBER; BEGIN FORRAISE :=1; P_TEST(FORRAISE => FORRAISE); END; 這裏要注意兩個地方: (1) 循環中begin和end不是必須的 (2) 這裏增長了if語句,其格式比較簡單就不細說了。 (3) 這裏沒有定義遊標,在遊標的位置直接用select語句代替了。 2. 這裏順便介紹下另一種循環,while循環,實現一樣的功能 CREATE OR REPLACE PROCEDURE p_test(forRaise in number) as cursor c is select * from emp; v_row emp%rowtype; begin open c; fetch c into v_row; while c%found Loop if(v_row.salary<'5000') then update emp set salary =(v_row.salary+forRaise) where emp_id=v_row.emp_id; end if; fetch c into v_row; end loop; close c; end; 說明: (1) 這裏須要定義一個遊標,還要定義一個emp%rowtype類型的變量,%前面是表名,後面表示這個表的一行, (2) 在使用遊標前還要顯示的打開遊標,並將其賦值到row中,使用後關閉遊標。 (3) C%found表示只有row中有值的時候纔會進行循環。 (4) 通過對比發現於while循環相比,for循環更像是C#中的foreach,使用起來方便不少。 (5) 另從9i開始提供的動態遊標類型sys_refcursor,之前的版本必需要先建立一個ref cursor的類型,如今多個 3. 如今咱們使用程序調用下漲工資的存儲過程,這個存儲過程是沒有返回值的。 OracleConnection conn = new OracleConnection(); //建立一個新鏈接 conn.ConnectionString = "Data Source='ds';user id='id ';password='pwd';"; OracleCommand cmd = new OracleCommand("P_TEST", conn); cmd.CommandType = CommandType.StoredProcedure; OracleParameter p1 = new OracleParameter("forRaise", OracleType.UInt32); p1.Value = 1; p1.Direction = System.Data.ParameterDirection.Input; cmd.Parameters.Add(p1); conn.Open(); int r=cmd.ExecuteNonQuery(); conn.Close(); 這樣咱們就能夠給員工漲工資了,說明: (1) 雖然給多我的漲了公司,但r的值是1,他只調用了1個存儲過程,或者說受影響的只是1個。 (2) 參數P1的名字必須和存儲過程當中的同樣不然會提示:調用 'P_TEST' 時參數個數或類型錯誤。 4. 如今咱們試着從存儲過程當中獲得點結果吧,我先看看我給幾我的漲了工資,我每月一共要多付多少錢了。 改動存儲過程: CREATE OR REPLACE PROCEDURE p_test(forRaise in number,res out number) is begin res:=0; for v_emp in (select * from emp) loop if(v_emp.salary<'4000') then update emp set salary =(v_emp.salary+forRaise) where emp_id=v_emp.emp_id; res:=res+1; end if; end loop; end; 增長了一個out 的number型,記錄改動的次數。而後相應的調整C#程序,得到這個改動的次數。 OracleCommand cmd = new OracleCommand("P_TEST", conn); cmd.CommandType = CommandType.StoredProcedure; OracleParameter p1 = new OracleParameter("forRaise", OracleType.UInt32); p1.Value = 4; p1.Direction = System.Data.ParameterDirection.Input; OracleParameter p2 = new OracleParameter("res", OracleType.UInt32); p2.Value = 10; p2.Direction = System.Data.ParameterDirection.Output; cmd.Parameters.Add(p1); cmd.Parameters.Add(p2); conn.Open(); int r=cmd.ExecuteNonQuery(); conn.Close(); MessageBox.Show(「你已經給:」+p2.Value.ToString()+「人漲了工資」); 好了,今天就到這,下次返回數據集。 Oracle使用存儲過程返回結果集必須使用包,包包括包頭和包體兩部分,包頭是定義部分包體是具體的實現 包頭: CREATE OR REPLACE PACKAGE pkg_test_select_mul AS TYPE myrctype IS REF CURSOR; PROCEDURE proc(s number, res OUT myrctype); END pkg_test_select_mul; 這裏定義了個一個遊標和一個存儲過程。 包體: CREATE OR REPLACE PACKAGE BODY "PKG_TEST_SELECT_MUL" AS PROCEDURE proc(s in number,res OUT myrctype) IS BEGIN OPEN res FOR Select emp_id,emp_Name, salary,birthday From emp where salary> s; END proc; END PKG_TEST_SELECT_MUL; 這裏實現裏包頭中定義的存儲過程,實現了查詢工資超過必定數額的人的信息,而遊標則不用從新定義了,且存儲過程當中的參數名必須和定義中的一致。下面咱們看一下C#的調用部分。 OracleConnection conn = new OracleConnection(); //建立一個新鏈接 conn.ConnectionString = "Data Source='" + "MyTest" + "';user id='" + "azkaser" + "';password='" + "sti" + "';"; //寫鏈接串 OracleCommand cmd = new OracleCommand("PKG_TEST_SELECT_MUL.proc", conn); cmd.CommandType = CommandType.StoredProcedure; OracleParameter p1 = new OracleParameter("s", OracleType.Number); p1.Value = 4000; p1.Direction = ParameterDirection.Input; OracleParameter p2 = new OracleParameter("res", OracleType.Cursor); p2.Direction = ParameterDirection.Output; cmd.Parameters.Add(p1); cmd.Parameters.Add(p2); conn.Open(); OracleDataReader myReader = cmd.ExecuteReader(); while (myReader.Read()) { MessageBox.Show(myReader.GetString(1)); } conn.Close(); 程序將獲得的結果存放在OracleDataReader的對象中。
存儲過程 1 CREATE OR REPLACE PROCEDURE 存儲過程名 2 IS 3 BEGIN 4 NULL; 5 END; 行1: CREATE OR REPLACE PROCEDURE 是一個SQL語句通知Oracle數據庫去建立一個叫作skeleton存儲過程, 若是存在就覆蓋它; 行2: IS關鍵詞代表後面將跟隨一個PL/SQL體。 行3: BEGIN關鍵詞代表PL/SQL體的開始。 行4: NULL PL/SQL語句代表什麼事都不作,這句不能刪去,由於PL/SQL體中至少須要有一句; 行5: END關鍵詞代表PL/SQL體的結束 存儲過程建立語法: create or replace procedure 存儲過程名(param1 in type,param2 out type) as 變量1 類型(值範圍); --vs_msg VARCHAR2(4000); 變量2 類型(值範圍); Begin Select count(*) into 變量1 from 表A where列名=param1; If (判斷條件) then Select 列名 into 變量2 from 表A where列名=param1; Dbms_output。Put_line(‘打印信息’); Elsif (判斷條件) then Dbms_output。Put_line(‘打印信息’); Else Raise 異常名(NO_DATA_FOUND); End if; Exception When others then Rollback; End; 注意事項: 1, 存儲過程參數不帶取值範圍,in表示傳入,out表示輸出 類型可使用任意Oracle中的合法類型。 2, 變量帶取值範圍,後面接分號 3, 在判斷語句前最好先用count(*)函數判斷是否存在該條操做記錄 4, 用select 。。。into。。。給變量賦值 5, 在代碼中拋異經常使用 raise+異常名 CREATE OR REPLACE PROCEDURE存儲過程名 ( --定義參數 is_ym IN CHAR(6) , the_count OUT NUMBER, ) AS --定義變量 vs_msg VARCHAR2(4000); --錯誤信息變量 vs_ym_beg CHAR(6); --起始月份 vs_ym_end CHAR(6); --終止月份 vs_ym_sn_beg CHAR(6); --同期起始月份 vs_ym_sn_end CHAR(6); --同期終止月份 --定義遊標(簡單的說就是一個能夠遍歷的結果集)
簡要記錄存儲過程語法與Java程序的調用方式 一 存儲過程 首先,咱們創建一個簡單的表進行存儲過程的測試 create table xuesheng(id integer, xing_ming varchar2(25), yu_wen number, shu_xue number); insert into xuesheng values(1,'zhangsan',80,90) insert into xuesheng values(2,'lisi',85,87) 1)無返回值的存儲過程 create or replace procedure xs_proc_no is begin insert into xuesheng values (3, 'wangwu', 90, 90); commit; end xs_proc_no; 2)有單個數據值返回的存儲過程 複製代碼 create or replace procedure xs_proc(temp_name in varchar2, temp_num out number) is num_1 number; num_2 number; begin select yu_wen, shu_xue into num_1, num_2 from xuesheng where xing_ming = temp_name; --dbms_output.put_line(num_1 + num_2); temp_num := num_1 + num_2; end; 複製代碼 其中,以上兩種與sql server基本相似,而對於返回數據集時,上述方法則不能知足咱們的要求。在Oracle中,通常使用ref cursor來返回數據集。示例代碼以下: 3)有返回值的存儲過程(列表返回) 首先,創建咱們本身的包。並定義包中的一個自定義ref cursor create or replace package mypackage as type my_cursor is ref cursor; end mypackage; 在定義了ref cursor後,能夠書寫咱們的程序代碼 create or replace procedure xs_proc_list(shuxue in number, p_cursor out mypackage.my_cursor) is begin open p_cursor for select * from xuesheng where shu_xue > shuxue; end xs_proc_list; 2、程序調用 在本節中,咱們使用java語言調用存儲過程。其中,關鍵是使用CallableStatement這個對象,代碼以下: String oracleDriverName = "oracle.jdbc.driver.OracleDriver"; // 如下使用的Test就是Oracle裏的表空間 String oracleUrlToConnect = "jdbc:oracle:thin:@127.0.0.1:1521:orcl"; Connection myConnection = null; try { Class.forName(oracleDriverName); } catch (ClassNotFoundException ex) { ex.printStackTrace(); } try { myConnection = DriverManager.getConnection(oracleUrlToConnect, "xxxx", "xxxx");//此處爲數據庫用戶名與密碼 } catch (Exception ex) { ex.printStackTrace(); } try { CallableStatement proc=null; proc=myConnection.prepareCall("{call xs_proc(?,?)}"); proc.setString(1, "zhangsan"); proc.registerOutParameter(2, Types.NUMERIC); proc.execute(); String teststring=proc.getString(2); System.out.println(teststring); } catch (Exception ex) { ex.printStackTrace(); } 對於列表返回值的存儲過程,在上述代碼中作簡單修改。以下 複製代碼 CallableStatement proc=null; proc=myConnection.prepareCall("{call getdcsj(?,?,?,?,?)}"); proc.setString(1, strDate); proc.setString(2, jzbh); proc.registerOutParameter(3, Types.NUMERIC); proc.registerOutParameter(4, OracleTypes.CURSOR); proc.registerOutParameter(5, OracleTypes.CURSOR); proc.execute(); ResultSet rs=null; int total_number=proc.getInt(3); rs=(ResultSet)proc.getObject(4); 複製代碼 上述存儲過程修改完畢。另外,一個複雜的工程項目中的例子:查詢一段數據中間隔不超過十分鐘且連續超過100條的數據。即上述代碼所調用的getdcsj存儲過程
轉載地址:http://www.cnblogs.com/liliu/archive/2011/06/22/2087546.htmlhtml
轉載地址:http://blog.sina.com.cn/s/blog_82faefb00100tn6o.htmljava