思途SQL学习 0729

在MySQL中,函数主要分为两大类:内置函数和用户自定义函数(UDF)。

内置函数

MySQL提供了丰富的内置函数来帮助执行各种操作,比如数学运算、字符串处理、日期时间处理等。使用这些函数非常直接,你只需要按照正确的语法在你的SQL查询中调用它们即可。例如:

  • 数学函数:ABS(-5) 返回 5。
  • 字符串函数:UPPER('hello') 返回 'HELLO'。
  • 日期函数:CURDATE() 返回当前日期。

用户自定义函数(UDF)

如果你需要的功能不能通过内置函数实现,你可以创建自己的函数。这通常涉及到存储过程或函数的编写。这里我们关注于如何创建一个简单的标量函数,它可以接受输入参数并返回一个结果。


一、MySQL 函数的本质

在 MySQL 中,函数(Function) 是一种可重复使用的、带有逻辑的数据库对象,它的核心特点是:

  1. 必须返回一个值 :这是函数与存储过程最根本的区别。函数通过 RETURN 语句返回一个结果。
  2. 可以接受参数 :函数可以定义输入参数(IN 参数,这是默认的)。
  3. 可以在 SQL 语句中调用 :函数最强大的地方在于它能像 SUM(), UPPER() 这样的内置函数一样,被嵌入到 SELECT, WHERE, INSERT, UPDATE 等 SQL 语句中使用。
  4. 有确定性(Deterministic)属性 :如果函数对于相同的输入总是返回相同的结果,可以声明为 DETERMINISTIC,这有助于查询优化器进行优化。

二、函数的基本语法结构

sql 复制代码
DELIMITER $$

CREATE FUNCTION function_name (parameter1 datatype, parameter2 datatype, ...)
RETURNS return_datatype
[DETERMINISTIC | NOT DETERMINISTIC] -- 可选,是否确定性
READS SQL DATA | MODIFIES SQL DATA | CONTAINS SQL | NO SQL -- 可选,数据访问特性
BEGIN
    -- 函数体:声明变量、控制流、SQL 语句等
    DECLARE variable_name datatype DEFAULT value;
    -- ... 逻辑处理 ...
    RETURN return_value; -- 必须有,且返回值类型与声明一致
END$$

DELIMITER ;

三、对例子进行详细解析

1. 最简单的函数:f_hello()
sql 复制代码
CREATE FUNCTION `f_hello`() RETURNS varchar(100) CHARSET utf8mb3
BEGIN
    RETURN 'Hello World';
END
  • 特点 :
    • 无参数。
    • 返回一个固定的字符串 varchar(100)。
    • 可以直接在 SELECT 中调用:SELECT f_hello(); -> 输出 Hello World。
  • 用途:演示基本语法,或作为占位符。
2. 带计算逻辑的函数:f_jiecheng(n)
sql 复制代码
CREATE FUNCTION `f_jiecheng`(n int) RETURNS bigint
BEGIN 
    declare result bigint DEFAULT 1; 
    declare i int default 1;
    WHILE i <= n DO
        IF i % 7 <> 0 THEN -- 注意:这里是排除 7 的倍数
            SET result = result * i;
        END IF;
        SET i = i + 1;
    END WHILE;
    RETURN result;
END
  • 特点 :
    • 接受一个 INT 类型的参数 n。
    • 使用 DECLARE 声明了局部变量 result 和 i。
    • 包含了循环 (WHILE) 和条件判断 (IF)。
    • 返回一个 BIGINT 类型的计算结果。
  • 调用 :SELECT f_jiecheng(5); 计算的是 1*2*3*4*5(因为 7>5,所以不影响),返回 120。
  • 注意:这个函数计算的不是标准阶乘(排除了 7 的倍数),但逻辑清晰。
3. 带 SQL 查询和游标的函数:f_get_avg_study_point(classid)
sql 复制代码
CREATE FUNCTION `f_get_avg_study_point`(classid int) RETURNS float
BEGIN
    DECLARE endLoop INT DEFAULT 0;
    DECLARE point int DEFAULT 0;
    DECLARE sum int DEFAULT 0;
    DECLARE amount int;

    DECLARE cur CURSOR FOR SELECT study_points from t_students where class_id = classid;
    DECLARE CONTINUE HANDLER FOR NOT FOUND set endLoop = 1;

    OPEN cur;
    FETCH cur INTO point;
    WHILE endLoop < 1 do 
        SET sum = sum + point;
        FETCH cur INTO point;
    END WHILE;
    CLOSE cur;

    SELECT count(*) INTO amount from t_students where class_id = classid;

    RETURN ROUND(sum/amount,2); 
END
  • 特点 :
    • 接受班级 ID 作为参数。
    • 使用 游标(CURSOR) 遍历 t_students 表中指定班级的学生学分。
    • 使用 DECLARE CONTINUE HANDLER FOR NOT FOUND 来捕获游标遍历结束的信号(NOT FOUND),并设置标志 endLoop=1 来退出循环。
    • 手动累加学分 (sum),再查询学生总数 (amount),最后计算平均值。
    • 使用 ROUND() 函数四舍五入到 2 位小数。
4. 嵌套查询和条件判断的函数:f_get_level(id)
sql 复制代码
CREATE FUNCTION `f_get_level`(id int) RETURNS char(2) CHARSET utf8mb4 COLLATE utf8mb4_general_ci
BEGIN
    DECLARE avg_iq float; 
    DECLARE current_iq int;

    -- 查询该学生所在班级的平均智商
    SELECT avg(iq) INTO avg_iq FROM t_student where class_id = (
        SELECT class_id from t_student t where t.id = id
    );

    -- 查询该学生的智商
    SELECT iq INTO current_iq from t_student t1 where t1.id = id;

    IF current_iq > avg_iq THEN
        RETURN '及格';
    ELSE
        RETURN '淘汰';
    END IF;
END
  • 特点 :
    • 接受学生 ID。
    • 包含子查询:先查出该学生所属的 class_id,再用这个 class_id 去查全班的平均智商。
    • 使用 SELECT ... INTO variable 将查询结果赋值给局部变量。
    • 使用 IF...ELSE...END IF 进行条件判断。
    • 返回一个 CHAR(2) 类型的字符串('及格' 或 '淘汰')。
  • 调用 :SELECT f_get_level(101); 返回学生 ID 为 101 的等级。
  • 优化思考:这个查询可以优化为一次连接查询,但作为函数逻辑是清晰的。

5.存储过程 p_delete_student() - 基于函数判断的删除操作
  • 核心逻辑 :
    1. 使用游标 cur 遍历 t_student 表中所有学生 ID (pid)。
    2. 对每个 pid,调用函数 f_get_level(pid) 来判断该学生是否"淘汰"。
    3. 如果函数返回 '淘汰',则执行 DELETE 语句删除该学生记录。
  • 关键点 :
    • 函数调用 :SELECT f_get_level(pid) INTO a; 这是函数在过程内部被调用的典型方式。函数 f_get_level 的返回值被赋给了变量 a。
    • DML 操作 :过程内部执行了 DELETE 语句,这只能 在存储过程中实现,不能在函数中实现。
    • 游标与循环 :展示了如何使用游标和 WHILE 循环来逐行处理查询结果。
    • 错误处理 :DECLARE CONTINUE HANDLER FOR NOT FOUND SET con = 1; 处理了游标 FETCH 时没有更多数据的情况,这是游标编程的标准做法。
  • 为什么是过程? 因为它执行了数据修改(DELETE),并且没有单一的返回值,而是执行一系列动作。

综合梳理:MySQL 函数与存储过程的核心要点

特性/方面 函数 (FUNCTION) 存储过程 (PROCEDURE)
定义关键字 CREATE FUNCTION CREATE PROCEDURE
返回值 必须 有 RETURNS 子句和 RETURN 语句。返回单一值。 没有 RETURNS 子句。可通过 OUT/INOUT 参数返回多个值,或不返回。
调用方式 可在 SQL 表达式中 调用:SELECT func_name(...), column FROM table; 或 WHERE func_name(...) = value; 使用 CALL proc_name(...); 语句独立调用。
参数类型 默认且仅支持 IN 参数(输入)。 支持 IN (输入), OUT (输出), INOUT (输入输出)。
DML 操作 禁止 执行 INSERT, UPDATE, DELETE 等修改数据的语句。 允许 执行任何 DML 语句。
事务控制 不能包含 COMMIT 或 ROLLBACK。 可以包含 COMMIT 或 ROLLBACK 来显式管理事务。
主要用途 数据计算、转换、格式化、复杂条件判断(返回结果)。作为 SQL 语句的一部分。 执行复杂的业务逻辑、批处理任务、数据清理、报表生成、调用多个函数/过程等。
你的例子 f_hello, f_jiecheng, f_get_avg_study_point, f_get_level, f_sum_unil p_delete_student, proc_gen_student_score
函数调用 可在自身或其他过程/函数中被调用(需注意限制)。 可在自身或其他过程/函数中被调用(用 CALL)。
错误处理 可使用 DECLARE ... HANDLER。 可使用 DECLARE ... HANDLER。
游标 可以 使用游标(如 f_get_avg_study_point),但不能在游标循环内执行 DML。 可以 使用游标,并在循环内执行 DML(如 p_delete_student, proc_gen_student_score)。

游标(Cursor) 在数据库管理系统中是一种用于遍历查询结果集的数据库对象。它允许应用程序或用户逐行处理查询返回的数据,而不是一次性将所有数据加载到内存中。这对于处理大量数据或者需要对每一行执行特定操作的情况特别有用。

游标的基本概念

  1. 声明游标:定义一个游标,并将其与一个查询关联起来。
  2. 打开游标:执行与游标关联的查询,并准备好获取数据。
  3. 提取数据:从查询结果集中逐行获取数据。
  4. 关闭游标:完成数据处理后,关闭游标释放资源。

MySQL中的游标使用

在MySQL中,游标通常在存储过程或函数内部使用。以下是一个基本的游标操作流程:

sql 复制代码
-- 声明变量
DECLARE var_name datatype;

-- 声明游标
DECLARE cur_name CURSOR FOR select_statement;

-- 声明继续处理器,当SQL语句没有找到更多行时设置标志位
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

-- 打开游标
OPEN cur_name;

-- 循环提取数据
read_loop: LOOP
    FETCH cur_name INTO var_name;
    IF done THEN
        LEAVE read_loop;
    END IF;
    -- 处理逻辑...
END LOOP;

-- 关闭游标
CLOSE cur_name;

关键点说明

  • DECLARE CURSOR :定义游标并指定与其关联的SELECT语句。
  • OPEN:执行游标对应的查询,并使游标指向结果集的第一行之前。
  • FETCH:将当前行的数据提取到指定的变量中,并将游标向前移动到下一行。
  • CONTINUE HANDLER :定义了当没有更多行可供读取时的行为(例如设置一个标志变量done为1)。
  • CLOSE:关闭游标,释放相关资源。
相关推荐
PHP实战开发录32 分钟前
MySQL字段加索引为什么没变快
数据库·mysql·php·开发
红红谈说2 小时前
标签化推送与已读统计怎么做?一次通知链路的工程复盘
数据库·消息推送·数字乡村·集合运算·已读回执·标签体系·写入放大
每天题库2 小时前
架子工脚手架工题库:安全操作考点与模拟题练习指南
学习·安全·考试·题库·考证
Shadow(⊙o⊙)2 小时前
MySQL索引
数据库·mysql
yangmu32032 小时前
Codex 提示词优化教程:用四要素把模糊需求变成可执行任务
人工智能·学习
无敌贵点大王3 小时前
RTThread学习记录13——关于要使用一个外设,RTThread与cubemx到底要怎么配合?
c语言·stm32·学习·rtthread
倔强的石头_4 小时前
多环境配置治理:开发、测试、生产连接信息如何隔离
数据库
杨云龙UP4 小时前
一次数据库查询缓慢故障复盘:大表数据增长、SQL全表扫描导致系统响应异常
linux·运维·服务器·数据库·sql·mysql
2501_933670794 小时前
2027 应用统计学秋招选岗指南:统计、SQL、业务指标如何对应岗位
数据库·sql
喜欢打篮球的普通人4 小时前
MiniMind 学习笔记(十二):Pretrain 实操——从版本梳理到 8GB 显卡上的真实训练
人工智能·笔记·学习