Oracle Procedure 存儲過程

Oracle Procedure




一、塊
塊是pl/sql組成的基本單元,本篇講述存儲過程,鑑於2者有很高的相似性,且是其中一種塊,所以在此做一個簡單的塊講述


1、塊的結構
declare
  定義部分(可選)
begin
  可執行部分(必選)
exception
  異常處理部分(可選)
end;


2、塊的分類
(1)、無名塊(也叫匿名塊):動態構造並只能執行一次,常用來測試或執行存儲過程。
(2)、命名塊:加了用<<>>括起了帶標號的無名塊。
(3)、子程序:包括存儲過程【本篇要講述的內容】、函數和包等。這些塊一旦被定義便會存儲在數據庫中,可隨時調用。
(4)、觸發器:根據觸發的事件調用。


3、舉例
(1)、無名塊
declare
  v_name varchar2(8):='CC';
  v_name2 constant varchar2(8):='CC';  --常量,constant關鍵字用於指定常量
  v_name3 varchar2(8) default 'CC';  --和:=等價
  v_name4 varchar2(8) not null :='CC';  --爲not null的必須被賦值
  v_name4 varchar2(8);  --缺省情況下,變量被初始化爲null
  v_name5 boolean not null default false;
begin
  sp_flux_user_list();
end;


(2)、命名塊
說明:主要用於不同作用域同名變量間的引用
<<insertype1>>
declare
  v_name number:=1;
begin
  insert into t_type values(v_name);
  <<insertype2>>
  declare
    v_name number:=2;
  begin
    dbms_output.put_line(v_name);
  end;
  <<insertype3>>
  declare
    v_name number:=2;
  begin
    dbms_output.put_line(v_name);
  end;
  <<insertype4>>
  declare
    v_salary number:=2;
  begin
    dbms_output.put_line(v_salary);
  end;
end;


如上面的例子,現在引入2個概念
變量的作用域:指變量在程序中的有效範圍。對於一個plsql變量,它的作用域是從該變量被聲明開始到變量所在塊結束。
變量的可視域:指變量前不加以限定就能直接訪該變量的那一段程序。
上面例子中,insertype1塊的變量v_name在到所在塊結束前都是其作用域,並且在insertype4塊內可視,但在insertype2和insertype3內不可視,因爲insertype2和insertype3內有同名變量。
當在塊中聲明一個變量時,如果在子塊中也聲明瞭同名的變量,則它們的作用域沒變。但在子塊中,只有子塊中聲明的變量時可視的,這時要引用父塊中的變量名時,必須加以限定。
這時要在insertype2內引用insertype1的變量就需要這樣寫“insertype1.v_name:=v_name”,但insertype2與insertype3的變量無法互相引用,因爲作用域互不相同。


(3)、存儲過程
create or replace procedure sp_flux_user_list
is
  v_em emp.empno%TYPE; --表示以emp表中的empno字段的類型定義變量
  v_de dept%rowtype; --表示v_de是一行數據
begin
  dbms_session.set_nls('nls_date_format','"yyyy-mm-dd hh24:mi:ss"');
  dbms_output.put_line(sysdate);
end sp_flux_user_list;


(4)、觸發器
create or replace trigger tri_tse
before insert or update or delete on emp
begin
  if to_char(sysdate,'DY','nls_date_language=american') in ('SAT','SUN') then
    raise_application_error(-20001,'不能在休息日改變僱員信息');
  end if;
end;


4、注意
(1)、定義部分的標識符必須以字母開頭;一個或多個字母、數字、或特殊符號組成;長度不超過30;不能有空格;非保留字
plsql不區分大小寫,如要區分大小寫或空格或保留字等,則使用"",例如"aAt code" number;
(2)、plsql將長度爲0的字符串當做null來處理。
(3)、在PLSQL裏,select、DML語句可以直接寫,而DDL、DCL語句不能直接寫。




二、存儲過程概述,存儲過程大體分爲這麼幾個部分: 
  1.創建語句:create or replace procedure 存儲過程名 
    如果沒有or replace語句,則僅僅是新建一個存儲過程。如果系統存在該存儲過程,則會報錯。Create or replace procedure 如果系統中沒有此存儲過程就新建一個,如果系統中有此存儲過程則把原來刪除掉,重新創建一個存儲過程。 
  2.存儲過程名定義:包括存儲過程名和參數列表。參數名和參數類型。參數名不能重複, 參數傳遞方式:IN, OUT, IN OUT 
    IN 表示輸入參數,按值傳遞方式,並且它不允許在存儲過程中被重新賦值(in相當於JAVA的final)。如果存儲過程的參數沒有指定存參數傳遞類型,默認爲IN。
    OUT 表示輸出參數,可以理解爲按引用傳遞方式,可以作爲存儲過程的輸出結果,供外部調用者使用。需要注意,當一個參數被指定爲OUT類型時,就算在調用存儲過程之前對該參數進行了賦值,在存儲過程中該參數的值仍然是null。 
    IN OUT 即可作輸入參數,也可作輸出參數,屬於真正的按引用傳遞參數。
    參數的數據類型只需要指明類型名即可,不能指定寬度,參數的寬度由外部調用者決定。過程可以有參數,也可以沒有參數。
  3.變量聲明塊:緊跟着的as (is)關鍵字,可以理解爲pl/sql的declare關鍵字,用於聲明變量。 
    變量聲明塊用於聲明該存儲過程需要用到的變量,它的作用域爲該存儲過程。另外這裏聲明的變量必須指定寬度。遵循PL/SQL的變量聲明規範。且最大長度爲30. 
    變量名[CONSTANT] 類型標識符[NOT NULL][:=值|DEFAULT 值];
  4.過程語句塊:從begin 關鍵字開始爲過程的語句塊。存儲過程的具體邏輯在這裏來實現。 
  5.異常處理塊:關鍵字爲exception ,爲處理語句產生的異常。該部分爲可選 
  6.結束塊:由end關鍵字結果。




三、存儲過程參數的傳遞方式
1.無參過程
create or replace procedure pro_time
is
  v_em emp.empno%TYPE; --字段變量,表示以emp表中的empno字段的類型定義變量
  v_de dept%rowtype; --記錄變量,表示v_de是一行數據
begin
  dbms_session.set_nls('nls_date_format','"yyyy-mm-dd hh24:mi:ss"');
  dbms_output.put_line(sysdate);
  dbms_output.put_line(v_de.id);--輸出記錄變量的某個字段
end pro_time;


2.帶輸入輸出參數的過程
create or replace procedure pro_emp(
  i_eno emp.empno%type, --默認in
  i_sal in emp.sal%type,
  o_name out varchar2,
  num1 in out number) is
begin
  update emp set sal=i_sal where empno=i_eno return ename into o_name;
  num1:=i_sal*10;
  commit;
end pro_emp;


調用
declare
  v_eno number:=1111;
  v_salary:=2500;
  vn emp.ename%type;
  n1 number:=30;
begin
  pro_sal(v_eno,v_salary,vn,n1);
  dbms_output.put_line('姓名:' || vn || '升至10倍後的工資:' || n1);
end;


說明:
我們無法在存儲過程的定義中指定存儲參數的寬度,也就導致了我們無法在存儲過程中控制傳入變量的寬度。這個寬度是完全由外部傳入時決定的。
對於IN參數,其寬度是由存儲過程外部決定。 
對於OUT 和IN OUT 參數,其寬度是由存儲過程內部決定。 
比較明智的方法就是參數的數據類型使用%type,這樣雙方就達成了一致。但注意,使用%type爲參數定義類型,該參數具有定義在形參上而不是通過實參傳遞的數據長度。




四、存儲過程參數的默認值
說明:
可以通過default 關鍵字爲存儲過程的參數指定默認值。在對存儲過程調用時,就可以省略默認值。 
需要注意的是,默認值僅僅支持IN傳輸類型的參數,OUT 和 IN OUT不能指定默認值。。


例一:
create or replace procedure pro_test(i1 in number,
                                     i2 in date default sysdate) is
begin
  dbms_output.put_line(to_char(add_months(i2, i1),'yyyy-mm-dd'));
end pro_test;
調用
exec pro_test(1) --輸出 2012-09-17(今天是2012-08-17)
exec pro_test(1,to_date('2012-01-01','yyyy-mm-dd')) --輸出 2012-02-01


例二:當默認值不在最後一位怎麼調用
create or replace procedure pro_test(i2 in date default sysdate,
                                     i1 in number) is
begin
  dbms_output.put_line(to_char(add_months(i2, i1),'yyyy-mm-dd'));
end pro_test;
調用
exec pro_test(i1=>1) --輸出 2012-09-17(今天是2012-08-17)




五、存儲過程參數的傳參形式
由上面默認值不在第一位的情況可見,傳參的形式有2種
第一種是位置表示法,按照參數順序傳參,例如exec pro_test(1,to_date('2012-01-01','yyyy-mm-dd')),這種形式不需要知道過程的形參名稱,但必須按照參數順序傳參。
第二種是名稱表示法,指定參數名稱傳參,例如exec pro_test(i1=>1),這種形式不需要按照參數的順序傳參,但必須知道過程形參的名稱,且如果使用缺省值的參數不是存儲過程最後一個參數,則只能用名稱表示法。
另外,位置表示法和名稱表示法在一些調用中也可以混合使用。但是,當在調用存儲過程中出現了第一個名稱表示法的參數時,後面的參數也必須使用名稱表示法傳值。


例一:
declare
  v_bir varchar2(10):='1999';
  v_sex varchar2(10):='1';
begin
  pro_tb_flux_user_list(v_code,v_name,p_bir=>v_bir,p_sex =>v_sex);
end;




六、存儲過程的結構
1.存儲過程的結構:
CREATE OR REPLACE PROCEDURE myproc AS --這裏寫as和is是等價
/*聲明變量部分*/
  v_name varchar2(10);  --變量
  c_male constant int :=1;  --常量
  type au_record is record(name varchar2(10),sex number);
  v_Author au_record;  --記錄類型
Begin
/*執行部分,必須且最重要的部分,至少包含一條可執行語句*/
Exception
/*異常處理*/
End myproc;




七、存儲過程的選擇語句


1、IF語句的三種形式:
IF-THEN-END IF
IF-THEN-ELSE-END IF
IF-THEN-ELSIF-ELSE-END IF


IF…ELSIF…ELSE語句
例子:
CREATE OR REPLACE PROCEDURE myproc AS
  cou NUMBER;
BEGIN 
  cou:= 1;
  IF cou>10 THEN DBMS_OUTPUT.put_line('cou = '||cou);
  ELSIF cou<5 THEN DBMS_OUTPUT.put_line('值小於5');
  ELSE DBMS_OUTPUT.put_line('條件不滿足');
  END IF;
END myproc;


2、case語句的三種形式


基本CASE 結構
CASE 選擇變量名
WHEN 表達式1 THEN 語句序列1
WHEN 表達式2 THEN 語句序列2
WHEN 表達式n THEN 語句序列n
ELSE 語句序列n+l
END CASE;


表達式CASE 結構
變量:=CASE 選擇變量名
WHEN 表達式1 THEN 值1
WHEN 表達式2 THEN 值2
WHEN 表達式n THEN 值n
ELSE 值n+l
END CASE;


搜索CASE 結構
CASE 
WHEN 條件表達式1 THEN 語句序列1
WHEN 條件表達式2 THEN 語句序列2
WHEN 條件表達式n THEN 語句序列n
ELSE 語句序列n+1
END CASE;




八、存儲過程的循環語句


1、Loop循環(do...while)
說明:此循環是先執行一次之後再進行判斷
格式:
LOOP 
循環的語句;
EXIT WHEN 終止條件,true則終止;
循環條件必須更改;
END LOOP;
--如果沒有WHEN 條件,遇到EXIT 語句則無條件退出循環。


例子(循環輸出1~10):
DECLARE
  cou NUMBER;
BEGIN -- 必須給一個初始值
  cou := 1;
  LOOP 
    DBMS_OUTPUT.put_line('cou = '||cou);
    cou := cou + 1;
    EXIT WHEN cou>10;
  END LOOP;
END;


2、While循環
說明:此循環是先判斷,之後如果條件滿足則執行
格式:
while(判斷循環的條件,true則執行) loop 
  循環的語句;
  循環條件的改變;
End loop;


例子:
DECLARE cou NUMBER;
BEGIN -- 必須給一個初始值
  cou := 1;
  WHILE(cou<10) LOOP 
    DBMS_OUTPUT.put_line('cou = '||cou);
    cou := cou + 1;
  END LOOP;
END;


3、For循環
說明:循環變量被隱式定義爲integer類型的局部變量,缺省情況下,循環變量從初始值以1的增量遞增到結束值,如果使用關鍵字reverse,則循環從結束值以1的減量遞減到初始值。
格式:
FOR 控制變量 in [REVERSE] 下限..上限LOOP
語句1;
語句2;
……
END LOOP;


循環控制變量是隱含定義的,不需要要聲明。
下限和上限用於指明循環次數。正常情況下循環控制變量的取值由下限到上限遞增,
REVERSE 關鍵字表示循環控制變量的取值由上限到下限遞減。


例子:
declare
  v_l binary_integer:=1;
begin
  for v_loop in 1..10 loop
    dbms_output.put_line(v_loop);
--exit when (v_loop=8),加上這句則當v_loop=8時退出;exit則直接退出。
  end loop;
end;


declare
  v_l binary_integer:=1;
begin
  for v_loop in reverse 1..10 loop
    dbms_output.put_line(v_loop);
  end loop;
end;




九、存儲過程的終止循環跳轉語句(EXIT和EXIT WHEN)


1、關於EXIT和EXIT WHEN的區別:
EXIT:無條件終止語句,當遇到一個exit語句,則立即終止當前的循環。
EXIT WHEN:有條件終止語句,當遇到exit when語句時,首先檢測when子句中的條件,爲true則終止。


2、主要作用
提前退出當前循環
提前退出嵌套循環


帶標號的loop循環語句,主要用於供內層的exit語句終止外層的循環
例如:


declare
  v_l binary_integer:=1;
begin
<<outer>>
  for i in 1..100 loop
    <<inter>>
    for v_loop in 1..10 loop
      dbms_output.put_line(v_loop);
      exit outer when outer.i*v_loop>=20;  --終止外層循環
      exit when v_loop=5;  --終止本層(內層)循環,等同於exit inter when v_loop=5
    end loop inter;
  end loop outer;
end;




十、存儲過程的無條件跳轉語句(GOTO語句)與NULL語句


1.GOTO語句概述
說明:GOTO爲非結構化語句,跳轉到某個標號,標號必須唯一,且標號後必須是一個可執行的語句或PLSQL塊
限制:不能跳轉到IF語句、loop語句、或子塊中;不能從子程序中挑出;不能從異常處理跳轉到可執行部分。


2.NULL語句概述
NULL語句不會執行任何操作,相當於一個佔位符,並且會直接連接控制傳遞到下一條語句,使用NULL語句的好處是可以提高PL/SQL程序的可讀性,使語句變得有意思合法。


例一:
DECLARE 
  eno emp.empno%TYPE; 
  sal emp.sal%TYPE; 
BEGIN 
  eno := &en;
  SELECT sal INTO sal FROM emp WHERE empno=eno;
  IF sal>3500 THEN goto po1;
  ELSIF sal>2000 THEN goto po2;
  ELSE goto po3;END IF;
<<po1>> DBMS_OUTPUT.put_line('高工資。。。'); 
<<po2>> DBMS_OUTPUT.put_line('中等工資。。'); 
<<po3>> DBMS_OUTPUT.put_line('底工資。。。');  --標號<<po3>>後面必須要跟着可執行的語句或PLSQL塊
END ;


例二:如果跳轉的後面沒有什麼需要執行的語句了怎麼辦?用null語句。
DECLARE 
  eno emp.empno%TYPE;
  sal emp.sal%TYPE; 
BEGIN 
  eno := &en; 
  SELECT sal INTO sal FROM emp WHERE empno=eno ; 
  IF sal>3500 THEN goto po1 ; 
  ELSIF sal>2000 THEN goto po2 ; 
  ELSE goto po3 ; END IF ; 
<<po1>> DBMS_OUTPUT.put_line('高工資。。。') ; 
<<po2>> DBMS_OUTPUT.put_line('中等工資。。') ; 
<<po3>> null;  --沒有null則語法錯誤,null顯式指定一個什麼也不做的操作,相當於一個佔位符,使語句變得有意義合法。
END ;




十一、存儲過程細節
1、刪除存儲過程
drop procedure procedure_name;


2、有關存錯過程中使用到的集合數據類型(PL/SQL表、索引表、嵌套表、可變數組)、遊標(普通遊標、參數遊標、遊標變量、循環選擇)、異常處理等在本博客的其他文章做專題詳述。


發佈了65 篇原創文章 · 獲贊 24 · 訪問量 41萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章