JavaWeb基础入门——(二)MySQL数据库基础(5-存储过程)

八、存储过程

8.1 存储过程介绍

8.1.1 SQL指令执行过程

在这里插入图片描述

从SQL执行执行的流程中我们分析存在的问题:

  1. 如果我们需要重复多次执行相同的SQL,SQL指令都需要通过连接传递到MySQL,并且需要经过编译和执行的步骤;

  2. 如果我们需要连续执行多个SQL指令,并且第二个SQL指令需要使用第一个SQL指令执行的结果作为参数;

8.1.2 存储过程的介绍

在这里插入图片描述

存储过程:

将能够完成特定功能的SQL指令进行封装(SQL指令集),编译之后存储在数据库服务器上,并且为之取一个名字,客户端可以通过名字直接调用这个SQL指令集,获取执行结果。

8.1.3 存储过程优缺点分析

存储过程优点:

  1. SQL指令无需客户端编写,通过网络传送,可以节省网络开销,同时避免SQL指令在网络传输过程中被恶意篡改保证安全性;

  2. 存储过程经过编译创建并保存在数据库中的,执行过程无需重复的进行编译操作,对SQL指令的执行过程进行了性能提升;

  3. 存储过程中多个SQL指令之间存在逻辑关系,支持流程控制语句(分支、循环),可以实现更为复杂的业务;

存储过程的缺点:

  1. 存储过程是根据不同的数据库进行编译、创建并存储在数据库中;当我们需要切换到其他的数据库产品时,需要重写编写针对于新数据库的存储过程;

  2. 存储过程受限于数据库产品,如果需要高性能的优化会成为一个问题;

  3. 在互联网项目中,如果需要数据库的高(连接)并发访问,使用存储过程会增加数据库的连接执行时间(因为我们将复杂的业务交给了数据库进行处理)

8.2 创建存储过程

8.2.1 存储过程创建语法

-- 语法[为参数部分,与java类似,可以定义参数,也可以不定义参数]:
create procedure <proc_name>([IN/OUT args])
begin-- SQL
end;

8.2.2 示例

-- 创建一个存储过程实现加法运算: Java语法中,方法是有参数和返回值的
-- 存储过程中,是有输入参数 和 输出参数的
create procedure proc_test1(IN a int,IN b int,OUT c int)
begin-- set代表的是定义变量的意思SET c = a+b;
end;-- 调用存储过程
-- 定义变量@m
set @m = 0;
-- 调用存储过程,将3传递给a,将2传递给b,将@m传递给c
call proc_test1(3,2,@m);
-- 显示变量值(dual系统表,无需创建,定义变量的值都会在这里)
select @m from dual;

8.3 存储过程中变量的使用

存储过程中的变量分为两种:局部变量 和 用户变量

8.3.1 定义局部变量

局部变量:定义在存储过程中的变量,只能在存储过程内部使用

  • 局部变量定义语法
-- 局部变量要定义在存储过程中,而且必须定义在存储过程开始
declare <attr_name> <type> [default value];
  • 局部变量定义示例
create procedure proc_test2(IN a int,OUT r int)
begindeclare x int default 0; -- 定义x int类型,默认值为0declare y int default 1; -- 定义yset x = a*a;set y = a/2;set r = x+y;
end;

8.3.2 定义用户变量l

用户变量:相当于全局变量,定义的用户变量可以通过 select @altreName from dual 进行查询

-- 用户变量会存储在mysql数据库的数据字典中(dual)
-- 用户变量定义使用set关键字直接定义,变量名要以@开头
set @n=1;

8.3.3 给变量设置值

无论是局部变量还是用户变量,都是使用 set 关键字修改值

-- 查询学生数量
-- 注意在储存过程中使用SQL语句需要将结果赋值给变量,那么就需要使用into关键字来进行赋值
create procedure proc_test3(OUT c int)
beginselect count(stu_num) INTO c from students; -- 将查询到学生数量赋值给c
end;
-- 调用存储过程
call proc_test3(@n);
select @n from dual;

8.3.4 用户变量使用注意事项

因为用户变量相当于全局变量,可以在SQL指令以及多个存储过程中共享,在开发中建议尽量少使用用户变量,用户变量过多会导致程序不易理解、难以维护。

8.4 存储过程的参数

MySQL存储过程的参数一共有三种:IN \ OUT \ INOUT

8.4.1 IN 输入参数

输入参数——在调用存储过程中传递数据给存储过程的参数(在调用的过程必须为具有实际值的变量 或者 字面值)

-- 创建存储过程:添加学生信息
create procedure proc_test4(IN snum char(8),IN sname varchar(20), IN gender
char(2), IN age int, IN cid int, IN remark varchar(255))
begininsert into students(stu_num,stu_name,stu_gender,stu_age,cid,remark)values(snum,sname,gender,age,cid,remark);
end;
call proc_test4('20220108','小丽','女',20,1,'aaa');

8.4.2 OUT 输出参数

输出参数——将存储过程中产生的数据返回给过程调用者,相当于Java方法的返回值,但不同的是一个存储过程可以有多个输出参数

-- 创建存储过程,根据学生学号,查询学生姓名
create procedure proc_test5(IN snum char(8),OUT sname varchar(20))
beginselect stu_name INTO sname from students where stu_num=snum;
end;
set @name='';
call proc_test5('20220107',@name);
select @name from dual;

8.4.3 INOUT 输入输出参数

注意:此方式不建议使用,一般我们输入就用 IN 输出就用OUT,此参数代码可读性低,容易混淆。

create procedure proc_test6(INOUT str varchar(20))
beginselect stu_name INTO str from students where stu_num=str;
end;
set @name='20220108';
call proc_test6(@name);
select @name from dual;

8.5 存储过程中流程控制

在存储过程中支持流程控制语句用于实现逻辑的控制

8.5.1 分支语句

  • if-then-else
-- 单分支:如果条件成立,则执行SQL
if conditions then-- SQL
end if;
-- 如果参数a的值为1,则添加一条班级信息
create procedure proc_test7(IN a int)
beginif a=1 theninsert into classes(class_name,remark)values('Java2209','test');end if;
end;
-- 双分支:如果条件成立则执行SQL1,否则执行SQL2
if conditions then-- SQL1
else-- SQL2
end if;-- 如果参数a的值为1,则添加一条班级信息;否则添加一条学生信息create procedure proc_test7(IN a int)beginif a=1 theninsert into classes(class_name,remark)values('Java2209','test');elseinsert intostudents(stu_num,stu_name,stu_gender,stu_age,cid,remark)values('20220110','小花','女',19,1,'...');end if;
end;
  • case
create procedure proc_test8(IN a int)
begincase awhen 1 then-- SQL1 如果a的值为1 则执行SQL1insert into classes(class_name,remark) values('Java2210','wahaha');when 2 then-- SQL2 如果a的值为2 则执行SQL2insert into students(stu_num,stu_name,stu_gender,stu_age,cid,remark)values('20220111','小刚','男',21,2,'...');else-- SQL (如果变量的值和所有when的值都不匹配,则执行else中的这个SQL)update students set stu_age=18 where stu_num='20220110';end case;
end;

8.5.2 循环语句

  • while

concat() 函数用于将两个字符串连接起来,形成一个单一的字符串

-- while
create procedure proc_test9(IN num int)
begindeclare i int;set i = 0;while i<num do-- SQLinsert into classes(class_name,remark) values( CONCAT('Java',i),'....');set i = i+1;end while;
end;
call proc_test9(4);
  • repeat
-- repeat
create procedure proc_test10(IN num int)
begindeclare i int;set i = 1;repeat-- SQLinsert into classes(class_name,remark) values( CONCAT('Python',i) ,'....');set i = i+1;until i > num end repeat;
end;
call proc_test10(4);
  • loop(注意如果需要停止循环,需要通过if来进行结束条件的判断)
-- loop
create procedure proc_test11(IN num int)
begindeclare i int ;set i =0;myloop:loop-- SQLinsert into classes(class_name,remark) values( CONCAT('HTML',i),'....');set i = i+1;# 结束循环的条件if i=num then# 离开循环leave myloop;end if;end loop;
end;
call proc_test11(5);

8.6 存储过程管理

8.6.1 查询存储过程

存储过程是属于某个数据库的,也就是说当我们将存储过程创建在某个数据库之后,只能在当前数据库中调用此存储过程。

查询存储过程:查询某个数据库中有哪些存储过程

-- 根据数据库名,查询当前数据库中的存储过程
show procedure status where db='db_test2';
-- 查询存储过程的创建细节
show create procedure db_test2.proc_test1;

8.6.2 修改存储过程

修改存储过程指的是修改存储过程的特征/特性

alter procedure <proc_name> 特征1 [特征2 特征3 ....]

存储过程的特征参数:

  • CONTAINS SQL 表示子程序包含 SQL 语句,但不包含读或写数据的语句
  • NO SQL 表示子程序中不包含 SQL 语句
  • READS SQL DATA 表示子程序中包含读数据的语句
  • MODIFIES SQL DATA 表示子程序中包含写数据的语句
  • SQL SECURITY { DEFINER |INVOKER } 指明谁有权限来执行
    • DEFINER 表示只有定义者自己才能够执行
    • INVOKER 表示调用者可以执行
  • COMMENT ‘string’ 表示注释信息
alter procedure proc_test1 READS SQL DATA;

8.6.3 删除存储过程

-- 删除存储过程
-- drop 删除数据库中的对象 数据库、数据表、列、存储过程、视图、触发器、索引....
-- delete 删除数据表中的数据
drop procedure proc_test1;

8.7 存储过程练习案例

使用存储过程解决企业项目开发过程中的问题

案例:使用存储过程完成借书操作

8.7.1 数据准备

-- 创建数据库
create database db_test;-- 使用数据库
use database db_test-- 创建图书信息表
create table books(book_id int primary key auto_increment,book_name varchar(50) not null,book_author varchar(50) not null,book_price decimal(10,2) not null,book_stock int not null,book_desc varchar(500)
);-- 添加图书信息
insert into books(book_name,book_author,book_price,book_stock,book_desc)
values('Java程序设计','亮亮',38.80,12,'亮亮老师带你学Java');
insert into books(book_name,book_author,book_price,book_stock,book_desc)
values('Java王者之路','威哥',44.40,12,'威哥带你学Java');-- 创建学生信息表
create table students(stu_num char(8) primary key,stu_name varchar(10) not null,stu_gender char(8) not null,stu_age int not null
);-- 添加学生信息
insert into students(stu_num,stu_name,stu_gender,stu_age)
values('1001','张三','男',20);
insert into students(stu_num,stu_name,stu_gender,stu_age)
values('1002','李四','男',21);
insert into students(stu_num,stu_name,stu_gender,stu_age)
values('1003','张丽','女',18);

8.7.2 业务分析

哪个学生借哪本书,借了多少本?
操作:

  • 保存借书记录
  • 修改图书库存

条件:

  • 判断学生是否存在?
  • 判断图书是否存在、库存是否充足?

创建借书记录表

-- 借书记录表
create table records(r_id int primary key auto_increment,snum char(4) not null,   -- 学生借书数量b_id int not null,borrow_num int not null,is_return int not null,  -- 0未归还,-- 1已归还borrow_date date not null,constraint FK_RECORDS_STUDENTS foreign key(snum) references students(stu_num),constraint FK_RECORDS_BOOKS foreign key(b_id) references books(book_id)
);

8.7.3 实现借书业务

-- 实现借书业务:
-- 参数1:a	输入参数 	学号
-- 参数2:b	输入参数 	图书编号
-- 参数3:m	输入参数 	借书的数量
-- 参数4:state 输出参数 借书的状态(1借书成功,2学号不存在,3图书不存在,4库存不足)
create procedure proc_borrow_book(IN a int ,IN b int ,IN m int ,OUT state int )
begindeclare stu_count int default 0;declare book_count int default 0;declare stocks int default 0;-- 判断学号是否存在,根据参数a到学生信息表查询是否有stu_num=a的记录select count(stu_num) INTO stu_count from students where stu_num=a;if stu_count >0 then -- 学号存在-- 判断图书是否存在,根据参数b,查询图书记录总数select count(book_id) INTO book_count from books where book_id=b;if book_count>0 then -- 图书存在-- 图书库存是否充足,根据参数m,查询当前图书库存,并与参数m进行比较select book_stock INTO stocks from books where book_id=b;if stocks>m then -- 执行借书-- 1 在借书记录表中添加记录insert into records(snum,b_id,borrow_num,is_return,borrow_date)values(a,b,m,0,sysdate());-- 2 修改图书库存update books set book_stock=stocks-m where book_id=b;-- 3 借书成功set state=1;else-- 库存不足set state=4;end if; else -- 图书不存在set state= 3;end if;else -- 学号不存在set state=2;end if;
end;-- 调用存储过程借书
set @state=0;
call proc_borrow_book('1001',1,2,@state);
select @state from dual;

8.7.4 实现还书业务

哪个学生还书, 还多少本

操作:

  • 保存还书记录
  • 修改图书库存

条件

  • 判断学生是否借过书?
  • 判断图书是否存在?
-- 实现还书业务:
-- 创建还书记录表
create table return_records(r_id int primary key auto_increment,s_num char(4) not null,   -- 学生还书数量b_id int not null,return_num int not null,return_date date not null,constraint FK_RETURN_RECORDS_BORROW_RCORDS foreign key(s_num) references borrow_records(snum)
);-- 参数1:sid	输入参数 	学号
-- 参数2:bid 输入参数 	图书编号
-- 参数3:n	输入参数 	还书的数量
-- 参数4:state 输出参数 借书的状态(1还书成功,2该生未借过图书,3图书不存在)
create procedure proc_return_book(IN sid int, IN bid int , IN n int , OUT state int )
begindeclare stu_count int default 0;declare b_count int default 0;declare stocks int default 0;-- 判断该生是否有借书记录,将参数sid与借书借书记录表中,是否sid=snumselect count(snum) INTO stu_count from borrow_records where snum=sid;if stu_count>0 then -- 有借书记录-- 判断是否借过该本图书,将参数bid与借书记录表中是否bid=r_idselect count(r_id) INTO b_count from borrow_records where bid=r_id;if b_count >0 then -- 图书编号正确-- 执行还书操作-- 1 在还书记录表中添加记录insert into return_records(s_num,b_id,return_num,return_date)values(sid,bid,n,sysdate());-- 2 修改图书库存update books set book_stock=stocks+n where book_id=bid; -- 3 修改借书状态update borrow_records set is_return=1 where r_id=bid;-- 4 还书成功set state=1;else-- 图书编号不正确set state =3;end if;else-- 无借书记录set state=2;end if;end;set @state=0;
call proc_return_book('1003',2,3,@state);
select @state from dual;

8.8 游标

问题:如果我们要创建一个存储过程,需要返回查询语句查询到的多条数据,该如何实现呢?

8.8.1 游标的概念

游标可以用来依次取出查询结果集中的每一条数据——逐条读取查询结果集中的记录

8.8.2 游标的使用步骤

1、声明游标

  • 声明游标

语法

DECLARE cursor_name CURSOR FOR select_statement;
  • 示例:
declare mycursor cursor for select class_id,class_name from classes;

2、打开游标

语法:

open mycursor;

3、使用游标

  • 使用游标:提取游标当前指向的记录(提取之后,游标自动下移)
fetch mycursor into cid,cname;

4、关闭游标

CLOSE mycursor;

游标使用案例

MySQL中, Concat_WS() 函数 用来通过指定符号,将2个或多个字段拼接在一起,返回拼接后的字符串。

create procedure proc_test12(out result varchar(200))
begin
# 游标变量
declare cid int;
# 游标变量
declare cname varchar(20);
# 计数变量
declare num int;
# 计数变量
declare i int;
# 每条数据
declare str varchar(100);
# 查询语句执行之后返回的是一个结果集(多条记录),使用游标遍历查询结果集
declare mycursor cursor for select class_id,class_name from classes;
# 记录总数据量
select count(*) into num from classes;
# 打开游标
open mycursor;
set i = 0;
# 开始遍历游标
while i<num do# 提取游标中的数据,并将结果赋值给游标变量fetch mycursor into cid,cname;set i = i+1;# set str=concat_ws('~',cid,cname); 不同的写法select concat_ws('~',cid,cname) into str;set result = concat_ws(',',result,str);end while;close mycursor;
end;
# 案例测试
set @r = '';
call proc_test12(@r);
select @r from dual;

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

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

相关文章

redis学习笔记(二)

一&#xff1a;redis数据的持久化 1.1&#xff1a;RDB方式 1.2&#xff1a;AOF方式 1.3&#xff1a;两种方式对比 二&#xff1a;redis主从架构 2.1&#xff1a;搭建主从架构 1&#xff1a;三台机器上安装三个redis&#xff0c;其中一个作为主节点&#xff0c;剩下两个作为从…

Kosmos-2: 在多模态大语言模型中引入基准和指代能力

Kosmos-2: 在多模态大语言模型中引入基准和指代能力 FesianXu 20240304 at Baidu Search Team 前言 之前笔者在博文中介绍过kosmos-1模型 [1]&#xff0c;该模型脱胎于MetaLM采用『因果语言模型作为通用任务接口』的思想&#xff0c;采用了多种形式的多模态数据进行训练得到。…

FPGA FIFO 读取模式

FPGA FIFO 读取模式分两种&#xff1a; Normal Mode: In normal mode, the “rdreq” signal serves as the read request or read enable. When this signal goes high, the data output provides the first data from the FIFO.Essentially, in normal mode, data is availa…

C++异常处理机制【自定义异常体系 || 异常规范 || 异常安全】

目录 一&#xff0c;传统C语言处理异常 二&#xff0c;C异常概念 三&#xff0c;使用 1. 自定义异常体系 2. 在函数调用链中异常栈展开匹配原则 3. 异常的重新抛出 四&#xff0c;异常规范 五&#xff0c;异常安全 六&#xff0c;C标准库的异常体系 七&#xff0c;异…

人工智能|机器学习——Canopy聚类算法(基于密度)

1.简介 Canopy聚类算法是一个将对象分组到类的简单、快速、精确地方法。每个对象用多维特征空间里的一个点来表示。这个算法使用一个快速近似距离度量和两个距离阈值T1 > T2 处理。 Canopy聚类很少单独使用&#xff0c; 一般是作为k-means前不知道要指定k为何值的时候&#…

算法学习06:数组模拟:单/双链表,栈和队列,单调栈/队列

算法学习06&#xff1a;数组模拟&#xff1a;单/双链表&#xff0c;栈和队列&#xff0c;单调栈/队列 文章目录 算法学习06&#xff1a;数组模拟&#xff1a;单/双链表&#xff0c;栈和队列&#xff0c;单调栈/队列前言一、链表1.单链表2.双链表 二、栈和队列1.普通栈、队列2.单…

docker安装ES、LogStash、Kibana

文章目录 一、安装Elasticsearch1. 安装Elasticsearch2. 安装IK分词器3. elasticsearch-head 监控的插件4. 配置跨域 二、安装LogStash三、安装kibana四、SpringBoot集成LogStash&#xff0c;将日志输出到ES中五、 启动项目&#xff0c;监控项目运行 提示&#xff1a;以下是本篇…

docker创建mysql,以及mysql无法连接问题

1、docker开放3306端口 Docker开放3306端口实现步骤 本文将介绍如何使用Docker开放3306端口&#xff0c;以便其他容器或外部应用程序可以通过该端口访问MySQL数据库。以下是实现步骤的简要说明&#xff1a; 步骤 描述 第一步 创建一个MySQL容器 第二步 打开3306端口…

Part-DB 安装及使用

Part-DB 安装及使用: http://coffeelatte.vip.cpolar.top/post/software/applications/part-db/part-db_安装及使用/ Part-DB 是比较实用的元器件进销存 Web 系统&#xff0c;支持使用电脑摄像头扫码&#xff08;条码和二维码&#xff09;。 1. 安装 大部分按照 官方安装指导…

C++:模版进阶 | Priority_queue的模拟实现

创作不易&#xff0c;感谢三连支持 一、非类型模版参数 模板参数分类为类型形参与非类型形参。 类型形参即&#xff1a;出现在模板参数列表中&#xff0c;跟在class或者typename之类的参数类型名称。 非类型形参&#xff0c;就是用一个常量作为类(函数)模板的一个参数&…

JavaScript高级Ⅱ(全面版)

接上文 JavaScript高级Ⅰ JavaScript高级Ⅰ(自认为很全面版)-CSDN博客 目录 第2章 DOM编程 2.1 DOM编程概述 2.1.4 案例演示(商品全选) 2.1.5 dom操作内容 代码演示&#xff1a; 运行效果&#xff1a; 2.1.6 dom操作属性 代码演示&#xff1a; 运行效果&#xff1a; 2…

CEF JS与c++能够交互的原理 以及 JS 调用C++的流程分析

相关章节:CEF 之 Render进程 与 Browser进程通信 目录 一、JS与c++能够交互的原理 二、JS调用C++ 流程梳理

大模型时代下的自动驾驶研发测试工具链-SimCycle

前言&#xff1a; 最近OpenAI公司的新产品Sora的发布&#xff0c;正式掀起了AI在视频创作相关行业的革新浪潮&#xff0c;AI不再仅限于文本、语音和图像&#xff0c;而直接可以完成视频的生成&#xff0c;这是AI发展历程中的又一座重要的里程碑。AI正在不断席卷着过去与我们息…

【Node.js从基础到高级运用】四、Node.js基础

Node.js 基础 Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。它使得 JavaScript 可以脱离浏览器环境&#xff0c;在服务器端运行。本教程将介绍 Node.js 的一些基础概念&#xff0c;包括全局对象、内置模块的使用&#xff0c;以及事件循环和异步编程的基础。 全…

【EXCEL自动化07】用pandas库实现vlookup函数功能

🔥学好办公自动化,帮你节省更多宝贵的时间 🔥这个专栏收录python办公自动化的实操案例,利用python实现高效的办公自动化 🔥实现excel,word,文件批处理等自动化操作 使用pandas库来实现Excel中VLOOKUP函数的功能。 需要安装pandas库。有时直接下载会失败,会提示先安…

STM32 学习10 PWM输出

STM32 学习10 PWM输出 一、PWM简介1. PWM的概念2. PWM的工作原理3. PWM 常用的应用场景 二、一些概念1. 频率2. 占空比 三、STM32F1 PWM介绍1. 定时器与寄存器&#xff08;1&#xff09;**自动重装载寄存器&#xff08;ARR&#xff09;**&#xff1a;&#xff08;2&#xff09;…

python基础——输入与输出【input 和 print】

&#x1f4dd;前言&#xff1a; 上一篇文章python基础——入门必备知识中讲解了一些关于python的基础知识&#xff0c;可以让我们更好的理解程序代码中内容的含义&#xff0c;不至于一头雾水。今天我就来介绍一下&#xff0c;python中两个常见的输入和输出语句 input 和 print …

产品推荐 - 基于星嵌 OMAPL138+国产FPGA的DSP+ARM+FPGA三核开发板

1 评估板简介 基于TI OMAP-L138&#xff08;定点/浮点DSP C674xARM9&#xff09; FPGA处理器的开发板&#xff1b; OMAP-L138是TI德州仪器的TMS320C6748ARM926EJ-S异构双核处理器&#xff0c;主频456MHz&#xff0c;高达3648MIPS和2746MFLOPS的运算能力&#xff1b; FPGA…

粘包与拆包

优质博文&#xff1a;IT-BLOG-CN 一、粘包出现的原因 服务端与客户端没有约定好要使用的数据结构。Socket Client实际是将数据包发送到一个缓存buffer中&#xff0c;通过buffer刷到数据链路层。因服务端接收数据包时&#xff0c;不能断定数据包1何时结束&#xff0c;就有可能出…

【操作系统概念】第11章:文件系统实现

文章目录 0.前言11.1 文件系统结构11.2 文件系统实现11.2.1 虚拟文件系统 11.3 分配方法11.3.1 连续分配11.3.2 链接分配11.3. 3 索引分配 11.5 空闲空间管理11.5.1 位图/位向量11.5.2 链表11.5.3 组 0.前言 正如第10章所述&#xff0c;文件系统提供了机制&#xff0c;以在线存…