CREATE
[DEFINER = user]
PROCEDURE [IF NOT EXISTS] sp_name ([proc_parameter[,...]])
[characteristic ...] routine_body
CREATE
[DEFINER = user]
FUNCTION [IF NOT EXISTS] 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: {
COMMENT 'string'
| LANGUAGE SQL
| [NOT] DETERMINISTIC
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
}
routine_body:
SQL routine
這些陳述式用於建立預存常式(預存程序或函數)。也就是說,指定的常式會被伺服器所知。預設情況下,預存常式會與預設資料庫關聯。若要將常式明確與特定資料庫關聯,請在建立時將名稱指定為 db_name.sp_name
。
CREATE FUNCTION
陳述式也在 MySQL 中用於支援可載入函數。請參閱第 15.7.4.1 節,「用於可載入函數的 CREATE FUNCTION 陳述式」。可載入函數可以視為外部預存函數。預存函數與可載入函數共用其命名空間。請參閱第 11.2.5 節,「函數名稱剖析和解析」,以了解描述伺服器如何解譯對不同類型函數的參考的規則。
若要叫用預存程序,請使用 CALL
陳述式(請參閱第 15.2.1 節,「CALL 陳述式」)。若要叫用預存函數,請在運算式中參考它。函數會在運算式評估期間傳回值。
CREATE PROCEDURE
和 CREATE FUNCTION
需要 CREATE ROUTINE
權限。如果存在 DEFINER
子句,則所需的權限取決於 user
值,如第 27.6 節,「預存物件存取控制」中所述。如果啟用二進位記錄,則CREATE FUNCTION
可能需要 SUPER
權限,如第 27.7 節,「預存程式二進位記錄」中所述。
預設情況下,MySQL 會自動將 ALTER ROUTINE
和 EXECUTE
權限授予常式的建立者。此行為可以透過停用 automatic_sp_privileges
系統變數來變更。請參閱第 27.2.2 節,「預存常式和 MySQL 權限」。
DEFINER
和 SQL SECURITY
子句指定在常式執行時檢查存取權限時要使用的安全性內容,如此節稍後所述。
如果常式名稱與內建 SQL 函數的名稱相同,除非您在定義常式或稍後叫用時,在名稱和後面的括號之間使用空格,否則會發生語法錯誤。因此,請避免為您自己的預存常式使用現有 SQL 函數的名稱。
IGNORE_SPACE
SQL 模式適用於內建函數,但不適用於預存常式。無論是否啟用 IGNORE_SPACE
,都允許在預存常式名稱後方加上空格。
如果已經存在具有相同名稱的常式,則 IF NOT EXISTS
可防止發生錯誤。此選項支援 CREATE FUNCTION
和 CREATE PROCEDURE
。
如果已經存在具有相同名稱的內建函數,嘗試使用 CREATE FUNCTION ... IF NOT EXISTS
建立預存函數會成功,並發出警告,指出它與原生函數的名稱相同;這與在不指定 IF NOT EXISTS
的情況下執行相同的 CREATE FUNCTION
陳述式沒有區別。
如果已經存在具有相同名稱的可載入函數,嘗試使用 IF NOT EXISTS
建立預存函數會成功,並發出警告。這與不指定 IF NOT EXISTS
的情況相同。
請參閱函數名稱解析,以了解更多資訊。
括在括號內的參數清單必須始終存在。如果沒有參數,則應使用空的參數清單 ()
。參數名稱不區分大小寫。
每個參數預設都是 IN
參數。若要為參數指定其他值,請在參數名稱之前使用關鍵字 OUT
或 INOUT
。
將參數指定為 IN
、OUT
或 INOUT
僅對 PROCEDURE
有效。對於 FUNCTION
,參數始終視為 IN
參數。
IN
參數將值傳遞到程序中。程序可能會修改該值,但當程序傳回時,該修改對呼叫者不可見。OUT
參數將值從程序傳遞回呼叫者。其初始值在程序中為 NULL
,且當程序傳回時,其值對呼叫者可見。INOUT
參數由呼叫者初始化,可以由程序修改,且當程序傳回時,程序所做的任何變更對呼叫者可見。
對於每個 OUT
或 INOUT
參數,請在叫用程序的 CALL
陳述式中傳遞使用者定義的變數,以便您在程序傳回時取得其值。如果您是從另一個預存程序或函數中呼叫程序,您也可以將常式參數或本機常式變數作為 OUT
或 INOUT
參數傳遞。如果您是從觸發程序中呼叫程序,您也可以傳遞 NEW.
作為 col_name
OUT
或 INOUT
參數。
如需未處理的條件對程序參數的影響的相關資訊,請參閱第 15.6.7.8 節,「條件處理和 OUT 或 INOUT 參數」。
無法在常式內準備的陳述式中參考常式參數;請參閱第 27.8 節,「預存程式的限制」。
下列範例顯示一個簡單的預存程序,該程序在給定國家/地區代碼的情況下,會計算 world
資料庫的 city
表格中出現的該國家/地區的城市數量。國家/地區代碼是使用 IN
參數傳遞,而城市計數是使用 OUT
參數傳回。
mysql> delimiter //
mysql> CREATE PROCEDURE citycount (IN country CHAR(3), OUT cities INT)
BEGIN
SELECT COUNT(*) INTO cities FROM world.city
WHERE CountryCode = country;
END//
Query OK, 0 rows affected (0.01 sec)
mysql> delimiter ;
mysql> CALL citycount('JPN', @cities); -- cities in Japan
Query OK, 1 row affected (0.00 sec)
mysql> SELECT @cities;
+---------+
| @cities |
+---------+
| 248 |
+---------+
1 row in set (0.00 sec)
mysql> CALL citycount('FRA', @cities); -- cities in France
Query OK, 1 row affected (0.00 sec)
mysql> SELECT @cities;
+---------+
| @cities |
+---------+
| 40 |
+---------+
1 row in set (0.00 sec)
此範例使用 mysql 用戶端的 delimiter
命令,將陳述式分隔符號從 ;
變更為 //
,同時定義程序。這讓程序主體中使用的 ;
分隔符號可以傳遞至伺服器,而不會由 mysql 本身解譯。請參閱第 27.1 節,「定義預存程式」。
RETURNS
子句只能針對 FUNCTION
指定,對於 FUNCTION
而言,此子句是強制性的。它會指示函數的傳回類型,且函數主體必須包含 RETURN
陳述式。如果 value
RETURN
陳述式傳回不同類型的值,則會將該值強制轉換為適當的類型。例如,如果函數在 RETURNS
子句中指定 ENUM
或 SET
值,但 RETURN
陳述式傳回整數,則從函數傳回的值會是 ENUM
成員或 SET
成員集合的對應字串。
下列範例函數會採用參數、使用 SQL 函數執行運算,並傳回結果。在此案例中,不需要使用 delimiter
,因為函數定義不包含任何內部 ;
陳述式分隔符號。
mysql> CREATE FUNCTION hello (s CHAR(20))
mysql> RETURNS CHAR(50) DETERMINISTIC
RETURN CONCAT('Hello, ',s,'!');
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT hello('world');
+----------------+
| hello('world') |
+----------------+
| Hello, world! |
+----------------+
1 row in set (0.00 sec)
可以宣告參數類型和函數傳回類型以使用任何有效的資料類型。如果前面加上 CHARACTER SET
規格,則可以使用 COLLATE
屬性。
routine_body
包含有效的 SQL 常式陳述式。這可以是簡單的陳述式,例如 SELECT
或 INSERT
,或是使用 BEGIN
和 END
撰寫的複合陳述式。複合陳述式可以包含宣告、迴圈和其他控制結構陳述式。這些陳述式的語法在第 15.6 節,「複合陳述式語法」中描述。實際上,預存函數傾向於使用複合陳述式,除非主體由單一的 RETURN
陳述式組成。
MySQL 允許常式包含 DDL 陳述式,例如 CREATE
和 DROP
。MySQL 也允許預存程序(但不能是預存函數)包含 SQL 交易陳述式,例如 COMMIT
。預存函數不得包含執行明確或隱含認可或回復的陳述式。SQL 標準並未要求支援這些陳述式,該標準指出每個 DBMS 供應商都可以決定是否允許這些陳述式。
會返回結果集的陳述式可以在預存程序中使用,但不能在預存函數中使用。此限制包括不帶有 INTO
子句的 var_list
SELECT
陳述式,以及其他陳述式,例如 SHOW
、EXPLAIN
和 CHECK TABLE
。對於在函數定義時可以確定會返回結果集的陳述式,會發生 Not allowed to return a result set from a function
錯誤(ER_SP_NO_RETSET
)。對於僅在運行時才能確定會返回結果集的陳述式,會發生 PROCEDURE %s can't return a result set in the given context
錯誤(ER_SP_BADSELECT
)。
不允許在預存常式中使用 USE
陳述式。當常式被調用時,會執行隱式的 USE
(並且在常式終止時還原)。這會導致常式在執行時擁有指定的預設資料庫。對預設資料庫以外的其他資料庫中的物件的參照應使用適當的資料庫名稱進行限定。db_name
有關預存常式中不允許使用的陳述式的更多資訊,請參閱第 27.8 節,「預存程式的限制」。
有關從以具有 MySQL 介面的語言編寫的程式中調用預存程序的資訊,請參閱第 15.2.1 節,「CALL 陳述式」。
MySQL 會儲存常式建立或更改時生效的 sql_mode
系統變數設定,並且始終使用此設定強制執行常式,無論常式開始執行時的當前伺服器 SQL 模式為何。
從調用者的 SQL 模式切換到常式的 SQL 模式,會在評估引數以及將結果值指派給常式參數之後發生。如果您在嚴格 SQL 模式下定義常式,但在非嚴格模式下調用它,則將引數指派給常式參數不會在嚴格模式下進行。如果您要求傳遞給常式的運算式在嚴格 SQL 模式下指派,則應在生效嚴格模式下調用該常式。
COMMENT
特性是 MySQL 擴充功能,可用於描述預存常式。此資訊會由 SHOW CREATE PROCEDURE
和 SHOW CREATE FUNCTION
陳述式顯示。
LANGUAGE
特性表示常式編寫的語言。伺服器會忽略此特性;僅支援 SQL 常式。
如果常式對於相同的輸入參數總是產生相同的結果,則該常式被視為「決定性的」,否則被視為「非決定性的」。如果在常式定義中未給定 DETERMINISTIC
也未給定 NOT DETERMINISTIC
,則預設為 NOT DETERMINISTIC
。若要宣告函數是決定性的,您必須明確指定 DETERMINISTIC
。
對常式性質的評估是基於建立者的「誠實」:MySQL 不會檢查宣告為 DETERMINISTIC
的常式是否沒有產生非決定性結果的陳述式。但是,錯誤地宣告常式可能會影響結果或影響效能。將非決定性常式宣告為 DETERMINISTIC
可能會導致最佳化器做出不正確的執行計畫選擇,從而導致意外的結果。將決定性常式宣告為 NONDETERMINISTIC
可能會因導致無法使用可用的最佳化而降低效能。
如果已啟用二進位記錄,則 DETERMINISTIC
特性會影響 MySQL 接受哪些常式定義。請參閱第 27.7 節,「預存程式二進位記錄」。
包含 NOW()
函數(或其同義詞)或 RAND()
的常式是非決定性的,但它仍然可能是可安全複寫的。對於 NOW()
,二進位記錄會包含時間戳記並正確地複寫。RAND()
也會正確地複寫,只要它在常式執行期間僅被呼叫一次。(您可以將常式執行時間戳記和隨機數種子視為來源和複本上相同的隱式輸入。)
多個特性提供了有關常式資料使用性質的資訊。在 MySQL 中,這些特性僅供參考。伺服器不會使用它們來限制常式允許執行的陳述式類型。
SQL SECURITY
特性可以是 DEFINER
或 INVOKER
,以指定安全性內容;也就是說,常式是使用常式 DEFINER
子句中指定的帳戶的權限執行,還是使用調用它的使用者執行。此帳戶必須具有存取與常式關聯之資料庫的權限。預設值為 DEFINER
。如果常式在定義者安全性內容中執行,則調用該常式的使用者必須擁有它的 EXECUTE
權限,DEFINER
帳戶也必須如此。
DEFINER
子句指定在常式執行時檢查具有 SQL SECURITY DEFINER
特性的常式的存取權限時要使用的 MySQL 帳戶。
如果存在 DEFINER
子句,則 user
值應為指定為 '
、user_name
'@'host_name
'CURRENT_USER
或 CURRENT_USER()
的 MySQL 帳戶。允許的 user
值取決於您擁有的權限,如第 27.6 節,「預存物件存取控制」中所述。另請參閱該節以取得有關預存常式安全性的其他資訊。
如果省略 DEFINER
子句,則預設定義者是執行 CREATE PROCEDURE
或 CREATE FUNCTION
陳述式的使用者。這與明確指定 DEFINER = CURRENT_USER
相同。
在使用 SQL SECURITY DEFINER
特性定義的預存常式的主體中,CURRENT_USER
函數會返回常式的 DEFINER
值。有關預存常式中的使用者稽核的資訊,請參閱第 8.2.23 節,「基於 SQL 的帳戶活動稽核」。
考慮以下程序,它會顯示 mysql.user
系統資料表中列出的 MySQL 帳戶數目計數
CREATE DEFINER = 'admin'@'localhost' PROCEDURE account_count()
BEGIN
SELECT 'Number of accounts:', COUNT(*) FROM mysql.user;
END;
無論哪個使用者定義程序,都會為該程序指派 'admin'@'localhost'
的 DEFINER
帳戶。無論哪個使用者調用它,它都會使用該帳戶的權限執行(因為預設安全性特性為 DEFINER
)。該程序的成功或失敗取決於調用者是否擁有它的 EXECUTE
權限,以及 'admin'@'localhost'
是否擁有 mysql.user
資料表的 SELECT
權限。
現在假設使用 SQL SECURITY INVOKER
特性定義程序
CREATE DEFINER = 'admin'@'localhost' PROCEDURE account_count()
SQL SECURITY INVOKER
BEGIN
SELECT 'Number of accounts:', COUNT(*) FROM mysql.user;
END;
該程序仍然具有 'admin'@'localhost'
的 DEFINER
,但在這種情況下,它會使用調用使用者的權限執行。因此,該程序的成功或失敗取決於調用者是否擁有它的 EXECUTE
權限,以及 mysql.user
資料表的 SELECT
權限。
預設情況下,當執行具有 SQL SECURITY DEFINER
特性的常式時,MySQL 伺服器不會為 DEFINER
子句中指定的 MySQL 帳戶設定任何有效角色,僅設定預設角色。例外情況是,如果啟用 activate_all_roles_on_login
系統變數,在這種情況下,MySQL 伺服器會設定授予 DEFINER
使用者的所有角色,包括強制角色。因此,在發出 CREATE PROCEDURE
或 CREATE FUNCTION
陳述式時,預設情況下不會檢查透過角色授予的任何權限。對於預存程式,如果執行應使用與預設值不同的角色,則程式主體可以執行 SET ROLE
以啟用所需的角色。這必須謹慎執行,因為可以變更指派給角色的權限。
伺服器會如下處理常式參數、使用 DECLARE
建立的本機常式變數或函數傳回值的資料類型
會檢查指派是否存在資料類型不符和溢位。轉換和溢位問題會導致警告,或在嚴格 SQL 模式下導致錯誤。
只能指派純量值。例如,
SET x = (SELECT 1, 2)
等陳述式是無效的。對於字元資料類型,如果宣告中包含
CHARACTER SET
,則會使用指定的字元集及其預設排序規則。如果也存在COLLATE
屬性,則會使用該排序規則而不是預設排序規則。如果不存在
CHARACTER SET
和COLLATE
,則會使用常式建立時生效的資料庫字元集和排序規則。若要避免伺服器使用資料庫字元集和排序規則,請為字元資料參數提供明確的CHARACTER SET
和COLLATE
屬性。如果您變更資料庫預設字元集或排序規則,則必須刪除並重新建立要使用新資料庫預設值的預存常式。
資料庫的字元集和定序由系統變數
character_set_database
和collation_database
的值決定。 更多資訊請參閱 第 12.3.3 節,「資料庫字元集與定序」。