MySQL进阶:存储过程和函数

news2024/12/23 6:58:45

存储过程和函数

  • 1. 简介
  • 2. 创建存储过程
    • 使用MySQL工作台创建存储过程
  • 3. 删除存储过程
  • 4. 参数
    • 带默认值的参数
    • 参数验证
    • 输出参数
  • 5. 变量
  • 6. 函数
  • 7. 其他约定

1. 简介

存储过程三大作用:

  1. 储存和管理SQL代码(置于数据库中,与应用层分离,同视图和函数一样,都是增加抽象层,作为模块化抽象工具)
  2. 数据安全
  3. 性能优化

假设你要开发一个使用数据库的应用程序,你应该将SQL语句写在哪里呢?
如果将SQL语句内嵌在应用程序的代码里,将使其混乱且难以维护,所以应该将SQL代码和应用程序代码分开,将SQL代码储存在所属的数据库中,具体来说,是放在储存过程(stored procedure)和函数中。

使用储存程序还有另外两个好处。首先,大部分DBMS会对储存过程中的代码进行一些优化,因此有时储存的SQL代码执行起来会更快。此外,就像视图一样,储存过程能加强数据安全。比如,我们可以移除对所有原始表的访问权限,让各种增删改的操作都通过储存过程来完成,然后就可以决定谁可以执行何种储存过程,用以限制用户对我们数据的操作范围,例如,防止特定的用户删除数据。
所以,储存过程很有用。

2. 创建存储过程

语法规则:

[DELIMITER $$]
-- delimiter 定界符;分隔符
CREATE PROCEDURE 储存过程名()
BEGIN
……;
……;
……;
END[$$]
[DELIMITER ;]

NOTE:

  1. MySQL中需要将储存过程主体内部的语句分隔符与SQL本身执行层面的语句分隔符 ; 区别开。要先用 DELIMITER(分隔符)关键字暂时将 SQL 语句的默认分隔符改为双美元符号 $ $ 或双斜杠 // 等,创建储存过程结束后再改回来。注意创建储存过程本身也是一个完整 SQL 语句,所以别忘了在 END 后要加一个暂时语句分隔符 ‘$$’。
  2. BEGIN 和 END 之间包裹的是此储存过程(PROCEDURE)的主体(body),主体里可以有多个语句,但每个语句都要以 ; 结束,包括最后一个。
  3. 调用此程序:点击闪电按钮或者用CALL关键字。大多数时候是在应用程序中调用。
DELIMITER $$
CREATE PROCEDURE get_invoices_with_balance()
BEGIN
SELECT *
FROM clinet_balance
WHERE balance > 0;
END$$
DELIMITER ;

使用MySQL工作台创建存储过程

也可以用点击的方式创造储存过程,右键选择 Create Stored Procedure,填空,Apply。这种方式Workbench会帮你处理暂时修改分隔符的问题。

3. 删除存储过程

标准模板:

DROP PROCEDURE IF EXISTS get_clients;
-- 注意加上IF EXISTS,以免因为此储存过程不存在而报错
DELIMITER $$
CREATE PROCEDURE get_clients()
BEGIN
SELECT * FROM clients;
END$$
DELIMITER ;
CALL get_clients()

Note:最好把删除和创建每一个储存过程的代码也储存在不同的SQL文件中,并把这样的文件放在Git这样的源码控制下,这样就能与其它团队成员共享Git储存库。他们就能在自己的机器上重建(复现)数据库以及该数据库下的所有的视图和储存过程参数

4. 参数

模板:

CREATE PROCEDURE 储存过程名(
参数1 数据类型,
参数2 数据类型,
……) 
BEGIN
……;
END

例子:

USE sql_invoicing;
DROP PROCEDURE IF EXISTS get_clients_by_state;
DELIMITER $$
CREATE PROCEDURE get_clients_by_state(
    state CHAR(2))   -- 一般用VARCHAR,除非可以确定字符的个数
BEGIN
    SELECT * FROM clients c   -- 采用别名的形式来引用参数,也可用前缀和后缀
    WHERE c.state = state;    -- 不能采用相同的别名来命名,如state = state
END $$
DELIMITER ;

带默认值的参数

给参数设置默认值,主要是运用条件语句块和替换空值函数。
SQL中的条件类语句:

  1. 替换空值 IFNULL(值1,值2);
  2. IF函数 IF(条件表达式, 返回值1, 返回值2)
  3. IF语句
    在这里插入图片描述
  4. CASE语句
    在这里插入图片描述
CREATE PROCEDURE get_clients_by_state( -- 尽量不用黄色箭头调用,传参数会出错。用CALL调用
    state CHAR(2))
BEGIN
    IF state IS NULL THEN
        SET state = 'CA';
    END IF;
    SELECT * FROM clients c
    WHERE c.state = state;
END

CREATE PROCEDURE get_clients_by_state(
    state CHAR(2))
BEGIN
    SELECT * FROM clients c
    WHERE c.state = IFNULL(state, c.state);
END

注意一个区别:

  1. Parameter 形参(形式参数):创建储存过程中用的占位符,如 client_id、payment_method_id
  2. Argument 实参(实际参数):调用时实际传入的值,如 1、3、5、NULL

参数验证

使用方法:储存过程除了可以查,也可以增删改,但修改数据前最好先进行参数验证以防止不合理的修改。主要利用 IF THEN条件语句和 SIGNAL SQLSTATE/MESSAGE_TEXT 关键字。

具体的错误类型可通过谷歌 “sqlstate error” 查阅(推荐使用IBM的那个表),这里是 ‘22 Data Exception’ 大类中的 ‘22003 A numeric value is out of range.’ 类型。
在这里插入图片描述

CREATE DEFINER=`root`@`localhost` PROCEDURE `make_payment`(
    invoice_id INT,
    payment_amount DECIMAL(9,2),
    /*9是精度(存储的有效位数), 2是小数位数。
    见:https://dev.mysql.com/doc/refman/8.0/en/fixed-point-types.html*/
    payment_date DATE)
BEGIN
	IF payment_amount <= 0 THEN
        SIGNAL SQLSTATE '22003'
            SET MESSAGE_TEXT = 'Invalid payment amount';
    UPDATE invoices i
    SET
        i.payment_total = payment_amount,
        i.payment_date = payment_date
    WHERE i.invoice_id = invoice_id;
END

Note:过犹不及,加入过多的参数验证会让代码过于复杂难以维护,像payment_amount 非空这样的验证就不需要添加因为 payment_amount 字段本身就不允许空值因此MySQL 会自动报错。参数验证工作更多的应该在应用程序端接受用户输入数据时就检测和报告,那样更快也更有效。储存过程里的参数验证只是在有人越过应用程序直接访问储存过程时作为最后的防线。这里只应该写那些最关键和必要的参数验证。

输出参数

输入参数是用来给储存过程传入值的,我们也可以用输出参数(变量)来获取储存程序的值.

  • 使用方法:具体是在参数的前面加上 OUT 关键字,然后再 SELECT 后加上 INTO……
    调用麻烦,如无需要,不要多此一举
CREATE DEFINER=`root`@`localhost` PROCEDURE `get_unpaid_invoices_for_client`(
    client_id INT,
    OUT invoice_count INT,
    OUT invoice_total DECIMAL(9,2))
    -- 默认是输入参数,输出参数要加OUT前缀
BEGIN
    SELECT COUNT(*),SUM(invoice_total)
    INTO invoice_count, invoice_total
    -- SELECT后跟上INTO语句将SELECT选出的值传入输出参数(输出变量)中
    FROM invoices i
    WHERE
        i.client_id = client_id AND
        payment_total = 0;
END

通过输出参数获取并读取数据有些麻烦,若无充足的原因,不要多此一举。

5. 变量

两种变量:

  1. 用户或会话变量 SET @变量名 = ……
    用 SET 语句并在变量名前加 @ 前缀来定义,将在整个用户会话期间存续,在会话结束断开MySQL连接时才被清空,这种变量主要在调用带输出变量的储存过程时使用,用来传入储存过程作为输出参数来获取结果值。
  2. 本地变量 DECLARE 变量名 数据类型 [DEFAULT 默认值]
    在储存过程或函数中通过 DECLARE 声明并使用,在函数或储存过程执行结束时就被清空,常用来执行储存过程(或函数)中的计算。
CREATE PROCEDURE  get_risk_factor ()
BEGIN
    -- 声明三个本地变量,可设默认值
    DECLARE risk_factor DECIMAL(9,2) DEFAULT 0;
    DECLARE invoices_total DECIMAL(9,2);
    DECLARE invoices_count INT;
    -- 用SELECT得到需要的值并用INTO传入变量
    SELECT SUM(invoice_total), COUNT(*)
    FROM invoices;
    -- 用SET语句给risk_factor计算赋值
    SET risk_factor = invoices_total/invoices_count * 5;
    -- 用SELECT语句将结果展示出来
    SELECT risk_factor;
END

6. 函数

函数和储存过程的作用非常相似,唯一区别是函数只能返回单一值而不能返回多行多列的结果集,当你只需要返回一个值时就可以创建函数。区别:

  1. 参数设置和 body 主体之间,有一段确定返回值类型以及函数属性的语句段。
  2. 最后是返回(RETURN)值而不是查询(SELECT)值。
  • 删除语法:DROP FUNCTION IF EXISTS。

关于函数属性的说明:

  1. DETERMINISTIC 决定性的,唯一输入决定唯一输出,和数据的改动更新无关,比如税收是订单总额的10%,则以订单总额为输入、税收为输出的函数就是决定性的,但这里每个顾客的 risk_factor 会随着其发票记录的增加、更新而改变,所以不是 DETERMINISTIC 的
  2. READS SQL DATA 需要用到 SELECT 语句进行数据读取的函数,几乎所有函数都满足
  3. MODIFIES SQL DATA 函数中有 增删改 或者说有 INSERT DELETE UPDATE 语句,这个例子不需要。
CREATE DEFINER=`root`@`localhost` FUNCTION `get_risk_factor_for_clients`(
    client_id INT) RETURNS int
READS SQL DATA
BEGIN
    DECLARE risk_factor DECIMAL(9,2) DEFAULT 0;
    DECLARE invoices_total DECIMAL(9,2);
    DECLARE invoices_count INT;
    SELECT SUM(invoice_total), COUNT(*)
    INTO invoices_total, invoices_count
    FROM invoices i
    WHERE i.client_id = client_id;
    SET risk_factor = invoices_total/invoices_count * 5;
    RETURN IFNULL(risk_factor, 0);
END

7. 其他约定

一些命名规则:

  1. 函数前加上fn前缀;
  2. 存储过程加上proc前缀
  3. 命名采用驼峰式:如procGetRiskFactor
  4. 不采用驼峰式,则采用下划线如:get_risk_factor
  5. 分隔符,有人用$$,有人用//
  6. 入乡随俗,遵守已经有的约定

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

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

相关文章

Redis集群-主从复制、哨兵

●主从复制&#xff1a;主从复制是高可用Redis的基础&#xff0c;哨兵和集群都是在主从复制基础上实现高可用的。 主从复制主要实现了数据的多机备份&#xff0c;以及对于读操作的负载均衡和简单的故障恢复。缺陷&#xff1a;故障 恢复无法自动化&#xff1b;写操作无法负载均…

小白学习手册:轻松理解MQ消息队列

目录 # 开篇 RabbitMQ介绍 通讯概念 1. 初始MQ及类型 2. MQ的架构 2.1 RabbitMQ的结构和概念 2.2 RabbitMQ消息流示意图 3. MQ下载使用 3.1 Docker下载MQ参考 3.2 进入RabbitMQ # 开篇 MessagesQueue 是一个抽象概念&#xff0c;用于描述消息队列系统的一般特性和功能…

Ubuntu(通用)—网络加固—ufw+防DNS污染+ARP绑定

1. ufw sudo ufw default deny incoming sudo ufw deny in from any to any # sudo ufw allow from any to any port 5353 protocol udp sudo ufw enable # 启动开机自启 # sudo ufw reload 更改后的操作2. 防ARP欺骗 华为云教程 arp -d删除dns记录arp -a显示arp表 ipconfi…

windows USB设备驱动开发-双角色驱动

在USB的通讯协议中&#xff0c;规定发起连接的一方为主机(Host)&#xff0c;接受连接的一方为设备&#xff0c;这可以用U盘插入电脑举个例子&#xff0c;当U盘插入电脑后&#xff0c;电脑这边主动发起查询和枚举&#xff0c;U盘被动响应查询和数据存取。 USB 双角色驱动程序堆…

《RepViT Revisiting Mobile CNN From ViT Perspective》

期刊&#xff1a;CVPR 年份&#xff1a;2024 代码&#xff1a;http://https: //github.com/THU-MIG/RepViT 摘要 最近&#xff0c;与轻量级卷积神经网络(CNN)相比&#xff0c;轻量级视觉Transformer(ViTs)在资源受限的移动设备上表现出了更高的性能和更低的延迟。研究人员已…

大聪明教你学Java | 深入浅出聊 RocketMQ

前言 &#x1f34a;作者简介&#xff1a; 不肯过江东丶&#xff0c;一个来自二线城市的程序员&#xff0c;致力于用“猥琐”办法解决繁琐问题&#xff0c;让复杂的问题变得通俗易懂。 &#x1f34a;支持作者&#xff1a; 点赞&#x1f44d;、关注&#x1f496;、留言&#x1f4…

技术派Spring事件监听机制及原理

Spring事件监听机制是Spring框架中的一种重要技术&#xff0c;允许组件之间进行松耦合通信。通过使用事件监听机制&#xff0c;应用程序的各个组件可以在其他组件不直接引用的情况下&#xff0c;相互发送和接受消息。 需求 在技术派中有这样一个需求&#xff0c;当发布文章或…

旋转变压器软件解码simulink仿真

1.介绍 旋转变压器是一种精密的位置、速度检测装置&#xff0c;尤其适用于高温、严寒、潮湿、高速、振动等环境恶劣、旋转编码器无法正常工作的场合。旋转变压器在使用时并不能直接提供角度或位置信息&#xff0c;需要特殊的激励信号和解调、计算措施&#xff0c;才能将旋转变压…

【RT摩拳擦掌】基于RT106L/S语音识别的百度云控制系统

【RT摩拳擦掌】基于RT106L/S语音识别的百度云控制系统 一 文档简介二 平台构建2.1 使用平台2.2 百度智能云2.2.1 物联网核心套件2.2.2 在线语音合成 2.3 playback语音数据准备与烧录2.4 开机语音准备与添加2.5 唤醒词识别词命令准备与添加 三 代码准备3.1 sln-local/2-iot 代码…

2025第13届常州国际工业装备博览会招商全面启动

常州智造 装备中国|2025第13届常州国际工业装备博览会招商全面启动 2025第13届常州国际工业装备博览会将于2025年4月11-13日在常州西太湖国际博览中心盛大举行&#xff01;目前&#xff0c;各项筹备工作正稳步推进。 60000平米的超大规模、800多家国内外工业装备制造名企将云集…

Unity Shader 软粒子

Unity Shader 软粒子 前言项目Shader连连看项目渲染管线设置 鸣谢 前言 当场景有点单调的时候&#xff0c;就需要一些粒子点缀&#xff0c;此时软粒子就可以发挥作用了。 使用软粒子与未使用软粒子对比图 项目 Shader连连看 这里插播一点&#xff0c;可以用Vertex Color与…

KUKA机器人不同运行方式

KUKA机器人有以下四种运行方式&#xff1a; 1、手动慢速运行&#xff08;T1&#xff09; 2、手动快速运行&#xff08;T2&#xff09; 3、自动运行&#xff08;AUT&#xff09; 4、外部自动运行&#xff08;AUT EXT&#xff09; 将示教器上的钥匙向右旋转&#xff0c;就会…

【数据结构之B树的讲解】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

专题五:Spring源码之初始化容器上下文

上一篇我们通过如下一段基础代码作为切入点&#xff0c;最终找到核心的处理是refresh方法&#xff0c;从今天开始正式进入refresh方法的解读。 public class Main {public static void main(String[] args) {ApplicationContext context new ClassPathXmlApplicationContext(…

Study--Oracle-05-Oracler体系结构

一、oracle 体系概览 Oracle数据库的体系结构通常包括以下主要组件&#xff1a; 1、实例&#xff08;Instance&#xff09;&#xff1a;运行数据库的软件环境&#xff0c;包括内存结构&#xff08;SGA&#xff09;和进程结构&#xff08;Background Processes and User Proces…

Mysql面试合集

概念 是一个开源的关系型数据库。 数据库事务及其特性 事务&#xff1a;是一系列的数据库操作&#xff0c;是数据库应用的基本逻辑单位。 事务特性&#xff1a; &#xff08;1&#xff09;原子性&#xff1a;即不可分割性&#xff0c;事务要么全部被执行&#xff0c;要么就…

Spring Boot 实现 AOP 动态热插拔功能并附DEMO源码

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

3D Gaussian Splatting代码中的Gaussian_Module和Cameras两个类的代码解读

Gaussian_model 讨论Gaussian_model这个类&#xff0c;是因为里面包含了三维高斯分布的基本信息&#xff0c;里面定义了各种参量的构建方式、用于优化学习的激活函数、学习率设置方法和高斯点优化过程中的增加与删除方式及对应优化器的处理方法。这个类定义在scene文件夹中的g…

探索工业AI智能摄像机的高端科技

在当今快速发展的工业智能化领域&#xff0c;工业AI智能摄像机系列以其卓越的性能和多功能性在国内外备受关注&#xff08;文末有国外工程师的评测链接&#xff09;。搭载Raspberry Pi CM4支持的旨在广泛应用&#xff0c;涵盖从简单的条形码扫描到基于人工智能的工业环境中的缺…

数学知识——欧拉函数

数学知识&#xff08;二&#xff09; 20240628 求和N互质的个数公式 先分解N&#xff0c;再求个数fai n欧拉函数的证明&#xff1a;用容斥原理 不考 求质因子 p1, … , pk 1-N中与N互质的个数&#xff0c; 去掉质因子倍数 是pi的倍数的有N/pi个&#xff0c;但是会有既是p1也是…