oracle的存储过程
用了兩年Oracle還沒(méi)寫(xiě)過(guò)存儲(chǔ)過(guò)程,真是十分慚愧,從今天開(kāi)始學(xué)習(xí)Oracle存儲(chǔ)過(guò)程,完全零起點(diǎn),爭(zhēng)取每日一篇學(xué)習(xí)筆記,可能開(kāi)始認(rèn)識(shí)的不全面甚至有錯(cuò)誤,但堅(jiān)持下來(lái)一定會(huì)有收獲。
1. 建立一個(gè)存儲(chǔ)過(guò)程
???????? create or replace PROCEDURE firstPro
???????? IS
???????? BEGIN
???????? DBMS_OUTPUT.PUT_LINE('Hello World!');
???????? END;
其中IS關(guān)鍵字替換為AS關(guān)鍵字結(jié)果不會(huì)出現(xiàn)任何變化,大多認(rèn)為他們是等同的,但也有一種說(shuō)法解釋為:一般PACKAGE 或者單獨(dú)的FUNCTION, PROCEDURE 都用AS,PACKAGE 中的FUNCTION, PROCEDURE 用IS。
???????? DBMS_OUTPUT.PUT_LINE('Hello World!'); 是一個(gè)輸出語(yǔ)句。
2. 執(zhí)行存儲(chǔ)過(guò)程
Oracle返回結(jié)果需要使用包,那么存儲(chǔ)過(guò)程似乎只能在數(shù)據(jù)庫(kù)中執(zhí)行或被其他調(diào)用,編程語(yǔ)言似乎并不能直接調(diào)用存儲(chǔ)過(guò)程返回?cái)?shù)據(jù),是否能執(zhí)行他有待研究。那么首先在數(shù)庫(kù)中執(zhí)行上面的存儲(chǔ)過(guò)程。
???????? BEGIN
???????? FirstPro();//注意有括號(hào)
???????? END;
?????? 運(yùn)行后輸出Hello World。
3. 下面寫(xiě)一個(gè)稍復(fù)雜的存儲(chǔ)過(guò)程,他定義了變量,進(jìn)行了運(yùn)算,輸出一個(gè)count操作所用的時(shí)間。
?????? 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. 下面試驗(yàn)下怎么能給存儲(chǔ)過(guò)程賦值
?????? CREATE OR REPLACE procedure test(num in number) is
?????? begin
????????????? dbms_output.put_line('The input numer is:' || num);
?????? end ;
?????? 今天的就到這,明天將調(diào)用這個(gè)存儲(chǔ)過(guò)程,并試驗(yàn)一寫(xiě)對(duì)表的操作。
1. 首先把昨天帶參的存儲(chǔ)過(guò)程執(zhí)行一下
?????? declare
?????? n number;
?????? begin
????????????? n:=1;
????????????? test(num=>n);
?????? end;
?????? 注;在調(diào)用存儲(chǔ)過(guò)程時(shí),=>前面的變量為存儲(chǔ)過(guò)程的形參且必須于存儲(chǔ)過(guò)程中定義的一致,而=>后的參數(shù)為實(shí)際參數(shù)。當(dāng)然也不可以不定義變量保存實(shí)參,可寫(xiě)成如下形式:
?????? Begin
????????????? test(num=>1);
?????? end;
?????? 這樣我們就能更清楚得看到給存儲(chǔ)過(guò)程賦值的格式了。后面打算用存儲(chǔ)過(guò)程操作一些表,按照增刪改查的順序依次建立存儲(chǔ)過(guò)程。
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;
?????? 調(diào)用:
?????? 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;
?????? 注:調(diào)用存儲(chǔ)過(guò)程聲明varchar時(shí),必須限定長(zhǎng)度,即斜體的部分不能少。同時(shí)如果給變量賦值時(shí)大于限定的長(zhǎng)度了,則會(huì)提示ORA-06502: PL/SQL: 數(shù)字或值錯(cuò)誤 :? 字符串緩沖區(qū)太小。
?
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;
?????? 調(diào)用:
?????? DECLARE
? ?????????? n NUMBER;
? ?????????? s varchar(11);
?????? BEGIN
? ?????????? n := 2;
? ?????????? s:=3998;
? ?????????? PROc_TEST_UPdate_single(e_no => n,s=>s);
?????? END;
4. 號(hào)外,今天在開(kāi)發(fā)過(guò)程中正好有個(gè)數(shù)據(jù)庫(kù)更新操作可用存儲(chǔ)過(guò)程實(shí)現(xiàn),順便練習(xí)一下,需求是將一個(gè)表中的ID字段,查出來(lái)更新到另一個(gè)表中,兩個(gè)表通過(guò)b_bs和b_kgh關(guān)聯(lián)。存儲(chǔ)過(guò)程如下:
?????? 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;
?????? 運(yùn)行這個(gè)存儲(chǔ)過(guò)程:
?????? Begin
????????????? update_yygzdbid();
?????? end;
?????? 說(shuō)明:
?????? (1).在沒(méi)有參數(shù)的存儲(chǔ)過(guò)程定義時(shí)存儲(chǔ)過(guò)程的名稱不需要括號(hào),寫(xiě)成update_yygzdbid()是錯(cuò)誤的,
?????? (2). cursor c_db是定義一個(gè)游標(biāo),獲得查詢語(yǔ)句的結(jié)果集,
?????? (3). For temp in c_bd loop
???????????????????? Begin
???????????????????? End;
????????????? End loop
????????????? 是循環(huán)游標(biāo),其形式類似于C#中的foreach,? 獲得字段:temp.b_id。
5. 查詢
?????? 最后我們做一個(gè)查詢的存儲(chǔ)過(guò)程,能夠返回一個(gè)值,注意不是結(jié)果集,結(jié)果集是明天的目標(biāo)。
?????? 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;
?????? 這個(gè)存儲(chǔ)過(guò)程使用了2個(gè)參數(shù),并分別出現(xiàn)了IN和OUT,in代表輸入,out用于輸出,從下面的語(yǔ)句也可以看到salary寫(xiě)入到變量r中了,這個(gè)r我們可以在調(diào)用存儲(chǔ)過(guò)程后得到。
?????? 這時(shí)編譯后會(huì)出現(xiàn)一個(gè)Warning(1,48): PLW-07203: 使用 NOCOPY 編譯器提示可能對(duì)參數(shù) 'R' 有所幫助,那么nocopy是什么呢,nocopy主要是針對(duì)in|out record/index-by table/varray/varchar2提高效率使用的, 對(duì)于number使用nocopy與否基本沒(méi)有影響.所以在'enable:performance'情況下不會(huì)對(duì)number提示warning.
?????? 我們把第一行改為:procedure proc_test_Select_Single(t in varchar,r out nocopy varchar )
現(xiàn)在即使對(duì)in的varchar沒(méi)有使用nocopy也不會(huì)提示警告,
?????? 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;
?????? 運(yùn)行后即可在輸出中看到結(jié)果了。
三、
1. 今天我們首先寫(xiě)一個(gè)漲工資的存儲(chǔ)過(guò)程,給每個(gè)低于5k工資的人漲點(diǎn)錢(qián)。
?????? 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;
?????? 調(diào)用:
?????? DECLARE
? ?????????? FORRAISE NUMBER;
?????? BEGIN
? ?????????? FORRAISE :=1;
????????????? P_TEST(FORRAISE => FORRAISE);
?????? END;
?????? 這里要注意兩個(gè)地方:
(1)?????? 循環(huán)中begin和end不是必須的
(2)?????? 這里增加了if語(yǔ)句,其格式比較簡(jiǎn)單就不細(xì)說(shuō)了。
(3)?????? 這里沒(méi)有定義游標(biāo),在游標(biāo)的位置直接用select語(yǔ)句代替了。
?????? 2. 這里順便介紹下另外一種循環(huán),while循環(huán),實(shí)現(xiàn)同樣的功能
????????????? 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;
????????????? 說(shuō)明:
(1)?????? 這里需要定義一個(gè)游標(biāo),還要定義一個(gè)emp%rowtype類型的變量,%前面是表名,后面表示這個(gè)表的一行,
(2)?????? 在使用游標(biāo)前還要顯示的打開(kāi)游標(biāo),并將其賦值到row中,使用后關(guān)閉游標(biāo)。
(3)?????? C%found表示只有row中有值的時(shí)候才會(huì)進(jìn)行循環(huán)。
(4)?????? 經(jīng)過(guò)對(duì)比發(fā)現(xiàn)于while循環(huán)相比,for循環(huán)更像是C#中的foreach,使用起來(lái)方便很多。
(5)?????? 另從9i開(kāi)始提供的動(dòng)態(tài)游標(biāo)類型sys_refcursor,以前的版本必須要先創(chuàng)建一個(gè)ref cursor的類型,現(xiàn)在多個(gè)
OracleConnection conn = new OracleConnection();?? //創(chuàng)建一個(gè)新連接
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();
??? 這樣我們就可以給員工漲工資了,說(shuō)明:
(1)???????? 雖然給多個(gè)人漲了公司,但r的值是1,他只調(diào)用了1個(gè)存儲(chǔ)過(guò)程,或者說(shuō)受影響的只是1個(gè)。
(2)???????? 參數(shù)P1的名字必須和存儲(chǔ)過(guò)程中的一樣否則會(huì)提示:調(diào)用 'P_TEST' 時(shí)參數(shù)個(gè)數(shù)或類型錯(cuò)誤。
???????? 改動(dòng)存儲(chǔ)過(guò)程:
???????? 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;
???????? 增加了一個(gè)out 的number型,記錄改動(dòng)的次數(shù)。然后相應(yīng)的調(diào)整C#程序,獲得這個(gè)改動(dòng)的次數(shù)。
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(“你已經(jīng)給:”+p2.Value.ToString()+“人漲了工資”);
好了,今天就到這,下次返回?cái)?shù)據(jù)集。
Oracle使用存儲(chǔ)過(guò)程返回結(jié)果集必須使用包,包包括包頭和包體兩部分,包頭是定義部分包體是具體的實(shí)現(xiàn)??? 包頭:
??? 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;
??? 這里定義了個(gè)一個(gè)游標(biāo)和一個(gè)存儲(chǔ)過(guò)程。
??? 包體:
??? 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;
??? 這里實(shí)現(xiàn)里包頭中定義的存儲(chǔ)過(guò)程,實(shí)現(xiàn)了查詢工資超過(guò)一定數(shù)額的人的信息,而游標(biāo)則不用重新定義了,且存儲(chǔ)過(guò)程中的參數(shù)名必須和定義中的一致。下面我們看一下C#的調(diào)用部分。
??? OracleConnection conn = new OracleConnection();?? //創(chuàng)建一個(gè)新連接
??????????? conn.ConnectionString = "Data Source='" + "MyTest" + "';user id='" + "azkaser" + "';password='" + "sti" + "';";?? //寫(xiě)連接串?
??????????? 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();
??? 程序?qū)⒌玫降慕Y(jié)果存放在OracleDataReader的對(duì)象中。
??? 到此簡(jiǎn)單的Oracle存儲(chǔ)過(guò)程操作就此就全部完成了,程序?qū)懙暮茈S便,目的就是實(shí)現(xiàn)功能,將來(lái)有時(shí)間會(huì)進(jìn)一步
總結(jié)
以上是生活随笔為你收集整理的oracle的存储过程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Linux目录、文件的创建与删除
- 下一篇: 不要伤害指针(3)--指针和结构类型的关