MySQL学习笔记之MySQL自定义函数

一、什么是函数
函数存储着一系列sql语句,调用函数就是一次性执行这些语句。所以函数可以降低语句重复。【但注意的是函数注重返回值,不注重执行过程,所以一些语句无法执行。所以函数并不是单纯的sql语句集合。】

二、函数和存储过程的区别
关于存储过程,可以查看MySQL学习笔记之MySQL存储过程
1)存储过程可以没有返回值,也可以有多个返回值,比较适合做批量插入、批量更新等操作
2)函数有且仅有一个返回值,比较适合做处理数据后返回一个结果

三、创建函数

  语法:
  create function 函数名([参数列表]) returns 返回类型
  begin
        函数体
  end
  参数格式:参数名 参数类型

四、调用函数
语法:select 函数名(参数列表)

五、系统变量log_bin_trust_function_creators

  首先查看一个系统变量:log_bin_trust_function_creators
  
  select @@log_bin_trust_function_creators;
  mysql> select @@log_bin_trust_function_creators;
  +-----------------------------------+
  | @@log_bin_trust_function_creators |
  +-----------------------------------+
  |                                 0 |
  +-----------------------------------+
  1 row in set (0.00 sec)

变量说明:
当二进制日志启用后,这个变量就会启用。它控制是否可以信任存储函数创建者,不会创建写入二进制日志引起不安全事件的存储函数。如果设置为0(默认值),用户不得创建或修改存储函数,除非它们具有除CREATE ROUTINE或ALTER ROUTINE特权之外的SUPER权限。 设置为0还强制使用DETERMINISTIC特性或READS SQL DATA或NO SQL特性声明函数的限制。 如果变量设置为1,MySQL不会对创建存储函数实施这些限制。 此变量也适用于触发器的创建。
当log_bin_trust_function_creators的值为0时创建存储函数将会报错:ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you might want to use the less safe log_bin_trust_function_creators variable),如下:

  mysql> delimiter $$
  mysql> create function get_students_total() returns int
        -> begin
        -> declare total int default 0; # 定义total变量,默认值为0
        -> select count(*) into total from students;
        -> return total;
        -> end $$
  ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)
  
  mysql> set @@global.log_bin_trust_function_creators = 1;
  Query OK, 0 rows affected (0.00 sec)

  mysql> delimiter $$
  mysql> create function get_students_total() returns int
        -> begin
        -> declare total int default 0;
        -> select count(*) into total from students;
        -> return total;
        -> end $$
  Query OK, 0 rows affected (0.02 sec)

  mysql> select get_students_total();
  +----------------------+
  | get_students_total() |
  +----------------------+
  |                    7 |
  +----------------------+
  1 row in set (0.06 sec)

那么为什么MySQL有这样的限制呢? 因为二进制日志的一个重要功能是用于主从复制,而存储函数有可能导致主从的数据不一致。
所以当开启二进制日志后,参数log_bin_trust_function_creators就会生效,限制存储函数的创建、修改、调用。
如何解决这个问题呢?
1)如果数据库没有使用主从复制,那么就可以将参数log_bin_trust_function_creators设置为1。

  mysql> set @@global.log_bin_trust_function_creators = 1;

这个动态设置的方式会在服务重启后失效,所以我们还必须在my.cnf中设置,加上log_bin_trust_function_creators=1,这样就会永久生效。
2)明确指明函数的类型,如果我们开启了二进制日志, 那么我们就必须为我们的function指定一个参数。
其中下面几种参数类型里面,只有 DETERMINISTIC, NO SQL 和 READS SQL DATA 被支持。
这样一来相当于明确的告知MySQL服务器这个函数不会修改数据。

  ① DETERMINISTIC 不确定的
  ② NO SQL 没有SQl语句,当然也不会修改数据
  ③ READS SQL DATA 只是读取数据,当然也不会修改数据
  ④ MODIFIES SQL DATA 要修改数据
  ⑤ CONTAINS SQL 包含了SQL语句
  mysql> delimiter $$
  mysql> create function get_total() returns int reads sql data
        -> begin
        -> declare total int default 0;
        -> select count(*) into total from students;
        -> return total;
        -> end $$
  Query OK, 0 rows affected (0.08 sec)

  mysql> delimiter ;
  mysql> select get_total();
  +-------------+
  | get_total() |
  +-------------+
  |           7 |
  +-------------+
  1 row in set (0.09 sec)

六、存储函数使用示例

  例1:创建存储函数,获取所有学生的总成绩
  mysql> delimiter $$
  mysql> create function get_students_totalScore() returns int reads sql data
        -> begin
        -> declare totalScore int default 0;
        ->  select sum(ifnull(t.总成绩,0)) into totalScore from students st left join (select s.student_id,sum(s.score) 总成绩 from subject su left join score s on su.id=s.subject_id group by s.student_id order by 总成绩 desc) t on st.id=t.student_id;
        ->  return totalScore;
        -> end $$
  Query OK, 0 rows affected (0.00 sec)

  mysql> delimiter ;
  mysql> select get_students_totalScore();
  +---------------------------+
  | get_students_totalScore() |
  +---------------------------+
  |                       576 |
  +---------------------------+
  1 row in set (0.04 sec)

  例2:创建存储函数根据传入的学生姓名,获取该学生的总成绩
  
  mysql> delimiter $$
  mysql> create function get_totalScore(name varchar(20)) returns int reads sql data
        -> begin
        -> declare totalScore int default 0;
        -> select ifnull(t.总成绩,0) into totalScore from students st left join (select s.student_id,sum(s.score) 总成绩 from subject su left join score s on su.id=s.subject_id group by s.student_id order by 总成绩 desc) t on st.id=t.student_id where st.name=name;
        -> return totalScore;
        -> end $$
  Query OK, 0 rows affected (0.06 sec)

  mysql> delimiter ;
  mysql> select get_totalScore('李四');
  +--------------------------+
  | get_totalScore('李四')   |
  +--------------------------+
  |                      268 |
  +--------------------------+
  1 row in set (0.06 sec)

七、查看存储函数

  语法:show create function 函数名
  
  mysql> show create function get_totalScore\G;
  *************************** 1. row ***************************
        Function: get_totalScore
        sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
 Create Function: CREATE DEFINER=`root`@`localhost` FUNCTION `get_totalScore`(name varchar(20)) RETURNS int
                  READS SQL DATA
                    begin
                          declare totalScore int default 0;
                          select ifnull(t.总成绩,0) into totalScore from students st left join (select s.student_id,sum(s.score) 总成绩 from subject su left join score s on su.id=s.subject_id group by s.student_id order by 总成绩 desc) t on st.id=t.student_id where st.name=name;
                          return totalScore;
                    end
  character_set_client: utf8mb4
  collation_connection: utf8mb4_0900_ai_ci
    Database Collation: utf8mb4_unicode_ci
  1 row in set (0.00 sec)

八、删除存储函数

  语法:drop function 函数名
posted @ 2020-12-02 17:54  huige185  阅读(290)  评论(0编辑  收藏  举报