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 | JAVASCRIPT }
| [NOT] DETERMINISTIC
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
}
routine_body:
SQL routine or JavaScript statements
這些陳述式用於建立預存常式 (預存程序或函式)。也就是說,指定的常式會被伺服器所知。依預設,預存常式會與預設資料庫相關聯。若要將常式明確地與指定的資料庫相關聯,請在建立時將名稱指定為 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.7,「預存物件存取控制」中所述。如果啟用二進位日誌記錄,CREATE FUNCTION
可能需要 SUPER
權限,如章節 27.8,「預存程式二進位日誌記錄」中所述。
預設情況下,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
。
僅在 PROCEDURE
中將參數指定為 IN
、OUT
或 INOUT
才有效。對於 FUNCTION
,參數始終被視為 IN
參數。
IN
參數將值傳遞到程序中。程序可能會修改該值,但當程序返回時,修改對呼叫者不可見。OUT
參數將值從程序傳遞回呼叫者。其初始值在程序中為 NULL
,且當程序返回時,其值對呼叫者可見。INOUT
參數由呼叫者初始化,可以由程序修改,並且程序所做的任何變更在程序返回時對呼叫者可見。
對於每個 OUT
或 INOUT
參數,請在呼叫程序的 CALL
陳述式中傳遞使用者定義的變數,以便您可以在程序返回時取得其值。如果您是從另一個預存程序或函數中呼叫該程序,您也可以將常式參數或本機常式變數作為 OUT
或 INOUT
參數傳遞。如果您是從觸發程序中呼叫該程序,您也可以將 NEW.
作為 col_name
OUT
或 INOUT
參數傳遞。
如需未處理的條件對程序參數的影響的相關資訊,請參閱章節 15.6.7.8,「條件處理和 OUT 或 INOUT 參數」。
常式參數無法在常式中準備的陳述式中引用;請參閱章節 27.9,「預存程式的限制」。
以下範例顯示一個簡單的預存程序,該程序在給定國家/地區代碼的情況下,會計算 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
指定,且為強制性。它表示函數的傳回類型,且函數主體必須包含 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.9,「預存程式的限制」。
如需從以具有 MySQL 介面的語言撰寫的程式中呼叫預存程序的相關資訊,請參閱章節 15.2.1,「CALL 陳述式」。
MySQL 會儲存建立或變更常式時生效的 sql_mode
系統變數設定,並始終強制執行此設定來執行常式,無論常式開始執行時的目前伺服器 SQL 模式如何。
在評估引數並將結果值賦予常式參數之後,會發生從呼叫者的 SQL 模式切換到常式 SQL 模式的情況。如果您在嚴格 SQL 模式下定義常式,但在非嚴格模式下呼叫它,則引數賦予常式參數的動作不會在嚴格模式下進行。如果您要求傳遞給常式的表達式以嚴格 SQL 模式賦予值,則應該在嚴格模式生效的情況下呼叫常式。
COMMENT
特性是 MySQL 的延伸功能,可用於描述儲存的常式。此資訊會由 SHOW CREATE PROCEDURE
和 SHOW CREATE FUNCTION
陳述式顯示。
LANGUAGE
特性會指出常式是以哪種語言撰寫的。伺服器會忽略此特性;僅支援 SQL 常式。如果未提供此特性,則會假設語言為 SQL。以 JavaScript 撰寫的儲存常式 (請參閱第 27.3 節「JavaScript 儲存程式」) 需要使用 LANGUAGE JAVASCRIPT
來指定。
如果常式針對相同的輸入參數總是產生相同的結果,則視為「決定性的」,否則視為「非決定性的」。如果常式定義中未給定 DETERMINISTIC
或 NOT DETERMINISTIC
,則預設為 NOT DETERMINISTIC
。若要宣告函式為決定性的,您必須明確指定 DETERMINISTIC
。
常式性質的評估是基於建立者的「誠實」:MySQL 不會檢查宣告為 DETERMINISTIC
的常式是否沒有產生非決定性結果的陳述式。但是,錯誤宣告常式可能會影響結果或效能。將非決定性的常式宣告為 DETERMINISTIC
可能會導致最佳化工具做出不正確的執行計畫選擇,進而產生非預期的結果。將決定性常式宣告為 NONDETERMINISTIC
可能會因為無法使用可用的最佳化而降低效能。
如果啟用二進位記錄,則 DETERMINISTIC
特性會影響 MySQL 接受哪些常式定義。請參閱第 27.8 節「儲存程式二進位記錄」。
包含 NOW()
函式(或其同義詞)或 RAND()
的常式是非決定性的,但它可能仍然可以安全地用於複寫。對於 NOW()
,二進位記錄會包含時間戳記並正確複寫。RAND()
也能正確複寫,只要它在常式執行期間僅呼叫一次即可。(您可以將常式執行時間戳記和隨機數種子視為來源和複本上相同的隱含輸入。)
數個特性提供了常式資料使用性質的相關資訊。在 MySQL 中,這些特性僅供參考。伺服器不會使用它們來限制允許常式執行的陳述式類型。
CONTAINS SQL
表示常式不包含讀取或寫入資料的陳述式。如果未明確給定這些特性,則這是預設值。這類陳述式的範例包括SET @x = 1
或DO RELEASE_LOCK('abc')
,它們會執行,但既不讀取也不寫入資料。NO SQL
表示常式不包含任何 SQL 陳述式。READS SQL DATA
表示常式包含讀取資料的陳述式(例如,SELECT
),但不包含寫入資料的陳述式。MODIFIES SQL DATA
表示常式包含可能寫入資料的陳述式(例如,INSERT
或DELETE
)。
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.7 節「儲存物件存取控制」中所述。另請參閱該節以了解有關儲存常式安全性的其他資訊。
如果省略 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, 「資料庫字元集和排序規則」。