MySQL 存储函数:数据库的自定义函数

  在数据库开发中,存储函数(Stored Function)是一种非常有用的工具。它允许我们创建自定义的函数,这些函数可以在 SQL 查询中像内置函数一样使用,用于实现特定的逻辑和计算。本文将深入探讨 MySQL 存储函数的概念、与存储过程的区别、语法、以及实际应用,帮助你更好地利用存储函数扩展 MySQL 的功能。

一、什么是存储函数?

  存储函数是一组预编译的SQL语句,它们被保存在数据库中,并且可以通过调用该函数来执行这些语句。与存储过程不同的是,存储函数必须返回一个单一的结果值。这使得它们非常适合用于执行复杂的计算或查询,并将结果返回给调用者。与存储过程类似,存储函数也具有以下特点:

  • 预编译: 存储函数在创建时会被编译成可执行代码,这使得存储函数的执行速度比普通的 SQL 语句更快。
  • 存储在数据库服务器:存储函数代码存储在数据库服务器端,避免了客户端和服务器之间传输大量的 SQL 语句,减少了网络开销。
  • 通过名称调用:存储函数可以通过名称来调用,方便代码的复用。
  • 返回值: 存储函数必须返回一个值, 可以在 SQL 查询语句中使用。
  • 模块化:存储函数可以实现代码的模块化,提高代码的可维护性。
  • 权限控制: 可以通过数据库的权限机制来限制存储函数的访问权限。

二、存储函数与存储过程的区别

特性存储过程 (Stored Procedure)存储函数 (Stored Function)
主要目的执行一系列 SQL 语句,完成特定操作执行计算或数据处理,返回一个值
返回值可以有多个输出参数或无返回值必须返回一个值
调用方式使用 CALL 语句在 SQL 查询语句中使用,像内置函数一样
使用场景适用于复杂业务逻辑、数据操作适用于数据计算、格式化、验证等
事务可以使用事务通常不能使用事务

三、存储函数的语法结构

3.1 创建存储函数:CREATE FUNCTION

DELIMITER //

CREATE FUNCTION 函数名 (
  [IN] 参数名 数据类型,
  [IN] 参数名 数据类型,
  ...
)
RETURNS 返回值数据类型
[函数特性]
BEGIN
  -- SQL 语句
  -- 返回值
  RETURN;
END //

DELIMITER ;
  • DELIMITER: 在 MySQL 中,默认的语句结束符号是分号(;)。当你在存储过程或触发器中编写包含多个语句的代码时,MySQL 会将每个分号视为一个语句的结束,这会导致语法错误,因为存储过程或触发器需要包含多个语句。为了解决这个问题,可以使用 DELIMITER 命令来更改语句的结束符号。上述使用 DELIMITER // 命令将语句结束符号更改为双斜线(//),在END结尾加上双斜线(//)标志着函数结尾,然后使用DELIMITER ;将结束符号改回分号(;),完成命令。
  • CREATE FUNCTION: 创建存储函数的关键字。 函数名: 存储函数的名称。
  • IN: 输入参数,存储函数需要从外部接收的参数(存储函数只支持 IN 参数, 不支持 OUT 和 INOUT 参数)。
  • RETURNS 返回值数据类型: 指定存储函数返回值的类型。
  • 函数特性: 存储函数的特性,分为如下几类。
    • DETERMINISTIC : 表示函数每次输入相同的参数都会返回相同的结果。
    • NOT DETERMINISTIC: 表示函数每次输入相同的参数,可能会返回不同的结果,例如其中使用了NOW()
    • NO SQL: 表示存储函数不读取或修改数据库中的任何数据。
    • READS SQL DATA: 表示存储函数读取数据库中的数据,但不修改数据。
    • MODIFIES SQL DATA: 表示存储函数会修改数据库中的数据。
    • SQL SECURITY DEFINER: 表示使用存储函数创建者的权限执行。
    • SQL SECURITY INVOKER: 表示存储函数以调用者 (调用存储函数的用户) 的权限执行 。
    • COMMENT 'string': 用于为存储函数添加注释,方便文档记录。
    • LANGUAGE SQL(可选): 用于声明存储函数使用 SQL 语言编写。
  • BEGIN ... END: 定义存储函数的起始和结束。
  • RETURN 值: 指定函数的返回值。

3.2 调用存储函数:

SELECT 函数名(参数1, 参数2, ...);

3.3 删除存储函数:DROP FUNCTION

DROP FUNCTION IF EXISTS 函数名;

3.4 变量声明与赋值

DECLARE v_count INT DEFAULT 0;

SET v_count = (SELECT COUNT(*) FROM employees WHERE salary > 5000);

3.5 条件判断

IF 条件 THEN
   -- SQL 语句
ELSEIF 条件 THEN
    -- SQL 语句
ELSE
  -- SQL 语句
END IF;

3.6 条件判断

WHILE 条件 DO
    -- SQL 语句
 END WHILE;

3.7 错误处理

DECLARE CONTINUE HANDLER FOR NOT FOUND
BEGIN
    -- Error handling code
    RETURN 0;  -- Return a default value in case of an error
END;

四、存储函数的示例

  计算员工奖金:假设我们需要根据员工的工作年限和绩效评分来计算他们的奖金。工作年限超过5年且绩效评分为优秀的员工将获得基本工资10%的奖金,如果没有就只能获得工资5%的奖金。我们可以编写一个存储函数来实现这一需求。
  表结构

CREATE TABLE employees (
    emp_id INT PRIMARY KEY,
    name VARCHAR(100),
    hire_date DATE,
    performance_rating ENUM('Poor', 'Average', 'Good', 'Excellent'),
    base_salary DECIMAL(10,2),
    department_id INT
);

  插入数据

INSERT INTO employees (emp_id, name, hire_date, performance_rating, base_salary, department_id) VALUES (1, 'Alice', '2015-06-01', 'Excellent', 8000.00, 1);
INSERT INTO employees (emp_id, name, hire_date, performance_rating, base_salary, department_id) VALUES (2, 'Bob', '2017-03-15', 'Good', 6500.00, 1);
INSERT INTO employees (emp_id, name, hire_date, performance_rating, base_salary, department_id) VALUES (3, 'Carol', '2019-09-22', 'Average', 5000.00, 2);
INSERT INTO employees (emp_id, name, hire_date, performance_rating, base_salary, department_id) VALUES (4, 'Dave', '2016-11-10', 'Poor', 4500.00, 2);
INSERT INTO employees (emp_id, name, hire_date, performance_rating, base_salary, department_id) VALUES (5, 'Eve', '2018-07-30', 'Good', 7000.00, 3);
INSERT INTO employees (emp_id, name, hire_date, performance_rating, base_salary, department_id) VALUES (6, 'Frank', '2020-01-15', 'Excellent', 9000.00, 3);
INSERT INTO employees (emp_id, name, hire_date, performance_rating, base_salary, department_id) VALUES (7, 'Grace', '2014-05-05', 'Excellent', 10000.00, 4);
INSERT INTO employees (emp_id, name, hire_date, performance_rating, base_salary, department_id) VALUES (8, 'Heidi', '2019-12-01', 'Average', 5500.00, 4);

  存储函数实现

CREATE FUNCTION CalculateBonus(p_emp_id INT)
RETURNS DECIMAL(10,2)
READS SQL DATA
BEGIN
    DECLARE v_years_of_service INT;
    DECLARE v_performance_rating ENUM('Poor', 'Average', 'Good', 'Excellent');
    DECLARE v_bonus DECIMAL(10,2);

    -- 获取指定员工的服务年限和绩效评分
    SELECT TIMESTAMPDIFF(YEAR, hire_date, CURDATE()), performance_rating 
    INTO v_years_of_service, v_performance_rating
    FROM employees
    WHERE emp_id = p_emp_id;

    -- 根据条件计算奖金
    IF v_years_of_service > 5 AND v_performance_rating = 'Excellent' THEN
        SELECT base_salary * 0.1 INTO v_bonus FROM employees WHERE emp_id = p_emp_id;
    ELSE
        SELECT base_salary * 0.05 INTO v_bonus FROM employees WHERE emp_id = p_emp_id;
    END IF;

    RETURN v_bonus;
END 

五、最佳实践

  • 谨慎使用存储函数:存储函数适用于简单的计算和数据处理,避免在存储函数中执行复杂的查询操作。避免使用存储函数处理事务,存储函数不能进行事务控制。
  • 保持存储函数简洁:存储函数应该只完成特定的功能,避免过于复杂。存储函数的逻辑应该尽量简单清晰,便于理解和维护。
  • 使用 DETERMINISTIC:如果存储函数的输出只依赖于输入参数,则应该使用 DETERMINISTIC 特性,这样可以提高 MySQL 查询优化器的性能。如果存储函数的输出不只依赖于输入参数, 例如使用 NOW() 等函数,则不应该使用 DETERMINISTIC 特性。
  • 良好的代码风格:使用有意义的函数名和变量名。使用缩进和注释,保持代码可读性。
  • 权限控制:应该控制存储函数的访问权限,只允许有权限的用户访问。
  • 避免副作用:存储函数应该避免产生副作用,例如修改数据库表中的数据,应该使用存储过程来完成此类操作。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

乐优商城项目总结

文章目录 项目简介微服务集群1.enreka注册中心2. zuul网关3. 公共工具类4. 商品微服务5. 文件上传微服务6. 搜索微服务7. 页面静态化微服务8. 用户微服务9. 短信微服务10. 认证微服务11. 购物车微服务12. 订单微服务项目最大的收获项目遇到的问题 项目简介 乐优商城是一个全品…

基于django的智能停车场车辆管理深度学习车牌识别系统

完整源码项目包获取→点击文章末尾名片!

【开源免费】基于Vue和SpringBoot的在线文档管理系统(附论文)

本文项目编号 T 038 ,文末自助获取源码 \color{red}{T038,文末自助获取源码} T038,文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

双层Git管理项目,github托管显示正常

双层Git管理项目,github托管显示正常 背景 在写React项目时,使用Next.js,该项目默认由git托管。但是我有在项目代码外层记笔记的习惯,我就在外层使用了git托管。 目录如下 code 层内也有.git 文件,对其托管。 我没太在意&…

54.数字翻译成字符串的可能性|Marscode AI刷题

1.题目 问题描述 小M获得了一个任务,需要将数字翻译成字符串。翻译规则是:0对应"a",1对应"b",依此类推直到25对应"z"。一个数字可能有多种翻译方法。小M需要一个程序来计算一个数字有多少种不同的…

基于Langchain-Chatchat + ChatGLM 本地部署知识库

一、相关环境 参考链接: Github:https://github.com/chatchat-space/Langchain-Chatchat Langchain-chatchat版本:v0.3.1 安装环境:Ubuntu:22.04,CUDA:12.1 二、搭建过程 2.1 环境配置 2.1.1 创建chatchat虚拟环…

Hive:日志,hql运行方式,Array,行列转换

日志 可以在终端通过 find / | grep hive-log4j2 命令查找Hive的日志配置文件 这些文件用于配置Hive的日志系统。它们不属于系统日志也不属于Job日志,而是用于配置Hive如何记录系统日志和Job日志, 可以通过hive-log4j2 查找日志的位置 HQL的3种运行方式 第1种就是l…

护眼好帮手:Windows显示器调节工具

在长时间使用电脑的过程中,显示器的亮度和色温对眼睛的舒适度有着重要影响。传统的显示器调节方式不仅操作繁琐,而且在低亮度下容易导致色彩失真。因此,今天我想为大家介绍一款适用于Windows系统的护眼工具,它可以帮助你轻松调节显…

简要介绍C语言和c++的共有变量,以及c++特有的变量

在C语言和C中,变量是用来存储数据的内存位置,它们的使用方式和特性在两种语言中既有相似之处,也有不同之处。以下分别介绍C语言和C的共有变量以及C特有的变量。 C语言和C的共有变量 C语言和C都支持以下类型的变量,它们在语法和基…

Python爬虫学习第三弹 —— Xpath 页面解析 实现无广百·度

早上好啊,大佬们。上回使用 Beautiful Soup 进行页面解析的内容是不是已经理解得十分透彻了~ 这回我们再来尝试使用另外一种页面解析,来重构上一期里写的那些代码。 讲完Xpath之后,小白兔会带大家解决上期里百度搜索的代码编写,保…

消息队列篇--通信协议篇--应用层协议和传输层协议理解

在网络通信中,传输层协议和应用层协议是OSI模型中的两个不同层次的协议,它们各自承担着不同的职责。 下文中,我们以TCP/UDP(传输层协议)和HTTP/SMTP(应用层协议)为例进行详细解释。 1、传输层协…

Maui学习笔记- SQLite简单使用案例02添加详情页

我们继续上一个案例,实现一个可以修改当前用户信息功能。 当用户点击某个信息时,跳转到信息详情页,然后可以点击编辑按钮导航到编辑页面。 创建项目 我们首先在ViewModels目录下创建UserDetailViewModel。 实现从详情信息页面导航到编辑页面…

arkui-x跨平台与android java联合开发

华为鸿蒙系统采用的是arkts,支持跨平台crossplatform 即前端为arkts,arkui-x框架,后端为其他的语言框架。 本篇示例后端采用的是java,android studio工程。 主要方式是前端鸿蒙完成界面元素、布局等效果,后面androi…

Unity敌人逻辑笔记

写ai逻辑基本上都需要状态机。因为懒得手搓状态机,所以选择直接用动画状态机当逻辑状态机用。 架构设计 因为敌人的根节点已经有一个animator控制动画,只能增加一个子节点AI,给它加一个animator指向逻辑“动画”状态机。还有一个脚本&#…

ts 基础核心

吴悠讲编程 : 20分钟学会TypeScript 无废话速成TS https://www.bilibili.com/video/BV1gX4y177Kf

BGP分解实验·11——路由聚合与条件性通告(3)

续接上(2)的实验。其拓扑如下: 路由聚合的负向也就是拆分,在有双出口的情况下,在多出口做流量分担是优选方法之一。 BGP可以根据指定来源而聚合路由,在产生该聚合路由的范围内的条目注入到本地BGP表后再向…

【leetcode】T1599

解题心得: 题目长且绕,直接看测试样例的解析有助于更快把握题目核心需求(即关注样例的输入、运算逻辑、输出) 题面 原题链接1599. 经营摩天轮的最大利润 - 力扣(LeetCode) AC代码 class Solution { pub…

Ansible自动化运维实战--通过role远程部署nginx并配置(8/8)

文章目录 1、准备工作2、创建角色结构3、编写任务4、准备配置文件(金甲模板)5、编写变量6、编写处理程序7、编写剧本8、执行剧本Playbook9、验证-游览器访问每台主机的nginx页面 在 Ansible 中,使用角色(Role)来远程部…

关于opencv环境搭建问题:由于找不到opencv_worldXXX.dll,无法执行代码,重新安装程序可能会解决此问题

方法一:利用复制黏贴方法 打开opencv文件夹目录找到\opencv\build\x64\vc15\bin 复制该目录下所有文件,找到C:\Windows\System32文件夹(注意一定是C盘)黏贴至该文件夹重新打开VS。 方法二:直接配置环境 打开opencv文…

Linux(19)——使用正则表达式匹配文本

新年快乐! 目录 一、正则表达式: 二、通过 grep 匹配正则表达式: 三、查找匹配项: 一、正则表达式: 正则表达式使用模式匹配机制查找特定内容,vim、grep 和 less 命令都可以使用正则表达式,P…