MySQL-day05
函数、存储过程和游标
一、基本概念
1. 什么是存储过程(Stored PROCEDURE)?
- 简单的 SQL 语句不过是一些命令,用过即结束,我们尚不能把代码存储起来,像各种语言中的函数一样,在需要的时候随意调用。存储过程和函数使我们能够把软件中的逻辑部分保存起来以便日后使用。
存储过程和函数就是命名的代码块。
2. 存储过程的优势
- 更快的速度
- 程序和数据库服务器之间不再需要传递大量的 SQL 代码,只需发送一个存储过程或函数命令即可。
- 存储过程和函数是编译过的,可直接执行,无需像普通 SQL 那样,每次都需要编译。
- 减少代码冗余
- 多个应用程序访问同一个数据库是十分常见的,为雷同的功能编写一个存储过程不仅可以减少代码冗余,而且还能够使相关程序更容易维护。
- 提高数据库的安全性
- 在安全性要求较高的领域,客户程序必须使用存储过程进行所有的数据库操作,这样可以对每次数据访问进行监控,并将操作记入日志。
3. 存储过程的缺点
- 存储过程的最大缺点是:移植性差。很难讲存储过程从一个数据库移植到另一个数据库离去, 因为每个数据库的语法和语法扩展都不尽相同。
4. 案例:一个简易的函数案例
1> 定义 shorten()
函数
USE d7111;
DROP FUNCTION IF EXISTS shorten;DELIMITER $$
-- 简化字符串函数:将一个长字符串缩短
-- 如果长度小于等于 13,直接返回
-- 如果 13 < len <= 20, 截取前 5 个字符,加上 ...
-- 如果 len > 20, 截取前后 5 个字符,中间加上 ...
CREATE FUNCTION shorten(s VARCHAR(50))
RETURNS VARCHAR(50)
BEGINDECLARE len INT DEFAULT 0;SET s = TRIM(s);SET len = LENGTH(s);IF ISNULL(s) OR len = 0 THENRETURN '';ELSEIF len <= 13 THENRETURN s;ELSEIF len <= 20 THENRETURN CONCAT(LEFT(s, 5), '...');ELSERETURN CONCAT(LEFT(s, 5), '...', RIGHT(s, 5));END IF;
END;$$DELIMITER ;
说明:DELIMITER $$
命令将默认终结符改为 $$
,因为定义函数时的语句需要使用 ;
作为语句结束符,为了不让环境立即执行该语句,所以需要修改终结符。使用完可以改回去。
2> 调用 shorten()
函数
SELECT shorten('abcdefg');SELECT shorten('abcdefg hijklmn');SELECT shorten('abcdefg hijklmn opq rst uvw xyz');
二、存储过程的语法规则
1. 基本规则
- 语句必须用
;
分隔,就连分支和循环的控制结构也必须用;
结束。 BEGIN-END
:没有位于 SP 关键字之间(如 THEN 和 END IF 之间)的多条 SQL 语句必须放在关键字BEGIN
和END
之间。- 变量:供 SP 内部使用的局部变量和局部参数不加
@
前缀。在 SP 内部使用的普通 SQL 变量必须加@
前缀。变量名不要使用特殊字符。 - 注释:以
--
开始的是行注释。
2. 变量
1> 变量的分类
MySQL 中的变量分为 3 类:
- 普通变量:以
@
开头,在 MySQL 连接被关闭时失去内容 - 系统变量和服务器变量:这里变量的内容是 MySQL 服务器的工作状态或属性,它们以
@@
开头,例如:许多系统变量都有两种形式,一个对应当前连接(如@@session.wait_timeout
),另一个对应整个 MySQL 服务器(如@@global.wait_timeout
,默认值) - 存储过程里的局部变量:在存储过程内部定义,没有特殊标识,但不能和数据表和数据列重名。
2> 变量的声明
变量的声明必须放在 BEGIN-END
语句块里,并且必须在语句块的头部。
- 语法
DECLARE a, b, ... datatype [DEFAULT value];
- 必须为所有局部变量声明数据类型
- 如果没有默认值,则默认值为
NULL
。
3> 变量的赋值
-- 语法1:SET
SET a = 20;
-- 语法2:select into
SELECT 3 + 4 INTO v;
SELECT COUNT(0) FROM student INTO a;SELECT studentName, address
FROM student WHERE studentNo = 3
INTO s_name, s_address;
3. 代码块
一条以上的 SQL 语句构成的函数或过程必须以关键字 BEGIN
开头、以关键字 END
结束。此外,有时在 IF
等结构中也需要使用代码块。
[blockname :]
BEGINDECLARE variables; -- 变量声明DECLARE cursors; -- 游标声明DECLARE conditions; -- 条件声明DECLARE handler; -- 异常声明other sql commands;
END [blockname];
说明: blockname 是代码块的标号,如果起了标号,则结束时必须写做 END blockname
。标号的目的是为了使用 LEAVE blockname
命令提前退出这个语句块。
4. 分支
IF-THEN-ELSE
IF score = 100 THENSELECT '厉害'; ELSEIF score >= 90 THENSELECT '优秀'; ELSEIF score >= 80 THENSELECT '良好'; ELSEIF score >= 60 THENSELECT '及格'; ELSESELECT '不及格'; END IF;
CASE
CASE ming_ciWHEN 1 THEN SELECT '第一名';WHEN 2 THEN SELECT '第二名';WHEN 3 THEN SELECT '第三名'; ELSESELECT '无名氏'; END CASE;
5. 循环
REPEAT-UNTIL
循环[loopname:] REPEAT循环体 UNTIL 条件 END REPEAT [loopname];
说明: 当条件成立时,退出。
WHILE
循环[loopname:] WHILE 条件 DO循环体 END WHILE [loopname];
LOOP
循环loopname: LOOP循环体IF 条件 THENLEAVE loopname;END IF; END LOOP loopname;
LEAVE
和ITERATE
语句LEAVE loopname
用于在循环体内跳出循环。ITERATE loopname
用于再执行一遍循环体。
6. 出错处理句柄
- SP 里的 SQL 命令在执行过程中可能出错,所有 SQL 也像其他一些程序设计语言那样向程序员提供了一种利用 出错处理句柄(error handler) 来处理这类错误的机制。
1> 声明出错处理句柄
DECLARE type HANDLER FOR condition1, condition2,... command;
说明:
- type(类型):目前只有 CONTINUE 和 EXIT 两种:
- condition(条件):这里可列出一个或多个出错条件,他们是出错处理句柄被调用的前提。出错处理条件可以用以下几种方式给出:
- SQLSTATE ‘errorcode’:单个 SQL 出错代码,errorcode 是错误代码。
- SQLWARNING:涵盖了 SQLSTATE 编号为 01nnn 的所有错误。
- NOT FOUND:涵盖了 SQLSTATE 编号不以 01 或 02 开头的错误。
- mysqlerrorcode:MySQL 出错代码,不是 SQLSTATE 出错代码。MySQL 出错代码的完整清单和对应的 SQLSTATE 值可以在 MySQL 在线文档里查到:https://dev.mysql.com/doc/refman/8.0/en/error-handling.html
- conditionname:用 DECLARE CONDITION 命令定义出来的出错处理条件,
- command(命令):执行出错时将要执行的命令。因为这里只允许放上一条命令,所以它通常是一个变量赋值命令,该变量将在后续的出错处理代码中使用。这里必须给出一条命令。
2> 使用 出错处理句柄
DECLARE myerror VARCHAR(128) DEFAULT 'none';
-- 1. 定义条件,指代对应的错误代码
DECLARE duplicate_key CONDITION FOR SQLSTATE '23000';
-- 2. 定义错误处理句柄,用来以指定方式处理指定条件,发生错误时,执行命令
DECLARE CONTINUE HANDLER FOR duplicate_key SET myerror = 'dupkey';SELECT myerror;
INSERT INTO student VALUES (2, 'dddddd', 'abc', '男', 3, '110', '', '1991-1-1', '', '');SELECT myerror;
7. 游标 Cursor
- 游标是一个指向数据表里一条记录的指针。 通过它,我们可以一条一条的处理记录。
1. 游标的使用步骤
-- 使用游标求平均年龄
DECLARE done INT DEFAULT 0;
DECLARE age DOUBLE DEFAULT 0;
DECLARE age_sum DOUBLE DEFAULT 0;
DECLARE cnt INT DEFAULT 0;
-- 1. 定义游标
DECLARE stu_cursor CURSOR
FOR SELECT YEAR(NOW()) - YEAR(borndate) FROM student;
-- 2. 定义出错处理句柄,处理 NOT FOUND 条件
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
-- 计算总人数
SELECT COUNT(0) FROM student INTO cnt;-- 计算年龄之和
-- 3. 打开游标
OPEN stu_cursor;
sumloop: LOOPFETCH stu_cursor INTO age;-- 4. 如果找不到下一条记录,则出现 NOT FOUND 错误,从而跳出循环IF done = 1 THEN LEAVE sumloop; END IF;SET age_sum = age_sum + age;
END LOOP sumloop;
CLOSE stu_cursor; -- 关闭游标
SELECT age_sum;
SELECT cnt;
SELECT age_sum / cnt;
2. 不足与缺陷
MySQL5.0 版 游标有如下限制:
- 游标是只读的。
- 游标只能前进。
- 游标是敏感的,即:使用游标读数据时,数据表不允许发生任何变化。如果非要这么做,MySQL 服务器可能出现难以预料的行为。
三、使用存储过程
1> 如何定义存储过程
-- 创建
CREATE PROCEDURE name ([parameter-list])
[options]
sqlcode
-- 删除
DROP PROCEDURE IF EXISTS name
2> 参数与返回值
- 过程的参数分为:输入参数(IN),输出参数(OUT)和输入输出参数(INOUT)。IN 参数必须有初值。
- 函数的参数没有这些分类,函数只能进行值传递。
- 过程没有返回值,而函数有返回值。
DROP PROCEDURE IF EXISTS test_proc;DELIMITER $$
CREATE PROCEDURE test_proc(IN stu_no INT, OUT stu_name VARCHAR(50), INOUT stu_grade INT)
BEGINSELECT studentName, gradeId FROM student WHERE studentNo = stu_no AND gradeId = stu_gradeINTO stu_name, stu_grade;
END;$$DELIMITER ;SET @grade = 2;
CALL test_proc(5, @name, @grade);
SELECT @name, @grade;
3> 调用存储过程
只能用 call proc()
来调用存储过程。
如下所示:
SET @grade = 2;
CALL test_proc(5, @name, @grade);
SELECT @name, @grade;
四、使用函数
1> 如何定义函数
-- 创建
CREATE FUNCTION name ([parameter-list]) RETURNS datatype
[options]
sqlcode
-- 删除
DROP FUNCTION IF EXISTS name;
2> 调用函数
函数可嵌入 SQL 语句中,或放在表达式里。
SELECT shorten('abcdefg hijklmn opq rst uvw xyz');
五、存储过程与函数的区别
方面 | 存储过程 | 函数 |
---|---|---|
调用方式 | 只能用CALL命令调用 | 可嵌入sql命令中 |
返回值 | 可返回一个或多个查询结果 | 只能返回一个标量 |
参数 | 可使用值参和引用参数(IN(默认),OUT,INOUT) | 只能用值参 |
内部可用的命令 | 所有sql语句 | 不能使用访问表的sql命令 |
可调用内容 | 允许调用其他过程和函数 | 只能调用函数 |
语法 |
CREATE PROCEDURE name([param list]) [option] BEGIN code END |
CREATE FUNCTION name([param list]) RETURNS datatype [option] BEGIN code END |
六、应用案例
- 插入一个学生,并返回 id
DROP PROCEDURE IF EXISTS insert_stu;DELIMITER $$ CREATE PROCEDURE insert_stu(IN stu_pwd VARCHAR(50), IN stu_name VARCHAR(50), IN stu_sex CHAR(2),IN stu_gradeId INT, OUT stu_no INT) myproc: BEGINSELECT studentNo FROM student WHERE studentName = stu_name INTO stu_no;-- 有同名学生,返回IF stu_no > 0 THENLEAVE myproc;END IF;INSERT student (studentNo, loginPwd, studentName, sex, gradeId)VALUES (NULL, stu_pwd, stu_name, stu_sex, stu_gradeId);SELECT LAST_INSERT_ID() INTO stu_no; END myproc;$$DELIMITER ;
- 调用
CALL insert_stu('ddd', 'lao8', '男', 3, @stu_no); SELECT @stu_no;CALL insert_stu('ddd', '孙悟空', '男', 3, @stu_no); SELECT @stu_no; -- 2
MySQL-day05相关推荐
- MySQL Day05 子查询、函数、MD5加密、SELECT小结
1 MySQL子查询 WHERE 这个值是计算出来的 本质:在where语句中嵌套一个子查询语句 1.查询Customer Relations 的所有考试结果(学号,科目编号,成绩) -- ===== ...
- 【HM】第5课:JDBC连接MySQL数据库
<pre> day05 上节内容回顾 (1)数据库语句 *创建数据库和查看数据库的语句 *切换数据库和删除数据库语句 (2)数据库表语句 *创建数据库表的语句 *创建带约束的表 **mys ...
- 【数据库1】mysql,DDL/DML,DQL,外键约束,多表/子查询,事务,登陆,连接池,jdbc,redis,crontab,ftp,oracle,数据交换/存储/收集
文章目录 1.mysql安装:存储:集合(内存:临时),IO流(硬盘:持久化) 1.1 服务端:双击mysql-installer-community-5.6.22.0.msi 1.2 客户端:命令行 ...
- Dao跨事务调用实现转账功能
1.首先在数据库当中创建数据库,并且创建它的 实现类 package com.beiwo.epet.entity;public class Account {private int id;privat ...
- mysql数据库 day05
1.一些多表查询的练习题 2.pymysql模块 3.sql注入问题 4.增删改查 一.一些多表查询的练习题 首先用Navicat导入数据: 1 /* 2 数据导入: 3 Navicat Premiu ...
- mysql学习day05—子查询 / CASH语句 / 连接查询
子查询 单行子查询:返回单行单列 多列子查询:返回单行多列(使用成对比较) 多行子查询:返回多行单列 相关子查询:子查询使用了外部SQL的某些表或列(但是外部SQL不可使用子查询中的表和列) 嵌套子查 ...
- 【Bootstrap4前端框架+MySQL数据库】前后端综合实训【10天课程 博客汇总表 详细笔记】【附:所有代码】
目 录 日常要求.项目要求 用到的软件版本情况说明 上课时的所有代码.用到的软件安装包 实训第2周--前后端"新闻管理系统"工程所有文件(MySQL语句+eclipse项目) ...
- day05【后台】菜单维护
day05[后台]菜单维护 1.数据库中存储树形结构 1.1.节点类型 1.2.创建菜单表 执行SQL语句创建数据库表 USE project_crowd CREATE TABLE t_menu (i ...
- mysql dba高级教程_MySQL DBA高级视频教程 博瑞森一线DBA大神亲授
下载服务器:五号服务器---VIP资料下载八区\数据库专区 游客,如果您要查看本帖隐藏内容请回复 MySQL DBA高级视频教程 这个是我参与培训的,分享给大家学习,真正一线DBA大神亲授 全程高清, ...
- 博瑞森mysql培训_MySQL DBA高级视频教程 博瑞森一线DBA大神亲授
下载服务器:五号服务器---VIP资料下载八区\数据库专区 游客,如果您要查看本帖隐藏内容请回复 MySQL DBA高级视频教程 这个是我参与培训的,分享给大家学习,真正一线DBA大神亲授 全程高清, ...
最新文章
- OPENCV-PYTHON将.GIF格式的图像转为PNG格式
- Educational Codeforces Round 103 (Rated for Div. 2) D. Journey dp
- java字符流写入式乱码_字节流乱码与字符流乱码
- 谷歌看下!罗永浩谈谷歌砍掉平板线 :主要是因为软件太烂
- 含有百分数的简便运算_青岛版三年级上册数学6.1不含括号的混合运算(一)微课知识点精讲+练习...
- 剑指offer【书】之简历抒写
- 深入掌握JMS(五):实战Topic 1
- 省市级联实现,并根据IP自动获取省市
- 黑马程序员---初学java建议(亲身经历)
- pycharm 弹出的Safe Delete安全 删除功能是什么?
- 常见网络编程面试题答案征集与面试题(收集) ZZ 【网络编程】
- DELL戴尔笔记本关闭触摸板触控板WIN10
- mysql数据库的封装
- uniapp获取视频第一帧展示,及视频的层级问题,亲测有效
- Vue实现动态复选框的全选 全不选 获取选中值
- python操作Excel设置打印标题时碰到的一个小问题
- STM32L031 HAL库读写内部EEprom
- java线程池的参数设置
- android 平板 办公,技德Remix超级平板如何让安卓系统成为办公神器
- FFmpeg系列(二)—— 音视频裸流转换:mp3转pcm、h264转YUV
热门文章
- 2017第五届南京国际佛事文化用品展览会会刊(参展商名录)
- php 随机播放音乐,听音乐时随机播放真的是随机的吗?
- 【C语言初阶】C语言%(%d,%c...)
- 苹果手机微信分身怎么弄
- Django计算机毕业设计网上商城比价系统python(源码程序+lw+远程部署)
- css3动画之太极图
- AJAX —— onreadystatechange 事件
- OpenSUSE 15.2 切换国内软件源
- 使用 UML 服务组件表示 SOA 体系结构模式
- 什么?接口测试?那是什么东西?