MySQL进阶:存储过程和函数

MySQL进阶:存储过程和函数

存储过程和函数

1. 简介

存储过程三大作用:

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

假设你要开发一个使用数据库的应用程序,你应该将SQL语句写在哪里呢?

如果将SQL语句内嵌在应用程序的代码里,将使其混乱且难以维护,所以应该将SQL代码和应用程序代码分开,将SQL代码储存在所属的数据库中,具体来说,是放在储存过程( )和函数中。

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

所以,储存过程很有用。

2. 创建存储过程

语法规则:

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

NOTE:

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

DELIMITER $$
CREATE PROCEDURE get_invoices_with_balance()
BEGIN
SELECT *
FROM clinet_balance
WHERE balance > 0;
END$$
DELIMITER ;

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

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

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,值2);IF函数 IF(条件表达式, 返回值1, 返回值2)IF语句

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

注意一个区别:

形参(形式参数):创建储存过程中用的占位符,如 、ument 实参(实际参数):调用时实际传入的值,如 1、3、5、NULL 参数验证

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

具体的错误类型可通过谷歌 “ error” 查阅(推荐使用IBM的那个表),这里是 ‘22 Data ’ 大类中的 ‘22003 A 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:过犹不及,加入过多的参数验证会让代码过于复杂难以维护,像 非空这样的验证就不需要添加因为 字段本身就不允许空值因此MySQL 会自动报错。参数验证工作更多的应该在应用程序端接受用户输入数据时就检测和报告,那样更快也更有效。储存过程里的参数验证只是在有人越过应用程序直接访问储存过程时作为最后的防线。这里只应该写那些最关键和必要的参数验证。

输出参数

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

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. 变量

两种变量:

用户或会话变量 SET @变量名 = ……

用 SET 语句并在变量名前加 @ 前缀来定义,将在整个用户会话期间存续,在会话结束断开MySQL连接时才被清空,这种变量主要在调用带输出变量的储存过程时使用,用来传入储存过程作为输出参数来获取结果值。本地变量 变量名 数据类型

默认值

在储存过程或函数中通过 声明并使用,在函数或储存过程执行结束时就被清空,常用来执行储存过程(或函数)中的计算。

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. 函数

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

参数设置和 body 主体之间,有一段确定返回值类型以及函数属性的语句段。最后是返回()值而不是查询()值。

关于函数属性的说明:

决定性的,唯一输入决定唯一输出,和数据的改动更新无关,比如税收是订单总额的10%,则以订单总额为输入、税收为输出的函数就是决定性的,但这里每个顾客的 会随着其发票记录的增加、更新而改变,所以不是 的READS SQL DATA 需要用到 语句进行数据读取的函数MySQL进阶:存储过程和函数,几乎所有函数都满足 SQL DATA 函数中有 增删改 或者说有 语句,这个例子不需要。

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. 其他约定

一些命名规则:

函数前加上fn前缀;存储过程加上proc前缀命名采用驼峰式:如不采用驼峰式,则采用下划线如:分隔符MySQL进阶:存储过程和函数,有人用$$,有人用//入乡随俗,遵守已经有的约定

公告:
1. 本站所有资源来源于用户上传和网络,如有侵权请联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系站长处理!
6. 本站不售卖代码,资源标价只是站长收集整理的辛苦费!如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
7. 站长QQ号码 2205675299

资源库 - 资源分享下载网 » MySQL进阶:存储过程和函数

常见问题FAQ

关于资源售价和售后服务的说明?
本站所有资源的标价均为本站收集资源的辛苦费,不代表资源本身的价值。软件是高智慧高价值的商品,不可能是白菜价。本站资源标价只是赞助费用,收取的赞助费仅用来维持本站的日常运营!毕竟收集整理资料需要投入云计算资源和站长大量的精力。
代码有没有售后服务和技术支持?
由于代码的运行具有不可预见性,本站不保证代码完整可运行,不提供技术支持和售后服务。 本站原创代码都是站长自己开发的,可以有偿提供技术支持服务。 网站里标明【亲测】的代码都是站长亲测过的,其他的代码由于精力有限,没有一一测试,不能保证代码就一定能够使用,更没有技术支持服务,下载前请自行斟酌。
有没有搭建服务?
由于搭建服务比较费时费力,所以本站除了原创代码外均不提供搭建服务。本站分享代码纯属兴趣爱好,不以盈利为目的,请勿咨询有没有搭建服务,谢谢理解。
链接地址失效了怎么办?
请带上资源链接地址联系客服,工作时间内我们看到后将第一时间回复。
关于解压密码
本站资源一般都没有加密,如果发现需要解压密码的,那么就输入 hao.35dc.com 试试。

发表评论

资源库,由老程序员细心甄别、精心筛选,只为提供优质的源码资源

关于我们 联系我们