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;
運行後輸出HelloWorld。
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
iNUMBER;
nvarchar(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 innumber,s in varchar)
as
begin
UPDATE emp set salary =s whereemp_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 procedureupdate_yygzdbid
as
bs varchar(20);
kgh varchar(20);
bid number;
cursor c_db is select b_id,b_bs,b_kgh frompmdcdb;
begin
for temp in c_db loop
update yygz_db set b_id= temp.b_id where g_bs=temp.b_bs andg_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 whereemp_name=t;
end;
這個存儲過程使用了2個參數,並分別出現了IN和OUT,in代表輸入,out用於輸出,從下面的語句也可以看到salary寫入到變量r中了,這個r我們可以在調用存儲過程後得到。
這時編譯後會出現一個Warning(1,48):PLW-07203: 使用 NOCOPY 編譯器提示可能對參數 'R' 有所幫助,那麼nocopy是什麼呢,nocopy主要是針對in|outrecord/index-by table/varray/varchar2提高效率使用的, 對於number使用nocopy與否基本沒有影響.所以在'enable:performance'情況下不會對number提示warning.
我們把第一行改爲:procedure proc_test_Select_Single(tin 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;
運行後即可在輸出中看到結果了。
三、
1.今天我們首先寫一個漲工資的存儲過程,給每個低於5k工資的人漲點錢。
CREATE OR REPLACE PROCEDURE p_test(forRaise innumber)
as
begin
for v_emp in (select * from emp) loop
if(v_emp.salary<'5000')then
update emp set salary =(v_emp.salary+forRaise) whereemp_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 innumber)
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) whereemp_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 outnumber)
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) whereemp_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);
ENDpkg_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 =newOracleConnection(); //創建一個新連接
conn.ConnectionString ="DataSource='" + "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的對象中。
到此簡單的Oracle存儲過程操作就此就全部完成了,程序寫的很隨便,目的就是實現功能,將來有時間會進一步