mysql 儲存過程與函式
什麼是儲存過程和函式
儲存過程和函式是事先經過編譯儲存在資料庫中的一段 SQL語句的集合,呼叫儲存過程和函式可以簡化應用開發人員的很多工作,減少資料在資料庫和應用伺服器中的傳輸,對於提高資料處理的效率是有好處的。
儲存過程和函式的區別在於:函式必須有返回值,而儲存過程沒有,儲存過程的引數可以使用 IN,OUT,INOUT 型別,而函式的引數只能是IN 型別的。如果有函式從其他型別的資料庫遷移到 MySQL,那麼就有可能因此需要將函式改造成儲存過程。
儲存過程和函式的相關操作
在對儲存過程或函式進行相關操作時,需要首先確認使用者是否具有相關的許可權。例如,建立儲存過程或者函式需要 CREATE ROUTINE 許可權。修改或者刪除儲存過程或者函式需要 ALTER ROUTINE 許可權,執行儲存過程或者函式需要 EXECUTE 許可權。
建立 或 修改 儲存過程 或 函式
CREATE PROCEDURE sp_name ([proc_parameter[,....]]) [characteristic...] routine_body CREATE FUNCTION sp_name ([func_parameter[,....]]) RETURNS type [characteristic...] routine_body proc_parameter: [IN | OUT | INOUT ] param_name type func_parameter: param_name type type: Any valid MySQL data type characteristic: LANQUAGE 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 ALTER { PROCEDURE | FUNCTION } sp_name [characteristic...] characteristic: { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA } | SQL SECURITY { DEFINER | INVOKER} | COMMENT 'string'
呼叫過程的語法如下:
CALL sp_name([parameter[,....]]);
MySQL 的儲存過程和函式中允許包含 DDL 語句,也允許在儲存過程中執行 提交 (Commit,即確認之前的修改)或者 回滾 (Rollback, 即放棄之前的修改),但是儲存過程和函式中不允許執行 LOAD DATA INFILE 語句。此外,儲存過程和函式中允許呼叫其它的過程或者函式。
下面建立了一個新的過程 proc_adder:
mysql > DELIMITER $$ mysql > CREATE PROCEDURE `proc_adder`(IN a int, IN b int, OUT sum int) > BEGIN > DECLARE c int; > if a is null then > set a = 0; > end if; > if b is null then > set b = 0; > end if; > set sum = a + b; > END $$ Query Ok, 0 rows affected (0.00 sec) mysql > mysql > DELIMITER ;
上面的儲存過程比較簡單做的是一個加法運算。
通常我們在執行建立過程和函式之前,都會通過 【DELIMITER $$ 】命令將語句的結束符由 【;】 修改成其它符號,這裡使用的是 【$$】 ,這樣在過程和函式中的【;】 就不會被 MySQL 解釋成語句的結束而提示錯誤。在儲存過程或者函式建立完畢,通過 【DELIMITER ;】 再將結束符改回成 【;】。
執行結果:
mysql> set @b=5;
Query OK, 0 rows affected (0.00 sec)
mysql> call proc_adder(2,@b,@s);
Query OK, 0 rows affected (0.00 sec)
mysql> select @s as sum;
+------+
| sum |
+------+
| 7 |
+------+
1 row in set (0.00 sec)
mysql>
可以看到,呼叫儲存過程與直接執行SQL語句的效果是相同的,但是儲存過的好處在於處理邏輯都封裝在資料庫端,呼叫值不需要了解中間的處理邏輯,一旦處理邏輯發生變化,只需要修改儲存過程即可,而對呼叫者的程式完全沒有影響。
另外,和檢視的建立語法稍有不同,儲存過程和函式的 CREATE 語法不支援用 CRATE OR REPLACE 對儲存過程和函式進行修改,如果需要對已有的儲存過程和函式進行修改,需要執行ALTER 語法。
下面對 characteristic 特徵值的部分進行簡單說明:
- LANAUAGE SQL: 說明下面過程的 body 是使用SQL 語言編寫,這條是系統預設的,為今後 MySQL 會支援的除 SQL 外的其它語言支援的儲存過程而準備。
- [NOT] DETERMINISTIC: DETERMINISTIC 確定的,即每次輸入一樣,輸出也一樣的程式,NOT DETERMINISTIC 非確定的,預設是非確定的,當前這個特徵值還沒有被優化程式使用。
- { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }: 這些特徵值提供子程式使用資料的內在資訊,這些特徵值目前只是提供給伺服器,並沒有根據這些特徵值來約束過程實際使用資料的情況。CONTAINS SQL:表示子程式包含讀或寫資料的語句,NO SQL:表示子程式不包含 SQL 語句,READS SQL DATA: 表示子程式包含讀資料的語句,但不包含寫資料的語句,MODIFIES SQL DATA: 表示子程式包含寫資料的語句,如果這些特徵值沒有明確給定,預設使用的值是:CONTAINS SQL。
- SQL SECURITY { DEFINER | INVOKER}: 可以用來指定子程式該用建立子程式的許可來執行,還是使用呼叫的者的許可來執行,預設值是 DEFINER。
- COMMENT 'string' :儲存過程或是函式的註釋資訊。
刪除儲存過程或是函式
一次只能刪除一個儲存過程或者函式,刪除儲存過程或者函式要有該過程或者函式的 ALTER ROUTINE許可權,具體語法如下:
DROP { PROCEDURE | FOUNCTION }{IF EXISTS} sp_name;
檢視儲存過程或者函式
show { PROCEDURE | FOUNCTION } STATUS [like 'pattern'];
檢視儲存過程 proc_adder 的資訊:
mysql> SHOW PROCEDURE STATUS LIKE 'proc_adder'\G;
*************************** 1. row ***************************
Db: daicooper
Name: proc_adder
Type: PROCEDURE
Definer: [email protected]
Modified: 2018-12-06 14:07:52
Created: 2018-12-06 14:07:52
Security_type: DEFINER
Comment:
character_set_client: utf8mb4
collation_connection: utf8mb4_unicode_ci
Database Collation: utf8_general_ci
1 row in set (0.00 sec)
ERROR:
No query specified
mysql>
檢視儲存過程或者函式的定義
show CREATE { PROCEDURE | FOUNCTION } sp_name;
檢視儲存過程 proc_adder 的定義:
mysql> SHOW PROCEDURE STATUS LIKE 'proc_adder'\G;
*************************** 1. row ***************************
Db: daicooper
Name: proc_adder
Type: PROCEDURE
Definer: [email protected]
Modified: 2018-12-06 14:07:52
Created: 2018-12-06 14:07:52
Security_type: DEFINER
Comment:
character_set_client: utf8mb4
collation_connection: utf8mb4_unicode_ci
Database Collation: utf8_general_ci
1 row in set (0.00 sec)
ERROR:
No query specified
mysql>
通過 information_schema.routines 瞭解儲存過程和函式的資訊
檢視儲存過程 proc_adder 的定義:
mysql> select * from information_schema.routines where ROUTINE_NAME = 'proc_adder'\G;
*************************** 1. row ***************************
SPECIFIC_NAME: proc_adder
ROUTINE_CATALOG: def
ROUTINE_SCHEMA: daicooper
ROUTINE_NAME: proc_adder
ROUTINE_TYPE: PROCEDURE
DATA_TYPE:
CHARACTER_MAXIMUM_LENGTH: NULL
CHARACTER_OCTET_LENGTH: NULL
NUMERIC_PRECISION: NULL
NUMERIC_SCALE: NULL
DATETIME_PRECISION: NULL
CHARACTER_SET_NAME: NULL
COLLATION_NAME: NULL
DTD_IDENTIFIER: NULL
ROUTINE_BODY: SQL
ROUTINE_DEFINITION: BEGIN
#Routine body goes here...
DECLARE c int;
if a is null then set a = 0;
end if;
if b is null then set b = 0;
end if;
set sum = a + b;
END
EXTERNAL_NAME: NULL
EXTERNAL_LANGUAGE: NULL
PARAMETER_STYLE: SQL
IS_DETERMINISTIC: NO
SQL_DATA_ACCESS: CONTAINS SQL
SQL_PATH: NULL
SECURITY_TYPE: DEFINER
CREATED: 2018-12-06 14:07:52
LAST_ALTERED: 2018-12-06 14:07:52
SQL_MODE: STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION
ROUTINE_COMMENT:
DEFINER: [email protected]
CHARACTER_SET_CLIENT: utf8mb4
COLLATION_CONNECTION: utf8mb4_unicode_ci
DATABASE_COLLATION: utf8_general_ci
1 row in set (0.00 sec)
ERROR:
No query specified
mysql>
變數的使用
儲存過程和函式中可以使用變數,變數是不區分大小寫的。
1:變數的定義
通過 DECLARE 可以定義一個區域性變數,改變數的作用範文只能在 BEGIN....END 塊中,可以用在巢狀的塊中。
變數的定義必須寫在複合語句的開頭,並且在任何其它語句的前面,可以一次宣告多個相同型別的變數。如果需要,可以使用 DEFAULT 賦預設值。
定義一個變數的語法如下:
DELARE var_name [,....] type [DEFAULT value]
例如:定義一個 DATE 型別的變數 ,名稱是 last_month_start
DELARE last_month_start DATE;
1:變數的賦值
變數可以直接賦值,或者通過查詢賦值。直接賦值使用 SET ,可以賦值常量或者表示式,語法如下:
set last_month_start = DATE_SUB(CURRENT_DATE(), INTERVAL 1 MONTH);
也可以通過查詢將結果賦值給變數, 這要求查詢返回的結果必須只有一行。具體語法如下:
select col_name [,....] INTO var_name [,....] table_expr
通過查詢結果賦值給 last_month_start :
select DATE_SUB(CURRENT_DATE(), INTERVAL 1 MONTH) INTO last_month_start from DUAL
流程控制
可以使用 IF, CASE, LOOP, LEAVE, ITERATE, REPEAT,WHILE 語句進行流程控制。
1:IF 語句
if 實現條件判斷,滿足不同的條件執行不同的語句列表,具體語法:
IF search_condition THEN statement_list
[ELSEIF search_condition THEN statement_list] ....
[ELSE statement_list]
END IF
2:CASE 語句
CASE case_value
WHEN when_value THEN statement_list
[ WHEN when_value THEN statement_list ]...
[ ELSE statement_list]
END CASE
或者
CASE
WHEN search_condition THEN statement_list
[ WHEN search_condition THEN statement_list ]...
[ ELSE statement_list]
END CASE
比如 ,過程 proc_case 判斷輸入的引數值是 0 還是 1 或是 other:
CREATE PROCEDURE `proc_case`(IN type int)
BEGIN
#Routine body goes here...
DECLARE c varchar(500);
CASE type
WHEN 0 THEN
set c = 'param is 0';
WHEN 1 THEN
set c = 'param is 1';
ELSE
set c = 'param is others, not 0 or 1';
END CASE;
select c;
END
3:LOOP 語句
LOOP 語句實現簡單的迴圈,退出迴圈的條件需要使用其它的語句定義,通常可以使用 LEAVE 語句實現,具體語法:
[begin_label:] LOOP
statement_list
END LOOP [end_label]
如果不在 statement_list 中增加退出迴圈的語句,那麼 LOOP 語句可以用來實現簡單的死迴圈。
4:LEAVE 語句
用來從標註的流程構造中退出,通常和 BEGIN...END 或迴圈一起使用。
下面是一個 LOOP 和 LEAVE 一起使用的一個簡單的例子,迴圈一百次向 test 表中插入記錄,當插入 100 條記錄後退出迴圈。
CRATE PROCEDURE proc_Loop(OUT sum INT)
BEGIN
set @x = 0;
ins:LOOP
set @x = @x +1;
IF @x = 100 THEN
LEAVE ins;
END IF;
set sum = sum + @x;
END LOOP;
select sum;
END
執行結果
mysql> call proc_Loop(@sum);
+------+
| sum |
+------+
| 4950 |
+------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)