我的MYSQL學習心得(10) : 自定義儲存過程和函式

發表於2015-06-05

這一篇《我的MYSQL學習心得(十)》將會講解MYSQL的儲存過程和函式

MYSQL中建立儲存過程和函式分別使用CREATE PROCEDURE和CREATE FUNCTION

使用CALL語句來呼叫儲存過程,儲存過程也可以呼叫其他儲存過程

函式可以從語句外呼叫,能返回標量值


建立儲存過程

語法

proc_parameter指定儲存過程的引數列表,列表形式如下:

其中in表示輸入引數,out表示輸出引數,inout表示既可以輸入也可以輸出;param_name表示引數名稱;type表示引數的型別

該型別可以是MYSQL資料庫中的任意型別

有以下取值:

LANGUAGE SQL :說明routine_body部分是由SQL語句組成的,當前系統支援的語言為SQL,SQL是LANGUAGE特性的唯一值

[NOT] DETERMINISTIC :指明儲存過程執行的結果是否正確。DETERMINISTIC 表示結果是確定的。每次執行儲存過程時,相同的輸入會得到

相同的輸出。

[NOT] DETERMINISTIC 表示結果是不確定的,相同的輸入可能得到不同的輸出。如果沒有指定任意一個值,預設為[NOT] DETERMINISTIC

CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA:指明子程式使用SQL語句的限制。

CONTAINS SQL表明子程式包含SQL語句,但是不包含讀寫資料的語句;

NO SQL表明子程式不包含SQL語句;

READS SQL DATA:說明子程式包含讀資料的語句;

MODIFIES SQL DATA表明子程式包含寫資料的語句。

預設情況下,系統會指定為CONTAINS SQL

SQL SECURITY { DEFINER | INVOKER } :指明誰有許可權來執行。DEFINER 表示只有定義者才能執行

INVOKER 表示擁有許可權的呼叫者可以執行。預設情況下,系統指定為DEFINER

COMMENT ‘string’ :註釋資訊,可以用來描述儲存過程或函式

routine_body是SQL程式碼的內容,可以用BEGIN…END來表示SQL程式碼的開始和結束。

下面的語句建立一個查詢t1表全部資料的儲存過程

t3表是我們上一節建立的表

這裡的邏輯是

1、先判斷是否有Proc() 這個儲存過程,有就drop掉

2、建立Proc() 儲存過程

3、執行Proc() 儲存過程

注意:“DELIMITER //”語句的作用是將MYSQL的結束符設定為//,因為MYSQL預設的語句結束符為分號;,為了避免與儲存過程

中SQL語句結束符相沖突,需要使用DELIMITER 改變儲存過程的結束符,並以“END//”結束儲存過程。

儲存過程定義完畢之後再使用DELIMITER ;恢復預設結束符。DELIMITER 也可以指定其他符號為結束符!!!!!!!!!!!

如果你是這樣寫的話,就會得到如下錯誤,初學者很容易犯這個錯誤,包括本人

建立名為CountProc的儲存過程,程式碼如下:

上面程式碼的作用是建立一個獲取t3表記錄數的儲存過程,名稱是CountProc,

COUNT(*)計算後把結果放入引數param1中。

注意:當使用DELIMITER命令時,應該避免使用反斜槓(\)字元,因為反斜槓是MYSQL的轉義字元!!!


儲存函式

建立儲存函式,需要使用CREATE FUNCTION語句,基本語法如下:

CREATE FUNCTION為用來建立儲存函式的關鍵字;func_name表示儲存函式的名稱

func_parameter為儲存函式的引數列表,引數列表如下

其中,IN表示輸入引數,OUT表示輸出引數,INOUT表示既可以輸入也可以輸出;

param_name表示引數名稱;type表示引數型別,該型別可以是MYSQL資料庫中的任意型別

RETURNS TYPE語句表示函式返回資料的型別;characteristics:指定儲存函式的特性,取值與建立儲存過程時相同

建立儲存函式,名稱為NameByT,該函式返回SELECT語句的查詢結果,數值型別為字串型

注意:RETURNS CHAR(50)資料型別的時候,RETURNS 是有S的,而RETURN (SELECT NAME FROM t3 WHERE id=2)的時候RETURN是沒有S的

所以有時候大家可能覺得MYSQL很煩,誰不知是自己寫錯了

這裡有一個方法,就是利用SQLYOG的程式碼格式化功能,選中要格式化的程式碼,然後按F12,如果能格式化,證明你的程式碼沒有問題,如果不能格式化

證明你寫的程式碼有問題!!!

不加s的話就會出現語法錯誤了

呼叫函式

如果在儲存函式中的RETURN語句返回一個型別不同於函式的RETURNS子句中指定型別的值,返回值將被強制轉換為恰當的型別。

例如,如果一個函式返回一個SET或ENUM值,但是RETURN語句返回一個整數,對於SET成員集的相應ENUM成員,從函式返回的值

是字串。

指定引數為IN、OUT、INOUT只對PROCEDURE是合法的。

(FUNCTION中總是預設是IN引數)RETURNS子句對FUNCTION做指定,對函式而言這是強制的。

他用來指定函式的返回型別,而且函式體必須包含一個RETURN value語句


變數的使用

變數可以在子程式中宣告並使用,這些變數的作用範圍是在BEGIN…END程式中

1、定義變數

在儲存過程中定義變數

var_name為區域性變數的名稱。DEFAULT VALUE子句給變數提供一個預設值。值除了可以被宣告為一個常數外,還可以被指定為一個表示式。

如果沒有DEFAULT子句,初始值為NULL

2、為變數賦值

定義變數之後,為變數賦值可以改變變數的預設值,MYSQL中使用SET語句為變數賦值

在儲存過程中的SET語句是一般SET語句的擴充套件版本。

被SET的變數可能是子程式內的變數,或者是全域性伺服器變數,如系統變數或者使用者變數

他執行SET a=x,b=y,….

宣告3個變數,分別為var1,var2和var3

MYSQL中還可以通過SELECT…INTO為一個或多個變數賦值


定義條件和處理程式

特定條件需要特定處理。這些條件可以聯絡到錯誤,以及子程式中的一般流程控制。定義條件是事先定義程式執行過程中遇到的問題,

處理程式定義了在遇到這些問題時候應當採取的處理方式,並且保證儲存過程或函式在遇到警告或錯誤時能繼續執行。

這樣可以增強儲存程式處理問題的能力,避免程式異常停止執行

1、定義條件

condition_name:表示條件名稱

condition_type:表示條件的型別

sqlstate_value和mysql_error_code都可以表示mysql錯誤

sqlstate_value為長度5的字串錯誤程式碼

mysql_error_code為數值型別錯誤程式碼,例如:ERROR1142(42000)中,sqlstate_value的值是42000,

mysql_error_code的值是1142

這個語句指定需要特殊處理條件。他將一個名字和指定的錯誤條件關聯起來。

這個名字隨後被用在定義處理程式的DECLARE HANDLER語句中

定義ERROR1148(42000)錯誤,名稱為command_not_allowed。

可以用兩種方法定義

2.定義處理程式

MySQL中可以使用DECLARE關鍵字來定義處理程式。其基本語法如下:

其中,handler_type引數指明錯誤的處理方式,該引數有3個取值。這3個取值分別是CONTINUE、EXIT和UNDO。

CONTINUE表示遇到錯誤不進行處理,繼續向下執行;

EXIT表示遇到錯誤後馬上退出;

UNDO表示遇到錯誤後撤回之前的操作,MySQL中暫時還不支援這種處理方式。

注意:通常情況下,執行過程中遇到錯誤應該立刻停止執行下面的語句,並且撤回前面的操作。

但是,MySQL中現在還不能支援UNDO操作。

因此,遇到錯誤時最好執行EXIT操作。如果事先能夠預測錯誤型別,並且進行相應的處理,那麼可以執行CONTINUE操作。

condition_value引數指明錯誤型別,該引數有6個取值。

sqlstate_value和mysql_error_code與條件定義中的是同一個意思。

condition_name是DECLARE定義的條件名稱。

SQLWARNING表示所有以01開頭的sqlstate_value值。

NOT FOUND表示所有以02開頭的sqlstate_value值。

SQLEXCEPTION表示所有沒有被SQLWARNING或NOT FOUND捕獲的sqlstate_value值。

sp_statement表示一些儲存過程或函式的執行語句。

下面是定義處理程式的幾種方式。程式碼如下:

上述程式碼是6種定義處理程式的方法。

第一種方法是捕獲sqlstate_value值。如果遇到sqlstate_value值為42000,執行CONTINUE操作,並且輸出”CAN NOT FIND”資訊。

第二種方法是捕獲mysql_error_code值。如果遇到mysql_error_code值為1148,執行CONTINUE操作,並且輸出”CAN NOT FIND”資訊。

第三種方法是先定義條件,然後再呼叫條件。這裡先定義can_not_find條件,遇到1148錯誤就執行CONTINUE操作。

第四種方法是使用SQLWARNING。SQLWARNING捕獲所有以01開頭的sqlstate_value值,然後執行EXIT操作,並且輸出”ERROR”資訊。

第五種方法是使用NOT FOUND。NOT FOUND捕獲所有以02開頭的sqlstate_value值,然後執行EXIT操作,並且輸出”CAN NOT FIND”資訊。

第六種方法是使用SQLEXCEPTION。SQLEXCEPTION捕獲所有沒有被SQLWARNING或NOT FOUND捕獲的sqlstate_value值,然後執行EXIT操作,並且輸出”ERROR”資訊

定義條件和處理程式

@X是一個使用者變數,執行結果@X等於3,這表明MYSQL執行到程式的末尾。

如果DECLARE CONTINUE HANDLER FOR SQLSTATE ‘23000’ SET @X2=1;,這一行不存在

第二個INSERT因PRIMARY KEY約束而失敗之後,MYSQL可能已經採取EXIT策略,並且SELECT @X可能已經返回2

注意:@X表示使用者變數,使用SET語句為其賦值,使用者變數與連線有關,一個客戶端定義的變數不能被其他客戶端所使用

即有作用域的,該客戶端退出時,客戶端連線的所有變數將自動釋放

這裡的變數跟SQLSERVER沒有什麼區別,都是用來儲存臨時值的

MYSQL這裡的條件和預定義程式其實跟SQLSERVER的自定義錯誤是一樣的


游標

MYSQL裡叫游標,SQLSERVER裡叫遊標,實際上一樣的

查詢語句可能查詢出多條記錄,在儲存過程和函式中使用游標來逐條讀取查詢結果集中的記錄。

游標的使用包括宣告游標、開啟游標、使用游標和關閉游標。游標必須宣告在處理程式之前,並且宣告在變數和條件之後。

1.宣告游標

MySQL中使用DECLARE關鍵字來宣告游標。其語法的基本形式如下:

其中,cursor_name參數列示游標的名稱;select_statement參數列示SELECT語句的內容,返回一個用於建立游標的結果集

下面宣告一個名為cur_employee的游標。程式碼如下:

上面的示例中,游標的名稱為cur_employee;SELECT語句部分是從employee表中查詢出name和age欄位的值。

2.開啟游標

MySQL中使用OPEN關鍵字來開啟游標。其語法的基本形式如下:

其中,cursor_name參數列示游標的名稱。

下面開啟一個名為cur_employee的游標,程式碼如下:

3.使用游標

MySQL中使用FETCH關鍵字來使用游標。其語法的基本形式如下:

其中,cursor_name參數列示游標的名稱;var_name參數列示將游標中的SELECT語句查詢出來的資訊存入該引數中。var_name必須在宣告游標之前就定義好。

下面使用一個名為cur_employee的游標。將查詢出來的資料存入emp_name和emp_age這兩個變數中,程式碼如下:

上面的示例中,將游標cur_employee中SELECT語句查詢出來的資訊存入emp_name和emp_age中。emp_name和emp_age必須在前面已經定義。

4.關閉游標

MySQL中使用CLOSE關鍵字來關閉游標。其語法的基本形式如下:

其中,cursor_name參數列示游標的名稱。

【示例14-11】 下面關閉一個名為cur_employee的游標。程式碼如下:

上面的示例中,關閉了這個名稱為cur_employee的游標。關閉之後就不能使用FETCH來使用游標了。

注意:MYSQL中,游標只能在儲存過程和函式中使用!!

到目前為止儲存函式,儲存過程、變數、條件、預定義程式、游標跟SQLSERVER差不多,只不過語法不同,結構不同

剛開始的時候會有不適應


流程控制的使用

儲存過程和函式中可以使用流程控制來控制語句的執行。

MySQL中可以使用IF語句、CASE語句、LOOP語句、LEAVE語句、ITERATE語句、REPEAT語句和WHILE語句來進行流程控制。

每個流程中可能包含一個單獨語句,或者是使用BEGIN…END構造的複合語句,構造可以被巢狀

1.IF語句

IF語句用來進行條件判斷。根據是否滿足條件,將執行不同的語句。其語法的基本形式如下:

其中,search_condition參數列示條件判斷語句;statement_list參數列示不同條件的執行語句。

注意:MYSQL還有一個IF()函式,他不同於這裡描述的IF語句

下面是一個IF語句的示例。程式碼如下:

該示例根據age與20的大小關係來執行不同的SET語句。

如果age值大於20,那麼將count1的值加1;如果age值等於20,那麼將count2的值加1;

其他情況將count3的值加1。IF語句都需要使用END IF來結束。

2.CASE語句

CASE語句也用來進行條件判斷,其可以實現比IF語句更復雜的條件判斷。CASE語句的基本形式如下:

其中,case_value參數列示條件判斷的變數;

when_value參數列示變數的取值;

statement_list參數列示不同when_value值的執行語句。

CASE語句還有另一種形式。該形式的語法如下:

其中,search_condition參數列示條件判斷語句;

statement_list參數列示不同條件的執行語句。

下面是一個CASE語句的示例。程式碼如下:

程式碼也可以是下面的形式:

本示例中,如果age值為20,count1的值加1;否則count2的值加1。CASE語句都要使用END CASE結束。

注意:這裡的CASE語句和“控制流程函式”裡描述的SQL CASE表示式的CASE語句有輕微不同。這裡的CASE語句不能有ELSE NULL子句

並且用END CASE替代END來終止!!

3.LOOP語句

LOOP語句可以使某些特定的語句重複執行,實現一個簡單的迴圈。

但是LOOP語句本身沒有停止迴圈的語句,必須是遇到LEAVE語句等才能停止迴圈。

LOOP語句的語法的基本形式如下:

其中,begin_label引數和end_label引數分別表示迴圈開始和結束的標誌,這兩個標誌必須相同,而且都可以省略;

statement_list參數列示需要迴圈執行的語句。

下面是一個LOOP語句的示例。程式碼如下:

該示例迴圈執行count加1的操作。因為沒有跳出迴圈的語句,這個迴圈成了一個死迴圈。

LOOP迴圈都以END LOOP結束。

4.LEAVE語句

LEAVE語句主要用於跳出迴圈控制。其語法形式如下:

其中,label參數列示迴圈的標誌。

下面是一個LEAVE語句的示例。程式碼如下:

該示例迴圈執行count加1的操作。當count的值等於100時,則LEAVE語句跳出迴圈。

5.ITERATE語句

ITERATE語句也是用來跳出迴圈的語句。但是,ITERATE語句是跳出本次迴圈,然後直接進入下一次迴圈。

ITERATE語句只可以出現在LOOP、REPEAT、WHILE語句內。

ITERATE語句的基本語法形式如下:

其中,label參數列示迴圈的標誌。

下面是一個ITERATE語句的示例。程式碼如下:

該示例迴圈執行count加1的操作,count值為100時結束迴圈。如果count的值能夠整除3,則跳出本次迴圈,不再執行下面的SELECT語句。

說明:LEAVE語句和ITERATE語句都用來跳出迴圈語句,但兩者的功能是不一樣的。

LEAVE語句是跳出整個迴圈,然後執行迴圈後面的程式。而ITERATE語句是跳出本次迴圈,然後進入下一次迴圈。

使用這兩個語句時一定要區分清楚。

6.REPEAT語句

REPEAT語句是有條件控制的迴圈語句。當滿足特定條件時,就會跳出迴圈語句。REPEAT語句的基本語法形式如下:

其中,statement_list參數列示迴圈的執行語句;search_condition參數列示結束迴圈的條件,滿足該條件時迴圈結束。

下面是一個ITERATE語句的示例。程式碼如下:

該示例迴圈執行count加1的操作,count值為100時結束迴圈。

REPEAT迴圈都用END REPEAT結束。

7.WHILE語句

WHILE語句也是有條件控制的迴圈語句。但WHILE語句和REPEAT語句是不一樣的。

WHILE語句是當滿足條件時,執行迴圈內的語句。

WHILE語句的基本語法形式如下:

其中,search_condition參數列示迴圈執行的條件,滿足該條件時迴圈執行;

statement_list參數列示迴圈的執行語句。

下面是一個ITERATE語句的示例。程式碼如下:

該示例迴圈執行count加1的操作,count值小於100時執行迴圈。

如果count值等於100了,則跳出迴圈。WHILE迴圈需要使用END WHILE來結束。


呼叫儲存過程和函式

儲存過程和儲存函式都是儲存在伺服器端的SQL語句的集合,要使用這些已經定義好的儲存過程和儲存函式就必須要通過呼叫的方式來實現

儲存過程是通過CALL語句來呼叫的。而儲存函式的使用方法與MySQL內部函式的使用方法是一樣的

執行儲存過程和儲存函式需要擁有EXECUTE許可權

EXECUTE許可權的資訊儲存在information_schema資料庫下面的USER_PRIVILEGES表中

呼叫儲存過程

MySQL中使用CALL語句來呼叫儲存過程。呼叫儲存過程後,資料庫系統將執行儲存過程中的語句。

然後,將結果返回給輸出值。

CALL語句的基本語法形式如下:

其中,sp_name是儲存過程的名稱;parameter是指儲存過程的引數。

呼叫儲存函式

在MySQL中,儲存函式的使用方法與MySQL內部函式的使用方法是一樣的。

換言之,使用者自己定義的儲存函式與MySQL內部函式是一個性質的。

區別在於,儲存函式是使用者自己定義的,而內部函式是MySQL的開發者定義的。

下面定義一個儲存函式,然後呼叫這個儲存函式。

程式碼執行如下:

上述儲存函式的作用是根據輸入的id值到t3表中查詢記錄。

查詢出id欄位的值等於id的記錄。然後將該記錄的name欄位的值返回。


檢視儲存過程和函式

儲存過程和函式建立以後,可以檢視儲存過程和函式的狀態和定義。

通過SHOW STATUS語句來檢視儲存過程和函式的狀態,也可以通過SHOW CREATE語句來檢視儲存過程和函式的定義。

通過查詢information_schema資料庫下的Routines表來檢視儲存過程和函式的資訊

1、SHOW STATUS語句檢視儲存過程和函式的狀態

MySQL中可以通過SHOW STATUS語句檢視儲存過程和函式的狀態。其基本語法形式如下:

其中,PROCEDURE參數列示查詢儲存過程;FUNCTION參數列示查詢儲存函式;

LIKE ‘ pattern ‘引數用來匹配儲存過程或函式的名稱。

下面查詢名為name_from_t3的函式的狀態。程式碼執行如下:

查詢結果顯示了函式的建立時間、修改時間和字符集等資訊。

注意:SHOW STATUS語句只能檢視儲存過程或函式是操作哪一個資料庫、儲存過程或函式的名稱、型別、誰定義的、建立和修改時間、字元編碼等資訊。

但是,這個語句不能查詢儲存過程或函式的具體定義。如果需要檢視詳細定義,需要使用SHOW CREATE語句

2、SHOW CREATE語句檢視儲存過程和函式的定義

MySQL中可以通過SHOW CREATE語句檢視儲存過程和函式的狀態。其基本語法形式如下:

其中,PROCEDURE參數列示查詢儲存過程;

FUNCTION參數列示查詢儲存函式;

sp_name參數列示儲存過程或函式的名稱

下面查詢名為name_from_t3的函式的定義。程式碼執行如下

3、從information_schema.Routines表中檢視儲存過程和函式的資訊

儲存過程和函式的資訊儲存在information_schema資料庫下的Routines表中。可以通過查詢該表的記錄來查詢儲存過程和函式的資訊。

其基本語法形式如下:

其中,ROUTINE_NAME欄位中儲存的是儲存過程和函式的名稱;

sp_name參數列示儲存過程或函式的名稱。

下面從Routines表中查詢名為name_from_t3函式的資訊。

程式碼執行如下:

查詢結果顯示name_from_t3的詳細資訊。

注意:在information_schema資料庫下的Routines表中,儲存著所有儲存過程和函式的定義。

如果使用SELECT語句查詢Routines表中的儲存過程和函式的定義時,一定要使用ROUTINE_NAME欄位指定儲存過程或函式的名稱。

否則,將查詢出所有的儲存過程或函式的定義。


修改儲存過程和函式

修改儲存過程和函式是指修改已經定義好的儲存過程和函式。

MySQL中通過ALTER PROCEDURE語句來修改儲存過程。

通過ALTER FUNCTION語句來修改儲存函式。

MySQL中修改儲存過程和函式的語句的語法形式如下:

其中,sp_name參數列示儲存過程或函式的名稱;

characteristic引數指定儲存函式的特性。

CONTAINS SQL表示子程式包含SQL語句,但不包含讀或寫資料的語句;

NO SQL表示子程式中不包含SQL語句;

READS SQL DATA表示子程式中包含讀資料的語句;

MODIFIES SQL DATA表示子程式中包含寫資料的語句。

SQL SECURITY { DEFINER | INVOKER }指明誰有許可權來執行。

DEFINER表示只有定義者自己才能夠執行;

INVOKER表示呼叫者可以執行。

COMMENT ‘string’是註釋資訊。

說明:修改儲存過程使用ALTER PROCEDURE語句,修改儲存函式使用ALTER FUNCTION語句。

但是,這兩個語句的結構是一樣的,語句中的所有參賽都是一樣的。

而且,它們與建立儲存過程或函式的語句中的引數也是基本一樣的。

修改儲存過程和函式,只能修改他們的許可權,目前MYSQL還不提供對已存在的儲存過程和函式程式碼的修改

如果要修改,只能通過先DROP掉,然後重新建立新的儲存過程和函式來實現

在SQLYOG裡選中選中函式,然後右鍵ALTER FUNCTION的時候,也是這樣,先DROP掉,然後重新建立新的函式來實現


刪除儲存過程和函式

刪除儲存過程和函式指刪除資料庫中已經存在的儲存過程和函式。

MySQL中使用DROP PROCEDURE語句來刪除儲存過程。通過DROP FUNCTION語句來刪除儲存函式。

其中,sp_name參數列示儲存過程或函式的名稱

下面刪除儲存過程Proc和儲存函式name_from_t3。刪除儲存過程的程式碼如下:

刪除儲存函式name_from_employee的程式碼如下:

刪除完畢之後,查詢information_schema.Routines來確認上面的刪除是否成功


總結

TIPS:

儲存過程裡面是可以呼叫其他儲存過程的,使用CALL語句呼叫其他儲存過程就可以了

儲存過程引數列表裡的引數名儘量不要和資料庫中表的欄位名一樣,否則有可能出錯

儲存過程的引數可以使用中文,在定義儲存過程的時候加上character set gbk就可以了

例如

如有不對的地方,歡迎大家拍磚o(∩_∩)o 

2014-6-22補充

DECLARE 語句要寫在儲存過程裡面,否則會報錯!!

相關文章