第一部分:基础知识 10 . 存储过程和函数 --[MySQL轻松入门教程]

news2024/12/16 23:11:57

1.存储过程

1.存储过程的概念

MySQL 存储过程(Stored Procedure)是一组预编译的 SQL 语句和可选的控制流语句,它们被作为一个单元存储在数据库中。存储过程可以接受输入参数、返回输出参数,并且可以包含复杂的业务逻辑,包括条件判断、循环等结构。它们通过提供封装好的代码块来简化重复性任务,提高代码复用性和维护性。

存储过程的主要特点
  1. 预编译:当创建存储过程时,MySQL 会对其进行语法检查并编译,这使得调用存储过程比直接执行相同的 SQL 语句更快。
  2. 减少网络流量:因为存储过程是在服务器端执行的,所以只需要发送一次请求就可以完成多个操作,减少了客户端与服务器之间的数据传输量。
  3. 增强安全性:可以通过授予用户对特定存储过程的执行权限而不是表级别的权限来限制访问,从而增加安全性。
  4. 模块化编程:允许将复杂的逻辑分解成较小的部分,便于管理和维护。
  5. 事务支持:存储过程中可以包含事务控制语句,如 BEGIN, COMMITROLLBACK,确保一系列操作要么全部成功,要么全部失败。
  6. 输入/输出参数:可以定义 IN、OUT 或者 INOUT 参数,用于传递数据进出存储过程。

2.创建存储过程

以下是一个简单的例子,展示如何创建一个名为 GetEmployeeById 的存储过程,它根据员工 ID 获取员工信息:

-- 更改语句结束符为 $$,以便在存储过程中使用分号作为语句结束符
DELIMITER $$

-- 创建名为 GetEmployeeById 的存储过程
-- 该存储过程接受一个整数类型的输入参数 emp_id,并根据此 ID 查询 employees 表中的记录
CREATE PROCEDURE GetEmployeeById(IN emp_id INT)
BEGIN
    -- 定义存储过程的主体
    -- 使用 SELECT 语句查询 employees 表中 id 等于输入参数 emp_id 的所有列数据
    SELECT * FROM employees WHERE id = emp_id;
END$$

-- 恢复默认的语句结束符为分号 ;
DELIMITER ;

-- 注释:存储过程 GetEmployeeById 已创建完毕。
-- 通过 CALL GetEmployeeById(员工ID) 可以调用此存储过程并传入具体的员工ID来获取员工信息。

在这个例子中:

  • DELIMITER $$ 更改了语句结束符为 $$,以便可以在存储过程中使用分号作为语句结束符。
  • CREATE PROCEDURE GetEmployeeById(IN emp_id INT) 定义了一个名为 GetEmployeeById 的存储过程,接收一个整数类型的输入参数 emp_id
  • BEGIN ... END 是存储过程体,其中包含了要执行的 SQL 语句。

3.调用存储过程

一旦创建了存储过程,你可以使用 CALL 语句来调用它:

CALL GetEmployeeById(1);

这行命令将会执行 GetEmployeeById 存储过程,并传入值 1 作为 emp_id 参数,返回所有字段的数据行,其中 id 等于 1

4.删除存储过程

如果不再需要某个存储过程,可以使用 DROP PROCEDURE 语句将其删除:

DROP PROCEDURE IF EXISTS GetEmployeeById;

这行命令会检查是否存在名为 GetEmployeeById 的存储过程,并在存在的情况下将其删除。

注意事项
  • 性能考虑:虽然存储过程通常能提高性能,但在某些情况下,过度使用或不当设计可能会导致性能下降。因此,应该合理评估其使用场景。
  • 调试困难:相比于应用程序代码,存储过程的调试可能更为复杂,尤其是在处理错误和异常时。
  • 版本控制:由于存储过程直接存在于数据库中,因此需要特别注意版本控制系统中的管理,以避免不同环境之间的不一致性。

希望这些信息能够帮助您理解 MySQL 存储过程的概念。

2.存储函数

MySQL 存储函数(Stored Function)类似于存储过程,但它们的主要区别在于存储函数必须返回一个值,并且可以在 SQL 语句中像内置函数一样使用。这使得存储函数非常适合用于计算和返回特定结果,而不仅仅是执行一系列操作。

创建存储函数

创建存储函数的语法与创建存储过程相似,但需要指定返回的数据类型,并且函数体中必须包含 RETURN 语句来返回值。

示例:创建一个简单的存储函数

假设我们想要创建一个名为 GetEmployeeCount 的存储函数,它返回 employees 表中的员工总数:

-- 更改语句结束符为 $$,以便在存储函数中使用分号作为语句结束符
DELIMITER $$

-- 创建名为 GetEmployeeCount 的存储函数
-- 参数:无
-- 返回值:INT - 返回 employees 表中的员工总数
-- 属性:DETERMINISTIC - 表示只要输入相同,函数的结果就会一致
-- 目标:提供一个简单的方法来获取当前数据库中所有员工的数量
CREATE FUNCTION GetEmployeeCount()
RETURNS INT
DETERMINISTIC
BEGIN
    -- 开始函数体
    
    -- 声明一个局部变量 emp_count 来保存员工数量
    -- 注意:局部变量的作用域仅限于 BEGIN...END 块内
    DECLARE emp_count INT;

    -- 执行查询以计算 employees 表中的记录数,并将结果存入 emp_count 变量
    -- 使用 INTO 关键字可以直接将查询结果存入变量中
    -- 注意:确保 'employees' 表结构和数据完整性,以保证查询结果的准确性
    SELECT COUNT(*) INTO emp_count FROM employees;

    -- 返回存储在 emp_count 中的员工数量
    -- 注意:RETURN 语句必须是函数体的最后一行,因为它会立即退出函数并返回值
    RETURN emp_count;

    -- 结束函数体
END$$

-- 恢复默认的语句结束符为分号 ;
DELIMITER ;

-- 注释:存储函数 GetEmployeeCount 创建完成。
-- 调用方法:可以在 SQL 查询中像使用内置函数一样调用此函数,例如:
-- SELECT GetEmployeeCount() AS employee_count;
-- 这条语句将会返回 employees 表中的总员工数,并将其显示为 employee_count 列。

在这个例子中:

  • CREATE FUNCTION GetEmployeeCount() 定义了一个名为 GetEmployeeCount 的存储函数。
  • RETURNS INT 指定了该函数将返回一个整数值。
  • DETERMINISTIC 关键字表示只要输入相同,函数的结果就会一致。如果函数的行为不是确定性的,则可以省略这个关键字或使用 NOT DETERMINISTIC
  • BEGIN ... END 是函数体,其中包含了要执行的 SQL 语句和逻辑。
  • RETURN emp_count; 语句用来返回查询到的员工数量。

使用存储函数

一旦创建了存储函数,你就可以在 SQL 语句中像使用内置函数一样调用它。例如:

SELECT GetEmployeeCount() AS employee_count;

这行命令将会调用 GetEmployeeCount 函数,并将返回的员工数量显示为 employee_count 列。

更复杂示例:带参数的存储函数

如果你希望根据部门 ID 获取某个部门的员工数量,可以创建一个带有参数的存储函数:

-- 更改语句结束符为 $$,以便在存储函数中使用分号作为语句结束符
DELIMITER $$

-- 创建名为 GetDepartmentEmployeeCount 的存储函数
-- 该函数根据提供的部门ID返回对应部门的员工总数
CREATE FUNCTION GetDepartmentEmployeeCount(dept_id INT)
RETURNS INT
DETERMINISTIC
BEGIN
    -- 声明一个局部变量 dept_emp_count 来保存部门员工数量
    DECLARE dept_emp_count INT;

    -- 查询 employees 表中 department_id 等于输入参数 dept_id 的记录数,
    -- 并将结果赋值给 dept_emp_count 变量
    SELECT COUNT(*) INTO dept_emp_count 
    FROM employees 
    WHERE department_id = dept_id;

    -- 返回存储在 dept_emp_count 中的部门员工数量
    RETURN dept_emp_count;
END$$

-- 恢复默认的语句结束符为分号 ;
DELIMITER ;

-- 注释:存储函数 GetDepartmentEmployeeCount 创建完成。
-- 该函数可以在 SQL 查询中像内置函数一样使用,例如:
-- SELECT GetDepartmentEmployeeCount(1) AS dept_employee_count;

然后你可以这样调用它:

SELECT GetDepartmentEmployeeCount(1) AS dept_employee_count;

这将返回部门 ID 为 1 的员工数量。

删除存储函数

如果不再需要某个存储函数,可以使用 DROP FUNCTION 语句将其删除:

DROP FUNCTION IF EXISTS GetEmployeeCount;

这行命令会检查是否存在名为 GetEmployeeCount 的存储函数,并在存在的情况下将其删除。

注意事项

  • 事务支持:存储函数不能包含 COMMITROLLBACK 等事务控制语句,因为它们可能会影响数据库的一致性。
  • 限制修改数据:默认情况下,存储函数不允许修改表中的数据。如果你想在存储函数中修改数据,需要在创建时声明 MODIFIES SQL DATA
  • 性能考虑:虽然存储函数可以提高代码复用性和维护性,但在某些情况下,过度使用或不当设计可能会导致性能下降。因此,应该合理评估其使用场景。
  • 调试困难:相比于应用程序代码,存储函数的调试可能更为复杂,尤其是在处理错误和异常时。
  • 版本控制:由于存储函数直接存在于数据库中,因此需要特别注意版本控制系统中的管理,以避免不同环境之间的不一致性。

通过这些信息,你应该能够理解如何在 MySQL 中创建和使用存储函数了。

3.变量的使用

在 MySQL 存储过程中,变量用于存储临时数据或计算结果,并可以在存储过程的执行过程中使用。定义和使用变量可以帮助简化复杂的逻辑、提高代码可读性并增强功能。下面我将通过一个示例来展示如何在 MySQL 存储过程中定义和使用变量。

示例:计算两个日期之间的天数

假设我们有一个需求,需要创建一个存储过程 CalculateDaysBetweenDates,它接受两个日期作为输入参数,并返回这两个日期之间的天数。我们将在这个存储过程中使用变量来存储中间计算结果。

创建存储过程
DELIMITER $$

CREATE PROCEDURE CalculateDaysBetweenDates(IN start_date DATE, IN end_date DATE, OUT days INT)
BEGIN
    -- 声明一个局部变量来保存天数差
    DECLARE day_difference INT;

    -- 计算两个日期之间的天数差异,并将其存储到局部变量中
    SET day_difference = DATEDIFF(end_date, start_date);

    -- 将计算的结果赋值给输出参数
    SET days = day_difference;

    -- 可选:添加一些额外的逻辑,比如检查是否为负数(如果开始日期大于结束日期)
    IF day_difference < 0 THEN
        SET days = -day_difference;
    END IF;
END$$

DELIMITER ;

在这个例子中:

  • DECLARE day_difference INT; 定义了一个名为 day_difference 的局部变量,用来存储两个日期之间的天数。
  • SET day_difference = DATEDIFF(end_date, start_date); 使用 DATEDIFF 函数计算两个日期之间的天数,并将结果存储到 day_difference 变量中。
  • SET days = day_difference; 将局部变量中的值赋给输出参数 days
  • IF day_difference < 0 THEN ... END IF; 添加了一段逻辑,确保即使开始日期晚于结束日期,返回的天数也是正数。
调用存储过程

要调用这个存储过程并获取结果,你可以这样做:

-- 声明一个变量来接收输出参数
SET @days_between = 0;

-- 调用存储过程,并传递输入参数以及输出参数的引用
CALL CalculateDaysBetweenDates('2024-01-01', '2024-12-31', @days_between);

-- 查询输出参数的值
SELECT @days_between AS days;

这段代码将会调用 CalculateDaysBetweenDates 存储过程,并传入起始日期 '2024-01-01' 和结束日期 '2024-12-31'。最终,它会返回两个日期之间的天数,并显示出来。

注意事项

  • 局部变量的作用域:局部变量仅在声明它们的 BEGIN…END 块内有效。如果你在嵌套的 BEGIN…END 块中再次声明同名变量,则新声明的变量将覆盖外部块中的同名变量。
  • 变量命名:为了区分局部变量和表中的列名,通常会在局部变量前面加上前缀,例如 v_ 或者 var_
  • 默认值:可以为局部变量指定默认值,如 DECLARE v_name VARCHAR(50) DEFAULT 'Unknown';
  • 错误处理:可以结合使用 DECLARE CONTINUE HANDLERDECLARE EXIT HANDLER 来处理可能发生的错误。

通过这个例子,你应该能够理解如何在 MySQL 存储过程中定义和使用变量了。

4.游标的使用

在 MySQL 中,游标(Cursor)允许你逐行处理查询结果集中的每一行数据。游标通常用于存储过程或函数中,当你需要对结果集的每一行执行复杂的操作时特别有用。下面通过一个具体的示例来展示如何在 MySQL 存储过程中使用游标。

示例:更新员工工资

假设我们有一个 employees 表,其中包含员工的信息,包括他们的 ID 和当前工资。我们希望创建一个存储过程,该存储过程可以遍历所有员工,并根据某些条件(例如部门)增加他们的工资。我们将使用游标来逐行处理每个员工的数据。

创建存储过程
DELIMITER $$

CREATE PROCEDURE UpdateEmployeeSalaries(IN dept_id INT, IN increase_rate DECIMAL(5,2))
BEGIN
    -- 声明变量
    DECLARE done INT DEFAULT FALSE;
    DECLARE emp_id INT;
    DECLARE current_salary DECIMAL(10,2);

    -- 声明游标,用于遍历指定部门的所有员工
    DECLARE cur CURSOR FOR SELECT id, salary FROM employees WHERE department_id = dept_id;

    -- 声明继续处理器,当没有更多行时设置 done 为 TRUE
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    -- 打开游标
    OPEN cur;

    read_loop: LOOP
        -- 获取下一行数据并赋值给变量
        FETCH cur INTO emp_id, current_salary;

        -- 如果没有更多行,则退出循环
        IF done THEN
            LEAVE read_loop;
        END IF;

        -- 更新员工工资
        UPDATE employees 
        SET salary = current_salary + (current_salary * increase_rate)
        WHERE id = emp_id;

        -- 可选:添加日志或其他逻辑
        -- INSERT INTO salary_update_log (emp_id, old_salary, new_salary) VALUES (emp_id, current_salary, current_salary + (current_salary * increase_rate));
    END LOOP;

    -- 关闭游标
    CLOSE cur;
END$$

DELIMITER ;

在这个例子中:

  • 声明变量:定义了几个局部变量来保存从游标中获取的数据以及控制循环结束的标志。
  • 声明游标:使用 DECLARE CURSOR 语句定义了一个名为 cur 的游标,它将遍历 employees 表中 department_id 等于输入参数 dept_id 的所有记录。
  • 声明处理器:使用 DECLARE CONTINUE HANDLER 定义了一个处理器,当游标遇到 NOT FOUND 条件(即没有更多行可读取)时,将 done 设置为 TRUE
  • 打开游标:使用 OPEN 语句打开游标以开始遍历结果集。
  • 循环读取数据:使用 FETCH 语句从游标中读取一行数据到局部变量中,并检查是否已经到达结果集末尾。如果没有,则执行更新操作。
  • 关闭游标:使用 CLOSE 语句关闭游标以释放资源。
调用存储过程

要调用这个存储过程并更新特定部门员工的工资,你可以这样做:

-- 调用存储过程,传入部门ID和加薪比例
CALL UpdateEmployeeSalaries(1, 0.10);

这段代码将会调用 UpdateEmployeeSalaries 存储过程,并传入部门 ID 1 和加薪比例 10%。最终,它会遍历所有属于部门 1 的员工,并将他们的工资提高 10%

注意事项

  • 性能考虑:虽然游标提供了一种灵活的方式来逐行处理数据,但它们可能不如批量更新高效。因此,在选择使用游标之前,请评估是否有更高效的方法实现相同的目标。
  • 锁定问题:使用游标时,可能会导致长时间持有表锁,特别是在大表上。这可能会影响并发性。确保你的应用能够容忍这种锁定行为。
  • 错误处理:可以通过声明多个处理器来增强错误处理能力,确保即使发生异常也能正确关闭游标并清理资源。
  • 事务管理:如果需要保证原子性,可以在存储过程中显式地管理事务,使用 START TRANSACTION, COMMIT, 和 ROLLBACK 语句。

通过这个例子,你应该能够理解如何在 MySQL 存储过程中定义和使用游标了。

5.流程控制

在 MySQL 存储过程中,流程控制语句允许你根据条件或循环结构来执行不同的逻辑。这使得存储过程更加灵活和强大,可以处理复杂的业务逻辑。以下是几种常见的流程控制语句及其用法示例:IF...THEN...ELSE, CASE, LOOP, WHILE, 和 REPEAT

1. IF…THEN…ELSE

IF...THEN...ELSE 语句用于基于条件判断执行不同的代码块。它支持简单的 IF、带 ELSEIF 以及嵌套的 IF...ELSEIF...ELSE 结构。

示例:根据员工绩效调整工资
-- 更改语句结束符为 $$,以便在存储过程中使用分号作为语句结束符
DELIMITER $$

-- 创建名为 AdjustSalary 的存储过程
-- 参数:
--   IN emp_id INT: 输入参数,表示要调整工资的员工ID
--   IN performance_score INT: 输入参数,表示员工的绩效评分
-- 目标:根据员工的绩效评分调整其工资
CREATE PROCEDURE AdjustSalary(IN emp_id INT, IN performance_score INT)
BEGIN
    -- 声明一个局部变量 current_salary 来保存当前工资
    DECLARE current_salary DECIMAL(10,2);

    -- 从 employees 表中查询指定员工的当前工资,并将其赋值给 current_salary 变量
    -- 注意:确保 'id' 是唯一标识员工的主键,以保证查询结果的准确性
    SELECT salary INTO current_salary FROM employees WHERE id = emp_id;

    -- 使用 IF...THEN...ELSE 进行条件判断,根据不同的绩效评分调整工资
    -- 绩效评分越高,工资增加的比例越大
    IF performance_score >= 90 THEN
        -- 如果绩效评分大于等于 90,则将工资增加 20%
        UPDATE employees SET salary = current_salary * 1.2 WHERE id = emp_id;
    ELSEIF performance_score >= 75 THEN
        -- 如果绩效评分介于 75 和 89 之间,则将工资增加 10%
        UPDATE employees SET salary = current_salary * 1.1 WHERE id = emp_id;
    ELSE
        -- 如果绩效评分低于 75,则将工资增加 5%
        UPDATE employees SET salary = current_salary * 1.05 WHERE id = emp_id;
    END IF;

END$$

-- 恢复默认的语句结束符为分号 ;
DELIMITER ;

-- 注释:存储过程 AdjustSalary 创建完成。
-- 调用方法:CALL AdjustSalary(员工ID, 绩效评分);
-- 例如:CALL AdjustSalary(1, 92); 将会根据绩效评分 92 更新员工 ID 为 1 的工资。

在这个例子中,我们根据员工的绩效分数(performance_score)来决定他们工资的增长比例。

2. CASE

CASE 语句提供了更简洁的方式来实现多条件分支逻辑。它可以有简单形式和搜索形式两种。

示例:根据部门名称获取部门ID
-- 更改语句结束符为 $$,以便在存储过程中使用分号作为语句结束符
DELIMITER $$

-- 创建名为 GetDepartmentIdByName 的存储过程
-- 参数:
--   IN dept_name VARCHAR(100): 输入参数,表示要查询的部门名称
--   OUT dept_id INT: 输出参数,用于返回对应的部门ID
-- 目标:根据提供的部门名称返回相应的部门ID
CREATE PROCEDURE GetDepartmentIdByName(IN dept_name VARCHAR(100), OUT dept_id INT)
BEGIN
    -- 使用 CASE 语句进行多条件判断,根据输入的部门名称设置输出参数 dept_id
    -- 注意:这里的映射关系是硬编码的,实际应用中应考虑从数据库表中动态获取部门信息
    CASE dept_name
        WHEN 'Sales' THEN 
            -- 如果部门名称为 'Sales',则设置 dept_id 为 1
            SET dept_id = 1;
        WHEN 'Engineering' THEN 
            -- 如果部门名称为 'Engineering',则设置 dept_id 为 2
            SET dept_id = 2;
        WHEN 'HR' THEN 
            -- 如果部门名称为 'HR',则设置 dept_id 为 3
            SET dept_id = 3;
        ELSE 
            -- 默认情况下,如果未找到匹配的部门名称,则设置 dept_id 为 0
            SET dept_id = 0; -- 默认值表示未找到匹配的部门
    END CASE;
END$$

-- 恢复默认的语句结束符为分号 ;
DELIMITER ;

-- 注释:存储过程 GetDepartmentIdByName 创建完成。
-- 调用方法:CALL GetDepartmentIdByName('部门名称', @dept_id);
-- 例如:CALL GetDepartmentIdByName('Sales', @dept_id); 然后 SELECT @dept_id; 将会返回部门 ID 1。

或者使用搜索形式的 CASE

DELIMITER $$

CREATE PROCEDURE GetDepartmentIdByName(IN dept_name VARCHAR(100), OUT dept_id INT)
BEGIN
    -- 使用搜索形式的 CASE 语句
    SET dept_id = CASE 
        WHEN dept_name = 'Sales' THEN 1
        WHEN dept_name = 'Engineering' THEN 2
        WHEN dept_name = 'HR' THEN 3
        ELSE 0 -- 默认值表示未找到匹配的部门
    END;
END$$

DELIMITER ;

3. LOOP

LOOP 语句创建一个无限循环,直到遇到 LEAVEITERATE 语句才会退出或继续下一次迭代。

示例:遍历并打印数字
-- 更改语句结束符为 $$,以便在存储过程中使用分号作为语句结束符
DELIMITER $$

-- 创建名为 PrintNumbers 的存储过程
-- 目标:打印从 1 到 10 的数字序列
CREATE PROCEDURE PrintNumbers()
BEGIN
    -- 声明一个局部变量 i 并初始化为 1,用作计数器
    DECLARE i INT DEFAULT 1;

    -- 开始一个名为 print_loop 的无限循环
    print_loop: LOOP
        -- 打印当前的数字 i
        SELECT i AS number;

        -- 增加计数器 i 的值
        SET i = i + 1;

        -- 如果计数器 i 超过 10,则退出循环
        IF i > 10 THEN
            LEAVE print_loop;
        END IF;
    END LOOP;

    -- 结束存储过程体
END$$

-- 恢复默认的语句结束符为分号 ;
DELIMITER ;

-- 注释:存储过程 PrintNumbers 创建完成。
-- 调用方法:CALL PrintNumbers();
-- 该存储过程将依次打印数字 1 到 10。

4. WHILE

WHILE 语句在满足特定条件时重复执行代码块,类似于其他编程语言中的 while 循环。

示例:计算阶乘
-- 更改语句结束符为 $$,以便在存储过程中使用分号作为语句结束符
DELIMITER $$

-- 创建名为 CalculateFactorial 的存储过程
-- 参数:
--   IN n INT: 输入参数,表示要计算阶乘的正整数
--   OUT result BIGINT: 输出参数,用于返回 n 的阶乘结果
-- 目标:计算并返回给定正整数 n 的阶乘(n!)
CREATE PROCEDURE CalculateFactorial(IN n INT, OUT result BIGINT)
BEGIN
    -- 声明一个局部变量 i 并初始化为 1,用作循环计数器
    DECLARE i INT DEFAULT 1;

    -- 初始化输出参数 result 为 1,因为任何数的 0! 和 1! 都等于 1
    SET result = 1;

    -- 开始一个名为 factorial_loop 的 WHILE 循环,条件是 i 小于或等于输入参数 n
    factorial_loop: WHILE i <= n DO
        -- 将当前的 result 与 i 相乘,并更新 result 的值
        SET result = result * i;

        -- 增加计数器 i 的值
        SET i = i + 1;
    END WHILE;

    -- 结束存储过程体
END$$

-- 恢复默认的语句结束符为分号 ;
DELIMITER ;

-- 注释:存储过程 CalculateFactorial 创建完成。
-- 调用方法:CALL CalculateFactorial(n, @result);
-- 例如:CALL CalculateFactorial(5, @result); 然后 SELECT @result; 将会返回 5 的阶乘,即 120。

5. REPEAT

REPEAT 语句会先执行代码块,然后检查条件是否为真,如果为真则退出循环;否则继续执行。这与 WHILE 的行为相反。

示例:寻找符合条件的第一个员工
-- 更改语句结束符为 $$,以便在存储过程中使用分号作为语句结束符
DELIMITER $$

-- 创建名为 FindFirstQualifiedEmployee 的存储过程
-- 参数:
--   IN min_salary DECIMAL(10,2): 输入参数,表示最低工资要求
--   OUT emp_id INT: 输出参数,用于返回符合条件的第一个员工ID
-- 目标:查找第一个工资不低于给定最低工资要求的员工,并返回其ID
CREATE PROCEDURE FindFirstQualifiedEmployee(IN min_salary DECIMAL(10,2), OUT emp_id INT)
BEGIN
    -- 声明局部变量 done 作为循环结束标志,默认值为 FALSE
    DECLARE done INT DEFAULT FALSE;

    -- 声明局部变量 cur_emp_id 和 cur_salary 分别保存当前遍历到的员工ID和工资
    DECLARE cur_emp_id INT;
    DECLARE cur_salary DECIMAL(10,2);

    -- 定义游标 cur,用于遍历 employees 表中的所有记录,并按 id 排序
    DECLARE cur CURSOR FOR SELECT id, salary FROM employees ORDER BY id;

    -- 声明一个继续处理器,当游标遇到 NOT FOUND 条件(即没有更多行)时,设置 done 为 TRUE
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    -- 打开游标,开始遍历数据
    OPEN cur;

    -- 开始 REPEAT 循环,直到 done 变量为 TRUE
    find_loop: REPEAT
        -- 从游标中获取下一行数据,并赋值给 cur_emp_id 和 cur_salary 变量
        FETCH cur INTO cur_emp_id, cur_salary;

        -- 如果找到了符合条件的员工(工资不低于 min_salary),则设置输出参数 emp_id 并退出循环
        IF NOT done AND cur_salary >= min_salary THEN
            SET emp_id = cur_emp_id;
            LEAVE find_loop;
        END IF;

    -- 循环条件:直到 done 为 TRUE(即游标遍历完毕)
    UNTIL done END REPEAT;

    -- 关闭游标,释放资源
    CLOSE cur;
END$$

-- 恢复默认的语句结束符为分号 ;
DELIMITER ;

-- 注释:存储过程 FindFirstQualifiedEmployee 创建完成。
-- 调用方法:CALL FindFirstQualifiedEmployee(最低工资, @emp_id);
-- 例如:CALL FindFirstQualifiedEmployee(50000.00, @emp_id); 然后 SELECT @emp_id; 将会返回第一个工资不低于 50000.00 的员工ID。

在这个例子中,我们使用 REPEAT 循环配合游标来查找第一个符合最低工资要求的员工。

总结

通过这些例子,你应该能够理解如何在 MySQL 存储过程中使用各种流程控制语句了。每种语句都有其适用场景,合理选择和组合它们可以帮助你编写出功能丰富且易于维护的存储过程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2260735.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

泷羽sec学习打卡-brupsuite7搭建IP炮台

声明 学习视频来自B站UP主 泷羽sec,如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都 与本人无关,切莫逾越法律红线,否则后果自负 关于brupsuite的那些事儿-Brup-IP炮台搭建 搭建炮台服务端安装zmap1、更新系统和安装基础依赖&#xff…

自适应卡尔曼滤波(包括EKF、UKF、CKF等)的创新思路——该调什么、不该调什么

在调节自适应卡尔曼滤波时&#xff0c;需要注意的参数和矩阵都对滤波器的性能有直接影响。本文给出详细的说明&#xff0c;包括相关公式和 MATLAB 代码示例 文章目录 需要调节的参数1. **过程噪声协方差矩阵 Q Q Q**&#xff1a;2. **测量噪声协方差矩阵 R R R**&#xff1a;…

【C语言】浮点数的原理、整型如何转换成浮点数

众所周知C语言中浮点数占四个字节&#xff0c;无论在32位或者64位机器上。不免会发出疑问四个字节是怎么计算出小数的呢&#xff1f;其实物理存放还是按照整型存放的。 IEEE 754 单精度浮点数格式 浮点数在计算机中是使用 IEEE 754 标准进行表示的。在 IEEE 754 标准中&#…

深入理解addEventListener中的第二个参数:listener

起因 首先&#xff0c;之前留给我们的一点东西&#xff0c;js的深入内容关键在addEventListener&#xff0c;这个函数中的参数&#xff0c;它们三个参数&#xff0c;分别为type、listener、options&#xff0c;当然在这里还有一些小的问题&#xff0c;比如mdn文档中它介绍到了另…

【密码学】ZUC祖冲之算法

一、ZUC算法简介 ZUC算法&#xff08;祖冲之算法&#xff09;是中国自主研发的一种流密码算法&#xff0c;2011年被3GPP批准成为4G国际标准&#xff0c;主要用于无线通信的加密和完整性保护。ZUC算法在逻辑上采用三层结构设计&#xff0c;包括线性反馈移位寄存器&#xff08;L…

详解下c语言下的多维数组和指针数组

在实际c语言编程中&#xff0c;三维及以上数组我们使用的很少&#xff0c;二维数组我们使用得较多。说到数组&#xff0c;又不得关联到指针&#xff0c;因为他们两者的联系太紧密了。今天我们就详细介绍下c语言下的多维数组(主要是介绍二维数组)和指针。 一、二维数组 1.1&am…

EXCEL的各种图形,统计图形

目录 0 EXCEL的各种图形&#xff0c;统计图形 1 统计图形 / 直方图 / 其实叫 频度图 hist最合适(用原始数据直接作图) 1.1 什么是频度图 1.2 如何创建频度图&#xff0c;一般是只选中1列数据&#xff08;1个数组&#xff09; 1.3 如何修改频度图的宽度 1.4 hist图的一个特…

npm内存溢出

项目过大运行项目内存溢出 报错代码 运行内存溢出 increase-memory-limit ‘“node --max-old-space-size8192”’ 不是内部或外部命令&#xff0c;也不是可运行的程序 FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of m…

快速部署一套K8s集群-v1.28

快速部署一套K8s集群-v1.28 1.前置知识点 1.1 生产环境可部署Kubernetes集群的两种方式 目前生产部署Kubernetes集群主要有两种方式: kubeadmKubeadm是一个K8s部署工具,提供kubeadm init和kubeadm join,用于快速部署Kubernetes集群。 二进制包从github下载发行版的二进…

Y3编辑器教程5:触发器进阶使用(镜头、UI、表格、函数库、排行榜、游戏不同步)

文章目录 一、游戏声音设计二、 游戏镜头设计2.1 镜头的基本参数2.2 镜头时间轴动画 三、界面编辑3.1 界面编辑器设置3.2 添加按钮事件3.3 触发编写 四、 表格编辑器&#xff08;实现对话UI&#xff09;4.1 一维表和多维表4.2 数据验证、搜索、保存与撤销4.3 Excel导入导出4.4 …

vue3实现页签

功能点&#xff1a; 新增和删除页签拖拽页签 需要引入插件"vue-draggable-plus": "^0.6.0", 代码已注释右键弹框操作页签左右点击滚动页签和鼠标滑轮滚动页签 注意点 useStore涉及的部分是pina的缓存&#xff0c;需要改成自己的&#xff1b;userStore.tab…

ARCGIS国土超级工具集1.2更新说明

ARCGIS国土超级工具集V1.2版本&#xff0c;功能已增加至47 个。在V1.1的基础上修复了若干使用时发现的BUG&#xff0c;新增了"矢量分割工具"菜单&#xff0c;同时增加及更新了了若干功能&#xff0c;新工具使用说明如下&#xff1a; 一、勘测定界工具栏更新界址点成果…

element-ui实现table表格的嵌套(table表格嵌套)功能实现

最近在做电商类型的官网&#xff0c;希望实现的布局如下&#xff1a;有表头和表身&#xff0c;所以我首先想到的就是table表格组件。 表格组件中常见的就是&#xff1a;标题和内容一一对应&#xff1a; 像效果图中的效果&#xff0c;只用基础的表格布局是不行的&#xff0c;因…

图像分割数据集石头rock分割数据集labelme格式2602张3类别

数据集格式&#xff1a;labelme格式(不包含mask文件&#xff0c;仅仅包含jpg图片和对应的json文件) 图片数量(jpg文件个数)&#xff1a;2602 标注数量(json文件个数)&#xff1a;2602 标注类别数&#xff1a;3 标注类别名称:["claystone","silt","…

语音芯片赋能可穿戴设备:开启个性化音频新体验

在科技日新月异的今天&#xff0c;语音芯片与可穿戴设备的携手合作&#xff0c;正引领我们步入一个前所未有的个性化音频时代。这一创新融合&#xff0c;用户可以享受到更加个性化、沉浸式的音频体验。下面将详细介绍语音芯片与可穿戴设备合作的优点和具体应用。 1. 定制化音效…

数据挖掘之聚类分析

聚类分析&#xff08;Clustering Analysis&#xff09; 是数据挖掘中的一项重要技术&#xff0c;旨在根据对象间的相似性或差异性&#xff0c;将对象分为若干组&#xff08;簇&#xff09;。同一簇内的对象相似性较高&#xff0c;而不同簇间的对象差异性较大。聚类分析广泛应用…

【iOS】OC高级编程 iOS多线程与内存管理阅读笔记——自动引用计数(四)

目录 ARC规则 规则 对象型变量不能作为C语言结构体的成员 显式转换id和void* 属性 数组 ARC规则 规则 在ARC有效的情况下编译源代码必须遵守一定的规则&#xff1a; 主要解释一下最后两条 对象型变量不能作为C语言结构体的成员 要把对象型变量加入到结构体成员中时&a…

Java-25 深入浅出 Spring - 实现简易Ioc-01 Servlet介绍 基本代码编写

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 大数据篇正在更新&#xff01;https://blog.csdn.net/w776341482/category_12713819.html 目前已经更新到了&#xff1a; MyBatis&#xff…

微服务SpringCloud链路追踪之Micrometer+Zipkin

视频教程&#xff1a; https://www.bilibili.com/video/BV12LBFYjEvR 效果演示 当我们发送一个请求给 Gateway 的时候&#xff0c;由 Micrometer trace 进行链路追踪和数据收集&#xff0c;由 Zipkin 进行数据展示。可以清楚的看到微服务的调用过程&#xff0c;以及每个微服务…

【Java】Iterator迭代器相关API

Iterator 是 Java 集合框架中用于遍历集合&#xff08;List、Set 等&#xff09;的工具&#xff0c;它提供了访问集合中每个元素的统一接口&#xff0c;避免直接操作集合的实现细节。 Iterator的基本使用和方法 基本方法 hasNext()&#xff1a;检查是否还有元素可供迭代。ne…