MySQL 存儲過程 存儲函數 局部變量 遊標 概念示例

一個存儲過程是一個可編程的函數,它能夠在MySQL中建立並保存。它是由一些SQL語句和一些特殊的控制結構語句組成。
sql

當但願在不一樣的應用程序或平臺上執行相同的函數,或者封裝特定的功能時,存儲過程是一個很是有用的方式。數據庫中的存儲過程能夠看作是對編程中面向對象方法的模擬。數據庫


基本示例total_ordres編程


delimiter //

create procedure total_orders (out total float)
BEGIN
    select sum(amount)  into total from orders;
 END
 //
 
 delimiter ;

下面,讓咱們來逐行分析以上代碼:數組

第一行語句:ide

delimiter //

將語句末尾的分隔符從默認值(MySQL的默認分隔符爲 ; )改變爲// 。這樣作的目的是能夠在存儲過程當中使用分號分隔符,這樣MySQL就會將分號做爲存儲過程的代碼,從而不會當即執行。函數


接下來的語句:oop

create procedure total_ordres  (out total float)

定義一個存儲過程。該存儲過程的名稱是 total_orders ,它只有一個total參數,該參數是最後獲得結果的值。out表示該參數將被 傳出返回。這裏也能夠聲明爲 in ,表示該值必須傳入到存儲過程當中。或者 inout 表示該值必須傳入可是能夠被存儲過程修改fetch

float 表示參數的類型。this


若是但願使用多個參數,能夠提供一個由逗號間隔的參數列表,就像在PHP中同樣。 過程體必須封裝在BEGIN  END 語句中。spa


在聲明瞭過程後,能夠將分隔符從新設置爲分號:

delimiter ;


在過程聲明瞭以後,能夠用call 關鍵字調用該過程:

call  total_orders(@h);

這個語句將調用total_orders 過程,而且傳入一個用來保存結果的變量。

要查看該變量,以下語句所示:

select @h ;



函數

與建立過程的方法相似,還能夠建立一個函數。函數接收輸入參數而且返回一個惟一值。建立函數的基本語法幾乎相同。


delimiter //

create function add_tax (price float)  retuns float
return price*1.1 ;

delimiter ;

能夠看到,該示例使用了function關鍵字。而不是以前的procedure 關鍵字。此外,兩者還存在一些其餘差別。

參數沒必要經過IN 或 OUT來指定,由於在函數中全部參數都是IN 或輸入參數。在參數列表以後是returns float子句,它指定了返回值的類型。須要再次提到的是,該值能夠是任何有效的MySQL類型。


使用return 語句能夠返回一個值,就像PHP中所介紹的同樣。

請注意,這個示例中並無使用BEGIN 和 END語句。固然可使用它們,可是它們並非必需的。就像PHP中,若是一個語句塊只包含了一個語句,那麼該語句塊的開始和結束標記能夠省略。


調用函數 與 調用過程 存在一些差別。能夠調用內置函數的相同方式調用一個存儲函數。

select add_tax(100);

該語句的返回以下所示:

+-------------------+

| add_tax(100) |

+-------------------+

|                110 |

+-------------------+


在定義了存儲過程 和 存儲函數以後,可使用以下所示的語句來查看定義這些過程和函數的代碼:

show create procedure total_orders;
或者
show create function add_tax;


也可使用以下語句來刪除它們:

drop procedure total_orders;
或者
drop function add_tax;


存儲過程提供了使用控制結構、變量、DECLARE句柄(就像異常)的功能,以及遊標這個重要概念。

在接下來的文章中,咱們將簡單介紹這些概念。







局部變量

使用declare語句,能夠在BEGIN ... END語句塊中聲明局部變量。

例如,能夠對add_tax函數進行修改,使其使用一個局部變量來保存稅率。

示例:


delimiter // 

create function add_tax (price float) returns float
begin
    declare tax float default 0.10;
    return price*(1+tax);
end
//
delimiter ;

正如你看到的,咱們使用了declare關鍵字以及變量名稱和變量類型聲明瞭該局部變量。

default子句是可選的,它指定了該變量的初始值。如今能夠開始使用這個變量了。




遊標 和 控制結構

如今,讓咱們來分析一個更復雜的例子。在這個例子中,咱們將編寫一個存儲過程,該存儲過程將計算出最大金額的訂單,而且返回該訂單的orderid(很明顯,這能夠經過一個簡單的查詢就能夠得出結果,可是這個簡單的示例只是爲了說明如何使用遊標 和 控制結構)。

代碼以下:


delimiter //
create procedure largest_order (out largest_id  int)
begin
    declare this_id int;
    declare this_amount float;
    declare l_amount float default 0.0;
    declare l_id int;
    
    declare done int default 0;
    declare c1 cursor for select orderid, amount  from  orders;
    declare continue handle for sqlstate '02000'  set done = 1 ;
    
    open c1 ;
    repeat 
        fetch c1  into  this_id , this_amount ;
        if not done then 
            if this_amount > l_amount  then
                set l_amount = this_amount ;
                set l_id  = this_id ;
            end if;
         end if;
     until  done end  repear;
     close c1 ;
 
     set largest_id = l_id ;
     
 end
 //
 
 delimiter ;

以上代碼使用了控制語句(條件語句和循環語句)、遊標 和 聲明句柄。下面咱們逐行分析以上代碼。

this_id 和 this_amount變量保存了當前行orderid 和 amount值。 l_amount 和 l_id 變量用來存儲最大的訂單金額和與之對應的ID。

下一個變量被聲明爲done ,初始化爲0 。這個變量是循環標記。當遍歷了全部須要查看的行,能夠將該變量設置爲1 (True)。


declare continue handle for sqlstate '02000'  set done = 1 ;

是一個聲明句柄。它相似於存儲過程當中的一個異常。在continue句柄和 exit句柄中,也可使用它。就像以上代碼所顯示的,continue 句柄執行了指定的動做,而且繼續存儲過程的執行。 exit句柄將從最近的begin...end代碼中退出。

聲明句柄的下一個部分指定了句柄被調用的時間。在這個例子中,該句柄將在sqlstate '02000' 語句被執行時調用。 你可能會奇怪,這是個什麼意思,由於該語句很是神祕。這意味着,該句柄將在沒法再獲得記錄行以後被調用。咱們將逐行處理一個結果集,並且當遍歷了全部須要處理的記錄時,這個句柄纔會被調用。  也能夠指定等價的 FOR NOT FOUND語句。其餘選項還包括SQLWARNING 和 SQLEXCEPTION 。


接下來就是遊標。一個遊標相似於一個數組;它將從一個查詢中得到結果集,而且容許一次只處理一行。分析如下游標:

declare c1  cursor  for select  orderid, amount  from orders ;

這個遊標名稱爲 c1 。它只是將要保存內容的定義。該查詢還不會被執行。

接下來一行代碼:

open  c1 ;

真正運行這個查詢。要得到每個數據行,必須運行一個 fetch 語句。能夠在repeat 循環中完成此操做。

在這個例子中,循環語句以下所示:

repeat
....
until  done end  repeat ;

請注意,只有在循環語句塊的末尾纔會檢查循環條件。

存儲過程還支持while 循環,以下所示:

while  condition  do
....
end while

此外,還支持loop循環語句,以下所示:

loop 
....
end  loop

這些循環沒有內置的循環條件,可是能夠經過 leave 語句退出循環。

請注意,存儲過程不支持 for 循環。


繼續這個例子,一下代碼將得到一個數據行:

fetch  c1  into  thid_id,  this_amount ;

以上代碼將從遊標查詢中得到一個數據行。該查詢所得到的兩個屬性保存在兩個指定的局部變量中。


咱們能夠檢查一個數據航是否被得到,而後再將當前循環量與最大的存儲值進行比較,經過兩個 if 語句的方式,以下所示:

if  not  done  then 
    if  this_amount > l_amount  then
        set l_amount =  this_amount ;
        set l_id =  this_id ;
    end if ;
end  if ;

請注意,變量值將經過set 語句進行設置。

除了if ... then 語句外,存儲過程還支持 if ... then ... else 語句結構。以下所示:

if  condition  then 
    ...
    [elseif  condition then]
    ...
    [else]
    ...
end if

此外,也可使用case語句,以下形式所示:

case  value
    when  value  then  statement
    [when value then statement ... ]
    [else statement]
end case

回到這個例子,在循環語句末尾,將執行一些清除操做:

close c1 ; 
set  largest_id = l_id ;

close語句將關閉這個遊標。

最後,將全部計算出的最大值賦值給 OUT 參數。不能將該參數做爲臨時變量,只能用來保存最終值。

若是按照以上方式建立了這個存儲過程,能夠像調用其餘存儲過程同樣調用這個存儲過程:

call  largest_order (@k) ;
select @k ;

將得到相似於以下所示的輸出:

+--------+

|  @k   |

+--------+

|  3     |

+--------+

你能夠本身檢查計算結果是否正確。

相關文章
相關標籤/搜索