oracle中的存儲過程例子

用了兩年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
                     EndEnd 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

相關文章
相關標籤/搜索