MySQL基礎值 儲存過程和函式
一、建立儲存過程和函式
什麼是建立儲存過程和函式?
就是將經常使用的一組SQL語句組合在一起,並將這些SQL語句當做一個整體儲存在MYSQL伺服器中。
建立儲存過程的語句是:CREATE PROCEDURE
建立儲存函式的語句是:CREATE FUNCTION
CALL語句負責呼叫儲存過程,只能輸出變數返回值。
1、建立儲存過程
語法:
CREATE PROCEDURE procedure_name( [ proc_parm[,...... ] ] ) [ characteristic...] routine_body
使用BEGIN......END來標誌SQL語句的開始和結束
proc_parm指定儲存過程的引數列表,列表形式如下:
[IN|OUT|INOUT] param_name type
其中in表示輸入引數,out表示輸出引數,inout表示既可以輸入也可以輸出;param_name表示引數名稱;type表示引數的型別
引數charateristic指定儲存過程的特性,有以下取值:
characteristic: LANGUAGE SQL | [NOT] DETERMINISTIC | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }| SQL SECURITY { DEFINER | INVOKER } | COMMENT 'string' routine_body: Valid SQL procedure statement or statements
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' :註釋資訊,可以用來描述儲存過程或函式
注意“:建立儲存過程,系統預設值是指定CONTAINS SQL,表示儲存過程中使用了SQL語句
建立儲存過程舉例子:
mysql> DELIMITER $$ mysql> CREATE PROCEDURE proc_id() -> COMMENT '檢視ID' -> BEGIN -> SELECT id FROM test1_5; -> END; -> $$ Query OK, 0 rows affected (0.00 sec) mysql> DELIMITER ; mysql> CALL proc_id; +------+ | id | +------+ | 1 | +------+ 1 row in set (0.00 sec) Query OK, 0 rows affected (0.00 sec)
2、建立儲存函式
CREATE FUNCTION func_name ( [ func_param[,....] ] ) [ characteristic...] routine_body
注意:具體建立函式時,函式名不能和已經存在的函式名重名,推薦使用func_xxxx來命名
func_parameter為儲存函式的引數列表,引數列表如下
[IN|OUT|INOUT]PARAM_NAMETYPE
其中,IN表示輸入引數,OUT表示輸出引數,INOUT表示既可以輸入也可以輸出;
param_name表示引數名稱;type表示引數型別,該型別可以是MYSQL資料庫中的任意型別
RETURNS TYPE語句表示函式返回資料的型別;characteristics:指定儲存函式的特性,取值與建立儲存過程時相同
例子:
mysql> DELIMITER $$ mysql> CREATE FUNCTION func_c_name(id int(4)) -> RETURNS char(4) -> COMMENT '查詢name' -> BEGIN -> RETURN(SELECT name from test1_5 where test1_5.id = id); -> END; -> $$ Query OK, 0 rows affected (0.00 sec) mysql> DELIMITER ; mysql> select func_c_name(1); #傳入引數數字1 ,即id=1 +----------------+ | func_c_name(1) | +----------------+ | chao | +----------------+ 1 row in set (0.00 sec)
3、變數的使用
變數可以在子程式中宣告並使用,這些變數的作用範圍是在BEGIN...END程式中
1、定義變數
在儲存過程中定義變數
DECLARE var_name[,varname]...date_type...[DEFAULT VALUE];
var_name為區域性變數的名稱。DEFAULT VALUE子句給變數提供一個預設值。值除了可以被宣告為一個常數外,還可以被指定為一個表示式。沒有DEFAULT的話,變數預設值為NULL
如果沒有DEFAULT子句,初始值為NULL
常見的用法是:
DECLARE MYPARAM INT
或者定義並賦值
DECLARE MYPARAM INT DEFAULT 100;
2、為變數賦值
定義變數之後,為變數賦值可以改變變數的預設值,MYSQL中使用SET語句為變數賦值
SET var_name=expr[,var_name=expr]...
比如:
SET MAPARAM = 100
為多個變數賦值
DECLARE var1,var2,var3 INT; SET var1=10,var2=20; SET var3=var1+var2;
注意:MySQL中declare和set都可以定義變數並賦值,但是兩者的作用範圍不一樣,declare定義的是區域性變數(只在儲存過程中的begin和end之間生效),而set定義的是全域性變數,在整個會話中都起作用(比如某個應用的一個連線過程中)
3、通過SELECT....INTO語句為變數賦值
語法:
SELECT col_name[,....] INTO var_name[,.....] FROM table_name WHERE condition
例如
DECLARE NAME CHAR(50); DECLARE id DECIMAL(8,2); SELECT id,NAME INTO id ,NAME FROM test1_5 WHERE id=1;
4、定義條件和處理程式
特定條件需要特定處理。這些條件可以聯絡到錯誤,以及子程式中的一般流程控制。定義條件是事先定義程式執行過程中遇到的問題,
處理程式定義了在遇到這些問題時候應當採取的處理方式,並且保證儲存過程或函式在遇到警告或錯誤時能繼續執行。
這樣可以增強儲存程式處理問題的能力,避免程式異常停止執行
1、定義條件
DECLARE condition_name CONDITION FOR[condition_type] [condition_type]: SQLSTATE[VALUE] sqlstate_value |mysql_error_code
condition_name:表示條件名稱
condition_type:表示條件的型別
sqlstate_value和mysql_error_code都可以表示mysql錯誤
sqlstate_value為長度5的字串錯誤程式碼
mysql_error_code為數值型別錯誤程式碼,例如:ERROR1142(42000)中,sqlstate_value的值是42000,
定義ERROR1148(42000)錯誤,名稱為command_not_allowed。 可以用兩種方法定義 //方法一:使用sqlstate_value DECLARE command_not_allowed CONDITION FOR SQLSTATE '42000' //方法二:使用mysql_error_code DECLARE command_not_allowed CONDITION FOR SQLSTATE 1148
2、定義處理程式
MySQL中可以使用DECLARE關鍵字來定義處理程式。其基本語法如下:
DECLARE handler_type HANDLER FOR condition_value[,...] sq_statement handler_type: CONTINUE | EXIT | UNDO condition_value: SQLSTATE [VALUE] sqlstate_value | condition_name | SQLWARNING | NOT FOUND | SQLEXCEPTION | mysql_error_code
其中,handler_type引數指明錯誤的處理方式,該引數有3個取值。這3個取值分別是CONTINUE、EXIT和UNDO。
CONTINUE表示遇到錯誤不進行處理,繼續向下執行;
EXIT表示遇到錯誤後馬上退出;
UNDO表示遇到錯誤後撤回之前的操作,MySQL中暫時還不支援這種處理方式。
因此,遇到錯誤時最好執行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值。
sq_statement表示一些儲存過程或函式的執行語句,如果遇到定義的錯誤時,需要執行的儲存過程或函式
例子:
//方法一:捕獲sqlstate_value DECLARE CONTINUE HANDLER FOR SQLSTATE '42000' SET @info='CAN NOT FIND'; //方法二:捕獲mysql_error_code DECLARE CONTINUE HANDLER FOR 1148SET @info='CAN NOT FIND'; //方法三:先定義條件,然後呼叫 DECLARE can_not_find CONDITION FOR 1146 ; DECLARE CONTINUE HANDLER FOR can_not_find SET @info='CAN NOT FIND'; //方法四:使用SQLWARNING DECLARE EXIT HANDLER FOR SQLWARNING SET @info='ERROR'; //方法五:使用NOT FOUND DECLARE EXIT HANDLER FOR NOT FOUND SET @info='CAN NOT FIND'; //方法六:使用SQLEXCEPTION DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @info='ERROR';
第一種方法是捕獲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"資訊
(1)當沒有條件處理時,執行結果的例子:
mysql> select * from test1_5; +----+------+ | id | name | +----+------+ | 1 | chao | +----+------+ 1 row in set (0.00 sec) mysql> desc test1_5; +-------+------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+------------+------+-----+---------+-------+ | id | tinyint(4) | NO | PRI | NULL | | | name | char(4) | YES | | NULL | | +-------+------------+------+-----+---------+-------+ 2 rows in set (0.00 sec) mysql> DELIMITER $$ mysql> CREATE PROCEDURE proc_insert() -> BEGIN -> SET @x=1; -> INSERT INTO test1_5 (id) values (2); -> SET @x=2; -> INSERT INTO test1_5 (id) values (1); -> SET @x=3; -> END; -> $$ Query OK, 0 rows affected (0.00 sec) mysql> DELIMITER ; mysql> CALL proc_insert(); ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY' mysql> select @x; +------+ | @x | +------+ | 2 | +------+ 1 row in set (0.00 sec)
我定義了一個表,id列是主鍵,然後定義變數x,看看執行到哪一步驟,
從上面的例子中我們可以看出來當插入重複值的時候,mysql退出執行,因此此時的x=2。沒有執行到x=3這一步
(2)當對主鍵重複的異常進行處理時的例子
mysql> DELIMITER $$ mysql> CREATE PROCEDURE proc_insert1() -> BEGIN -> DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2=1; -> SET @x=1; -> INSERT INTO test1_5(id) values(3); -> SET @x=2; -> INSERT INTO test1_5(id) values(3); -> SET @x=3; -> END; -> $$ Query OK, 0 rows affected (0.00 sec) mysql> delimiter ; mysql> call proc_insert1(); Query OK, 0 rows affected (0.00 sec) mysql> select @x,@x2;+------+------+ | @x | @x2 | +------+------+ | 3 | 1 | +------+------+ 1 row in set (0.00 sec)
呼叫條件處理的過程,遇到主鍵重的錯誤時,會按照定義的處理方式進行處理,我們例子中是CONTINUE操作,所以遇到錯誤就繼續執行下面的語句
5、游標的使用
作用:查詢語句可能查詢出多條記錄,在儲存過程和函式中使用游標來逐條讀取查詢結果的幾種記錄
1.宣告游標
MySQL中使用DECLARE關鍵字來宣告游標。其語法的基本形式如下:
DECLARE cursor_name CURSOR FOR select_statement ;
其中,cursor_name引數表示游標的名稱;select_statement引數表示SELECT語句的內容,返回一個用於建立游標的結果集
下面宣告一個名為cur_employee的游標。程式碼如下:
DECLARE cur_employee CURSOR FOR SELECT name, age FROM employee ;
上面的示例中,游標的名稱為cur_employee;SELECT語句部分是從employee表中查詢出name和age欄位的值。
2.開啟游標
MySQL中使用OPEN關鍵字來開啟游標。其語法的基本形式如下:
OPEN cursor_name ;
其中,cursor_name引數表示游標的名稱。
下面開啟一個名為cur_employee的游標,程式碼如下:
OPEN cur_employee ;
3.使用游標
MySQL中使用FETCH關鍵字來使用游標。其語法的基本形式如下:
FETCH cur_employee INTO var_name[,var_name…] ;
其中,cursor_name引數表示游標的名稱;var_name引數表示將游標中的SELECT語句查詢出來的資訊存入該引數中。var_name必須在宣告游標之前就定義好。
下面使用一個名為cur_employee的游標。將查詢出來的資料存入emp_name和emp_age這兩個變數中,程式碼如下:
FETCH cur_employee INTO emp_name, emp_age ;
上面的示例中,將游標cur_employee中SELECT語句查詢出來的資訊存入emp_name和emp_age中。emp_name和emp_age必須在前面已經定義。
4.關閉游標
MySQL中使用CLOSE關鍵字來關閉游標。其語法的基本形式如下:
CLOSE cursor_name ;
其中,cursor_name引數表示游標的名稱。
【示例14-11】 下面關閉一個名為cur_employee的游標。程式碼如下:
CLOSE cur_employee ;
上面的示例中,關閉了這個名稱為cur_employee的游標。關閉之後就不能使用FETCH來使用游標了。
例子:
mysql> delimiter $$ mysql> CREATE PROCEDURE proc_sun() -> BEGIN -> DECLARE i_grade tinyint;
-> DECLARE cur_grade CURSOR FOR select grade from test1_5; -> DECLARE EXIT HANDLER FOR NOT FOUND CLOSE cur_grade; -> set @x1=0; -> set @x2=0; -> open cur_grade; -> repeat -> FETCH cur_grade INTO i_grade; -> set @x1 = @x1 + i_grade; -> UNTIL 0 END REPEAT; -> close cur_grade; -> END; -> $$ Query OK, 0 rows affected (0.00 sec) mysql> delimiter ; mysql> call proc_sun(); Query OK, 0 rows affected (0.00 sec) mysql> select @x1; +------+ | @x1 | +------+ | 451 | +------+ 1 row in set (0.00 sec)
這個例子就相當於把表中的grade的列的值都相加起來。
注意:變數、條件、處理程式、游標都是通過DECLARE定義的,他們之間是有先後順序的變數和條件必須在最前面生命,然後才能使游標的宣告,最後才可以是處理程式的宣告
6、流程控制語句的使用
儲存過程和函式中可以使用流程控制來控制語句的執行。
MySQL中可以使用IF語句、CASE語句、LOOP語句、LEAVE語句、ITERATE語句、REPEAT語句和WHILE語句來進行流程控制。
每個流程中可能包含一個單獨語句,或者是使用BEGIN...END構造的複合語句,構造可以被巢狀
1、IF語句
IF語句用來進行條件判斷。根據是否滿足條件,將執行不同的語句。其語法的基本形式如下:
IF search_condition THEN statement_list [ELSEIF search_condition THEN statement_list] ... [ELSE statement_list] END IF
其中,search_condition引數表示條件判斷語句;statement_list引數表示不同條件的執行語句。
2、CASE語句
CASE語句也用來進行條件判斷,其可以實現比IF語句更復雜的條件判斷。CASE語句的基本形式如下:
CASE case_value WHEN when_value THEN statement_list [WHEN when_value THEN statement_list] ... [ELSE statement_list] END CASE
其中,case_value引數表示條件判斷的變數;
when_value引數表示變數的取值;
statement_list引數表示不同when_value值的執行語句。
程式碼可以是下面的形式:
CASE WHEN age=20 THEN SET @[email protected]+1; ELSE SET @[email protected]+1; END CASE ;
本示例中,如果age值為20,count1的值加1;否則count2的值加1。CASE語句都要使用END CASE結束。
3、LOOP語句
LOOP語句可以使某些特定的語句重複執行,實現一個簡單的迴圈。
但是LOOP語句本身沒有停止迴圈的語句,必須是遇到LEAVE語句等才能停止迴圈。
LOOP語句的語法的基本形式如下:
[begin_label:] LOOP statement_list END LOOP [end_label]
其中,begin_label引數和end_label引數分別表示迴圈開始和結束的標誌,這兩個標誌必須相同,而且都可以省略;
statement_list引數表示需要迴圈執行的語句。
下面是一個LOOP語句的示例。程式碼如下:
add_num: LOOP SET @[email protected]+1; END LOOP add_num ;
該示例迴圈執行count加1的操作。因為沒有跳出迴圈的語句,這個迴圈成了一個死迴圈。
LOOP迴圈都以END LOOP結束。
4、LEAVE語句
LEAVE語句主要用於跳出迴圈控制。其語法形式如下:
LEAVE label
其中,label引數表示迴圈的標誌。
下面是一個LEAVE語句的示例。程式碼如下:
add_num: LOOP SET @[email protected]+1; IF @count=100 THEN LEAVE add_num ; END LOOP add_num ;
該示例迴圈執行count加1的操作。當count的值等於100時,則LEAVE語句跳出迴圈。
1.使用者變數:以"@"開始,形式為"@變數名"
使用者變數跟mysql客戶端是繫結的,設定的變數,只對當前使用者使用的客戶端生效
2.全域性變數:定義時,以如下兩種形式出現,set GLOBAL 變數名 或者 set @@global.變數名
對所有客戶端生效。只有具有super許可權才可以設定全域性變數
例子:
mysql> desc test1_5;
+-------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------+------+-----+---------+-------+
| id | tinyint(4) | NO | PRI | NULL | |
| name | char(4) | YES | | NULL | |
| grade | tinyint(4) | YES | | NULL | |
+-------+------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
mysql> create procedure actor_insert() -> BEGIN -> set @x=0; -> ins:LOOP -> set @x = @x +1; -> IF @x = 100 then leave ins; -> END IF; -> insert into test1_5 values(@x,'haha',100); -> END LOOP ins; -> END; -> $$ Query OK, 0 rows affected (0.00 sec) mysql> delimiter ; mysql> call actor_insert(); Query OK, 0 rows affected (0.11 sec) mysql> select count(*) from test1_5; +----------+ | count(*) | +----------+ | 99 | +----------+ 1 row in set (0.00 sec)
5、ITERATE語句
ITERATE語句也是用來跳出迴圈的語句。但是,ITERATE語句是跳出本次迴圈,然後直接進入下一次迴圈。
ITERATE語句只可以出現在LOOP、REPEAT、WHILE語句內。
ITERATE語句的基本語法形式如下:
ITERATE label
6、REPEAT語句
REPEAT語句是有條件控制的迴圈語句。當滿足特定條件時,就會跳出迴圈語句。REPEAT語句的基本語法形式如下:
[begin_label:] REPEAT statement_list UNTIL search_condition END REPEAT [end_label]
其中,statement_list引數表示迴圈的執行語句;search_condition引數表示結束迴圈的條件,滿足該條件時迴圈結束。
下面是一個REPEAT語句的示例。程式碼如下:
REPEAT SET @[email protected]+1; UNTIL @count=100 END REPEAT ;
該示例迴圈執行count加1的操作,count值為100時結束迴圈。
REPEAT迴圈都用END REPEAT結束。
7、WHILE語句
WHILE語句也是有條件控制的迴圈語句。但WHILE語句和REPEAT語句是不一樣的。
WHILE語句是當滿足條件時,執行迴圈內的語句。
WHILE語句的基本語法形式如下:
[begin_label:] WHILE search_condition DO statement_list END WHILE [end_label]
其中,search_condition引數表示迴圈執行的條件,滿足該條件時迴圈執行;
statement_list引數表示迴圈的執行語句。
二、呼叫儲存過程和函式
儲存過程和儲存函式都是儲存在伺服器端的SQL語句的集合。儲存過程通過CALL語句來呼叫,儲存函式的使用方法與MYSQL內部函式的使用方法時一樣的。此外執行儲存過程和儲存函式需要擁有EXECUTE許可權。
1、呼叫儲存過程
CALL proc_name(parameter....)
其中parameter為儲存過程的引數。
mysql> delimiter $$ mysql> create procedure proc_select(IN ID INT,OUT name char) -> COMMENT '查詢薪水' -> BEGIN -> SELECT name from test1_5 where test1_5.id = ID; -> END; -> $$ Query OK, 0 rows affected (0.00 sec) mysql> delimiter ; mysql> call proc_select(10,@name);
2、呼叫儲存函式
mysql> delimiter $$ mysql> create function proc_select4(ID INT(4)) RETURNS INT(4) BEGIN RETURN(SELECT grade from test1_5 where test1_5.id=ID); END;$$ Query OK, 0 rows affected (0.00 sec) mysql> delimiter ; mysql> select proc_select4(1); +-----------------+ | proc_select4(1) | +-----------------+ | 100 | +-----------------+ 1 row in set (0.00 sec) mysql> select proc_select4(10); +------------------+ | proc_select4(10) | +------------------+ | 100 | +------------------+ 1 row in set (0.00 sec)
注意:where子句中的id前面必須帶上表名,即where test1_5.id =
3、檢視儲存過程和函式
SHOW {PROCEDURE|FUNCTION} STATUS {LIKE 'pattern'}
mysql> show procedure status like 'proc_select5'\G *************************** 1. row *************************** Db: test1 Name: proc_select5 Type: PROCEDURE Definer: root@localhost Modified: 2018-11-17 19:50:39 Created: 2018-11-17 19:50:39 Security_type: DEFINER Comment: 查詢name character_set_client: utf8 collation_connection: utf8_general_ci Database Collation: utf8_general_ci 1 row in set (0.00 sec)
檢視有哪些儲存過程直接:show procedure status\G
4、使用SHOW CREATE語句檢視儲存過程和函式的定義
SHOW CREATE {PROCEDURE|FUNCTION} proc_name
mysql> show create procedure proc_select6 \G *************************** 1. row *************************** Procedure: proc_select6 sql_mode: STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION Create Procedure: CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_select6`(IN ID INT,OUT grade INT) COMMENT '查詢name' BEGIN SELECT grade from test1_5 where test1_5.id=ID; END character_set_client: utf8 collation_connection: utf8_general_ci Database Collation: utf8_general_ci 1 row in set (0.00 sec)
5、修改儲存過程
ALTER {PROCEDURE | FUNCTION} sp_name [characteristic ...] characteristic: { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA } | SQL SECURITY { DEFINER | INVOKER } | COMMENT 'string'
6、刪除儲存過程和函式
DROP PROCEDURE pro_name
DROP FUNCTION func_name