oracle pl/sql之函數(function)

一.PL/SQL語言介紹
sql

雖然SQL是用於從數據庫中檢索數據和添加、修改或刪除數據的主要語言,可是它確實缺少某些重要的編程構造。例如,SQL不能控制執行流,也不能爲了之後的重用而將數據存儲爲變量,甚至在出現錯誤的時候不能執行特定的動做。Oracle採用PL/SQL解決了上述問題。PL/SQL表示對SQL語言的過程語言擴展(Procedural Language Extensions to SQL)。數據庫

在Oracle中集成PL/SQL以前,應用程序檢索和操縱數據庫信息的方式會受到限制。咱們既能夠經過諸如SQL*Plus之類的交互式工具所生成的腳本文件來向服務器發送SQL語句,也能夠將這些SQL語句嵌入在一個名爲Pro*C的語言預編譯器內。後一種方法提供了咱們想獲得的處理能力,可是卻難以實現。這種方法須要使用若干行代碼來解釋鏈接數據庫的方式、運行的語句以及如何使用語句執行的結果。SQL與預編譯語言可以使用的數據類型也存在差別。PL/SQL則解決了上述兩種方法的侷限性。編程

PL/SQL代碼必須被編寫爲若干名爲代碼塊(block)的部分。由於PL/SQL是一種編譯型語言,因此這些代碼塊在執行以前必須通過編譯器的處理。編譯是一個檢查過程,這個過程可以確保代碼中引用的對象存在以及語句具備正確的語法。代碼在編譯過程完成後能夠運行,可是必須在PL/SQL引擎內運行。PL/SQL引擎不是一個與Oracle服務器分離的產品,而是Oracle數據庫的一個集成部分,這個集成部分可以獲取並執行PL/SQL代碼塊。服務器

二.PL/SQL代碼塊app

PL/SQL代碼塊具備兩種形式:匿名塊與命名塊。匿名(anonymous)PL/SQL代碼塊是頭部不具備名稱的PL/SQL代碼。此時,咱們能夠經過諸如SQL*Plus之類的交互式工具將匿名塊發送至PL/SQL引擎,這些代碼塊隨後會當即運行。需要記住的是,PL/SQL是一種編譯型語言,所以匿名塊會被編譯並運行,隨後則會消失。若是但願再次進行運行,則必須將完整的代碼塊再次發送至PL/SQL引擎,這些代碼在PL/SQL引擎內會再次被編譯並運行,隨後又會消失。爲了更易於再次運行,匿名塊能夠被存儲至操做系統的腳本文件中。ide

使用命名(named)PL/SQL代碼塊的名稱就能夠屢次"調用"命名塊。所以,命名塊常常被用於實現某個程序內的模塊化。這個程序能夠被分爲若干可以被屢次調用的模塊或子程序。Oracle中存在下列4種命名子程序:過程、函數、程序包和觸發器。過程與函數是在其擁有者的模式中能夠被建立爲數據庫對象的子程序。若是進行了上述建立,那麼這些過程與函數就被稱爲存儲子程序(stored subprogram)。使用存儲子程序的優勢是它們在建立階段通過編譯,隨後在不須要重編譯開銷的狀況下可以屢次運行。程序包是若干過程與函數的集合,而且沒法經過其名稱被調用,可是對於程序包內的不一樣子程序(也就是過程與函數)來講,只要前面添加了程序包名,就可使用其名稱分別調用這些子程序。觸發器是在發生觸發動做(如在某個表中插入一條記錄、用戶登入數據庫或出現系統錯誤)時被自動調用的代碼塊。模塊化

全部PL/SQL代碼塊都具備相同的結構,包括一個用於聲明變量和其餘標識符的聲明部分(這一部分以DECLARE關鍵字開始)、一個用於運行代碼的執行部分(這一部分以BEGIN關鍵字開始),一個用於捕獲錯誤的異常部分(這一部分以EXCEPTION關鍵字開始)以及一個使用END關鍵字指示的代碼塊結束符。對於匿名PL/SQL代碼塊,必需的元素只有BEGN和END關鍵字(以及這兩個關鍵字之間的代碼)。函數

有效的PL/SQL代碼塊只須要BEGIN和END關鍵字,而且這兩個關鍵字之間至少要存在一行有效的代碼。工具

PL/SQL代碼塊的每行代碼都使用分號來結束,同時Oracle將整個代碼塊視爲一個執行單元,這意味着先運行完該代碼塊,而後再將運行結果發送至調用程序或客戶工具。下面示例給出了一個匿名PL/SQL代碼塊:this

DECLARE  

Val1 NUMBER := 5;  

Val2 NUMBER := 2;  

TheAnswer NUMBER;  

BEGIN  

TheAnswer:=Val1 + Val2;  

DBMS OUTPUTPUT_LINE(:′The answer is′ | | TheAnswer);  

EXCEPTION  

WHEN ZERO_DIVIDE THEN  

DBMS_OUTPUT.PUT_ LINE(′Cannot divide by zero!′);  

END;

命名PL/SQL代碼塊具備類似的結構,可是容許將某些參數傳入代碼塊,而且能夠選擇向調用程序返回某些值。函數必須老是返回一個值並指定返回類型,而過程與觸發器則不須要實現某些功能。下面示例演示瞭如何將前面給出的一個匿名PL/SQL代碼塊做爲一個過程(注意在過程名與參數後面不須要使用DECLARE關鍵字定義的聲明部分):

CREATE OR REPLACE PROCEDURE Add_Nums (Val1 IN NUMBER)  

AS  

TheAnswer NUMBER;  

BEGIN  

TheAnswer := Val1 + Val2  

DBMS_OUTPUT.PUT LINE('The answer is' 丨丨 TheAnswer);  

EXCEPTION  

WHEN ZERO_DIVIDE THEN  

DBMS OUTPUT.PUT LINE('Cannot divide by zero!');  

END;

爲了調用上面這個過程並向其傳遞要相加的數字,須要在SQL*Plus、SQL Worksheet或其餘支持的客戶查詢工具中執行以下所示的命令:

Execute Add_nums(10,2)

三.存儲過程和函數的區別

過程與函數(另外還有包與觸發器)是命名的PL/SQL塊(也是用戶的方案對象),被編譯後存儲在數據庫中,以備執行。所以,其它PL/SQL塊能夠按名稱來使用他們。因此,能夠將商業邏輯、企業規則寫成函數或過程保存到數據庫中,以便共享。

過程和函數統稱爲PL/SQL子程序,他們是被命名的PL/SQL塊,均存儲在數據庫中,並經過輸入、輸出參數或輸入/輸出參數與其調用者交換信息。過程和函數的惟一區別是函數總向調用者返回數據,而過程則不返回數據。在本節中,主要介紹:

函數通常狀況下是用來計算並返回一個計算結果而存儲過程通常是用來完成特定的數據操做(好比 修改、插入數據庫表或執行某些DDL語句等等


函數語法:

create or  replace function function_name(argu1 datatype,argu2 datatype...)

return datatype

is|as

PL/SQL Block;



在創建函數時,在函數頭部必需要帶有RETURN語句,在函數體內至少要包含一條RETURN語句。



1.不帶任何參數

---code

create or replace function fun_user

return varchar2

is

v_user varchar2(50);


begin

select username  into v_user from user_users;

return v_user;

end;



-------

//調試

SQL> select fun_user from dual;    ---method


FUN_USER

--------------------------------------------------------------------------------

SCOTT


SQL>

---mothod

SQL> var v_res varchar2(100);

SQL> exec :v_res :=fun_user;


PL/SQL procedure successfully completed.


SQL> print v_res;


V_RES

--------------------------------------------------------------------------------

SCOTT




2.帶IN參數


//經過僱員名獲取員工薪水

create or replace function fun_get_sal(v_ename varchar2) return number is

 v_error_code    number;

 v_error_message varchar2(100);

 v_sal           number;


begin


 select sal into v_sal from emp where upper(ename) = upper(v_ename);

 return v_sal;

exception

 when others then

   v_error_code    := sqlcode;

   v_error_message := substr(sqlerrm, 1, 100);

   insert into errors

     (error_id, program_name, error_code, error_message)

   values

     (seq_errors.nextval, 'fun_get_sal', v_error_code, v_error_message);

   commit;

end;






3.帶OUT參數


若是要同時返回多個數據,須要使用輸出參數


//返回僱員名所在的部門名和崗位


---code

create or replace function fun_ename_info(v_ename varchar2,v_job out varchar2)

return varchar2

is


v_deptname dept.dname%type;


begin

select b.dname,a.job into  v_deptname,v_job from emp a

join dept b on a.deptno=b.deptno

where upper(a.ename) = upper(v_ename);


return v_deptname;


end;

------------------



//調試

SQL> var job varchar2(50);

SQL> var dname varchar2(50);

SQL>

SQL> exec :dname :=fun_ename_info('scott',:job);


PL/SQL procedure successfully completed.


SQL> print dname job;



5.練習

//function


1.創建函數fun_valid_customer,根據輸入的客戶號,檢查客戶是否存在,若是客戶存在,則返回TRUE,不然返回FALSE。

---------------------------------------

create or replace function fun_valid_customer(v_customer_id number) return boolean

is

   v_tmp number;

begin

   select 1 into v_tmp from customers where customer_id=v_customer_id;

   return true;

exception

when no_data_found then

   return false;

end;

---------------------------------------



2.創建函數fun_get_total,根據輸入的訂單號返回訂單總價,而後調用該函數。當創建函數fun_get_total時,實現規則:

若是訂單不存在,則顯示自定義錯誤消息「ORA-20001:Please check correct order no.」

---------------------------------------

create or replace function fun_get_total(v_order_id number)

return number

is

v_total number;

begin

select total into v_total from orders where order_id = v_order_id;

return v_total;


exception

when others then

raise_application_error(-20001,'Please check correct order no.');


end;


#調用1

select fun_get_total(2) from dual;

#調用2

select fun_get_total(2440) from dual;

----------------------------------------



3.創建過程pro_add_order,根據輸入的訂單號,預約日期,客戶號,交付日期和訂單總價,爲ORDERS表插入數據,而後調用過程。當創建過程pro_add_order,實現規則:

使用fun_valid_customer檢查客戶號是否正確;若是正確,則插入數據,不然顯示自定義錯誤消息「ORA-20001,Please check correct customer no.」

若是交付日期小於預約日期,則顯示錯誤信息「ORA-20002:交付日期必須在預約日期以後。」

若是輸入了已經存在的訂單號,則顯示自定義錯誤信息「ORA-20003:該訂單已經存在。」

---------------------------------------------

create or replace procedure pro_add_order(v_order_id number,v_order_date timestamp,

v_customer_id number,v_ship_date timestamp,v_total number)

is

test varchar2(20);


begin  

if fun_valid_customer(v_customer_id) then  

   if v_order_date > v_ship_date then

       raise_application_error(-20002,'ship_date must after the order_date');

   else

       insert into orders values(v_order_id,v_order_date,v_customer_id,v_ship_date,v_total);

   end if;

else

   raise_application_error(-20001,'Please check correct customer no.');

end if;


exception

   when dup_val_on_index then

   raise_application_error(-20003,'this order is already exist');

end;


#調用1

set serveroutput on;

call pro_add_order(2458,sysdate,138,sysdate+1,455);

#調用2

set serveroutput on;

call pro_add_order(24,sysdate,10,sysdate+1,455);

--------------------------------------------



4.創建過程pro_delete_order,根據輸入的訂單號取消特定訂單,而後調用該過程。實現規則:

(1).若是訂單不存在,則顯示錯誤信息「ORA-20001,請檢查並輸入正確的訂單號。」

--------------------------------------------

create or replace procedure pro_delete_order(v_order_id number)

is

begin

delete from orders where order_id=v_order_id;

if sql%notfound then

raise_application_error(-20001,'please input the correct order_id');

end if;

end;


set serveroutput on;

call pro_delete_order(2438);

-------------------------------------------

相關文章
相關標籤/搜索