本文为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,'!');