本文爲mariadb官方手冊:CREATE FUNCTION的譯文。mysql
原文:https://mariadb.com/kb/en/library/create-function/
我提交到MariaDB官方手冊的譯文:https://mariadb.com/kb/zh-cn/create-function/ sql
CREATE [OR REPLACE] [DEFINER = {user | CURRENT_USER | role | CURRENT_ROLE }] [AGGREGATE] FUNCTION [IF NOT EXISTS] func_name ([func_parameter[,...]]) RETURNS type [characteristic ...] RETURN func_body func_parameter: param_name type type: Any valid MariaDB data type characteristic: LANGUAGE SQL | [NOT] DETERMINISTIC | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA } | SQL SECURITY { DEFINER | INVOKER } | COMMENT 'string' func_body: Valid SQL procedure statement
可使用CREATE FUNCTION語句建立一個新的存儲函數stored function。要使用CREATE FUNCTION語句,必需要具有CREATE ROUTINE權限。數據庫
函數能夠定義任意數量的參數,在函數體(func_body)部分會返回一個值。函數體部分能夠是任意有效的SQL表達式,例如某些select語句。若是你有合適的權限,你徹底能夠像調用內置函數同樣調用存儲函數。關於權限的詳細信息,見下文:Security。服務器
此外,你也可使用CREATE FUNCTION語句的變體格式來安裝一個用戶自定義函數(UDF)。關於UDF,詳細信息見:CREATE FUNCTION (UDF)。函數
你可使用一個圓括號包圍SELECT做爲func_body部分,正如使用子查詢同樣。但注意,SELECT語句必須返回單個值(標量值,即單行且單列的值)。調用函數時,若是SELECT語句返回了多列,則報1241的錯誤,若是SELECT語句返回了多行,則報1242的錯誤。爲了保險,可使用LIMIT子句保證只返回單行數據。性能
你可使用BEGIN...END語句塊替換這裏的RETURN子句,可是在語句塊中,必需要包含一個RETURN語句。當調用函數時,執行到RETURN子句時將當即返回其結果,在RETURN子句以後的語句都不會再執行。優化
默認狀況下,函數是關聯到默認數據庫上的。若是要將函數顯式關聯到一個指定的數據庫,能夠在建立時使用全稱db_name.func_name
。若是建立的存儲函數名和內置的函數名同名,則必須使用全稱來調用它。ui
定義存儲函數時,參數列表能夠爲空。若是指定參數名,則參數名不區分大小寫。spa
每一個參數均可以聲明爲任意有效的數據類型,但沒法使用COLLATE屬性。日誌
RETURNS子句指定函數的返回類型。可使用NULL值來表示返回任意有效數據類型。
若是RETURN子句的返回值類型和此處定義的數據類型不一致會如何?這取決於建立函數的時候,SQL_MODE的影響行爲。
若是SQL_MODE爲strict模式的值(即指定了STRICT_ALL_TABLES或STRICT_TRANS_TABLES),將報1366錯誤。
除這種狀況,若是返回值類型不一致,則返回值將被強制轉換爲指定的數據類型。例如,RETURNS子句指定返回一個ENUM或SET數據類型,但RETURN子句返回了一個整型,則返回值將強制轉換爲ENUM或SET成員對應的字符串(譯者注:雖然ENUM容許存儲數值,但強烈建議不要存儲數值,由於很是容易混淆ENUM的索引值和實際存儲的數值,所以這裏直接說是字符串)。
MariaDB將在建立routine的時候保留系統變量SQL_MODE的值,之後任什麼時候間調用routine時都使用該SQL_MODE值,而無論當前調用routine時的SQL MODE值是什麼。
LANGUAGE SQL表明的是一個標準的SQL子句,它是爲了移植性而存在的。可是,該子句在MariaDB中沒有任何意義,由於MariaDB的存儲函數中惟一支持的語言只有SQL。
若是使用了OR REPLACE子句,它的行爲等價於:
DROP FUNCTION IF EXISTS function_name;
CREATE FUNCTION function_name ...;
但不會刪除該函數已有的權限privileges。
若是使用 IF NOT EXISTS 子句,那麼當函數存在時,MariaDB將返回一個warning信息而不是直接返回錯誤。IF NOT EXISTS不能和OR REPLACE一塊兒使用。
若是函數根據給定的參數列表可以返回一個肯定的結果,則該函數是肯定的(deterministic)。若是函數的返回值 會因某些數據、變量、隨機數或任意不肯定的值而受影響,則函數是不肯定的。此外,若是存儲函數中使用了不肯定的函數(如NOW()或CURRENT_TIMESTAMP()),則該存儲函數也是不肯定的。
若是優化器知道函數是肯定的,它會選擇一個更快更有效的執行計劃。你可使用DETERMINISTIC關鍵字來定義這個routine。若是你想顯式將函數標記爲不肯定的(默認就是如此),可使用NOT DETERMINISTIC關鍵字。
若是你將一個不肯定的函數聲明爲DETERMINISTIC,將返回一個錯誤結果。若是你將一個肯定的函數聲明爲NOT DETERMINISTIC,則某些狀況下,該查詢語句的性能將大幅下降。
[NOT] DETERMINISTIC子句還會影響二進制日誌binary logging,由於日誌中的語句格式沒法 存儲或替換不肯定的語句。
CONTAINS SQL, NO SQL, READS SQL DATA 以及 MODIFIES SQL DATA是信息類的子句,它們告訴服務器該函數是作什麼的。MariaDB不會對這些語句作任何語法檢查。若是不指定這些語句,則默認使用CONTAINS SQL。
MODIFIES SQL DATA意味着函數中包含了要修改數據庫中數據的語句。例如函數中使用了相似於DELETE, UPDATE, INSERT, REPLACE或DDL類的語句。
READS SQL DATA意味着函數中包含了從數據庫中讀取數據的語句,可是不會修改任何數據。例如函數中使用了不包含任何寫操做的SELECT語句。
CONTAINS SQL意味着函數包含了至少一條SQL語句,可是它不會讀也不會寫數據庫。例如函數中包含了SET或DO子句。
NO SQL意味着什麼?啥也不意味着。由於MariaDB目前除了SQL語言,不支持任何其餘語言。
要想調用函數,你必需要擁有該函數的EXECUTE權限。
MariaDB會自動爲建立函數CREATE FUNCTION的用戶授予EXECUTE 和 ALTER ROUTINE權限,即便使用了DEFINER子句。
每一個函數都有一個關聯的帳號(即definer)。默認狀況下,definer即爲函數的建立者。可使用DEFINER子句顯式指定關聯到其餘帳號上。要使用DEFINER,你必需要擁有SUPER權限。詳細信息見:Account Names。
SQL SECURITY子句指定了當調用函數時所使用的權限。若是SQL SECURITY的值爲INVOKER,則將使用函數調用者的權限去對比(即評估)函數體中的語句權限。若是SQL SECURITY的值爲DEFINER,則老是使用definer用戶的權限去評估函數體的權限。默認值爲DEFINER。
經過該子句,你能夠建立一個只容許某用戶訪問部分數據的函數。例如,你有一張存儲了員工信息的表,而且你已經授予了用戶roger對該表某些列(only on certain columns)的SELECT權限。
CREATE TABLE employees (name TINYTEXT, dept TINYTEXT, salary INT);
GRANT SELECT (name, dept) ON employees TO roger;
能夠定義一個函數來獲取部門中薪水最高的用戶,並授予EXECUTE權限:
CREATE FUNCTION max_salary (dept TINYTEXT) RETURNS INT RETURN (SELECT MAX(salary) FROM employees WHERE employees.dept = dept);
GRANT EXECUTE ON FUNCTION max_salary TO roger;
因爲SQL SECURITY的默認值爲DEFINER,不管roger用戶什麼時候調用該函數,都會使用你的權限來執行其中的子查詢。只要你有查詢每一個員工薪水的權限,即便函數調用者不具有直接查詢薪水的權限,他們也能獲取到每一個部門的最高薪水。
能夠爲函數聲明使用任意有效的字符集和排序規則character set and collation。若是定義了它們,COLLATE屬性須要定義在CHARACTER SET以後。
若是沒有指定字符集和排序規則,則使用函數建立時的系統默認值。即便以後系統默認字符集和排序規則改變了,函數所使用的字符集也不會隨之改變。這種狀況下,應該重建函數並使用數據庫所使用的字符集和排序規則。
下面的函數示例使用了一個參數,並在函數中執行了一個SQL內置函數CONCAT()
,最後返回結果。
CREATE FUNCTION hello (s CHAR(20)) RETURNS CHAR(50) DETERMINISTIC RETURN CONCAT('Hello, ',s,'!');
SELECT hello('world');
+----------------+
| hello('world') |
+----------------+
| Hello, world! |
+----------------+
你能夠在函數內部使用一個語句塊來操做數據(即便用DML),例如INSERT和UPDATE。下面的例子中建立了一個函數計數器,它使用了一個臨時表來存儲當前的值。由於語句塊包含了語句終止符號";",所以必須首先使用DELIMITER語句改變語句的終止符,使得函數體中可以使用分號。更多信息見Delimiters in the mysql client。
CREATE TEMPORARY TABLE counter (c INT);
INSERT INTO counter VALUES (0);
DELIMITER //
CREATE FUNCTION counter () RETURNS INT BEGIN UPDATE counter SET c = c + 1;
RETURN (SELECT c FROM counter LIMIT 1);
END // DELIMITER ;
字符集和排序規則:
CREATE FUNCTION hello2 (s CHAR(20)) RETURNS CHAR(50) CHARACTER SET 'utf8' COLLATE 'utf8_bin' DETERMINISTIC RETURN CONCAT('Hello, ',s,'!');