oracle 存儲過程

用了兩年Oracle還沒寫過存儲過程,真是十分慚愧,從今天開始學習Oracle存儲過程,完全零起點,爭取每日一篇學習筆記,可能開始認識的不全面甚至有錯誤,但堅持下來一定會有收穫。

1. 建立一個存儲過程

         create or replace PROCEDURE firstPro

         IS

         BEGIN

         DBMS_OUTPUT.PUT_LINE('Hello World!');

         END;

其中IS關鍵字替換爲AS關鍵字結果不會出現任何變化,大多認爲他們是等同的,但也有一種說法解釋爲:一般PACKAGE 或者單獨的FUNCTION, PROCEDURE 都用ASPACKAGE 中的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_bsb_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個參數,並分別出現了INOUTin代表輸入,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 )

現在即使對invarchar沒有使用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 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)       循環中beginend不是必須的

(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 = newOracleCommand("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的對象中。

    到此簡單的Oracle存儲過程操作就此就全部完成了,程序寫的很隨便,目的就是實現功能,將來有時間會進一步

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章