本文正在参加「技术专题19期 漫谈数据库技术」活动
存储过程在基础的sql学习过程当中并不多见,但是执行的性能确实是很不错,尽管可移植性差,但是效率还是很不错的,那么这篇文章就使用mysql来聊聊存储过程。
存储过程
在实际的开发过程当中,很多时候业务逻辑不是使用一条sql语句可以表达的,比如在电商项目当中用户的一次购买行为,最简单来看:
1、用户发起购买。
2、确定商品表当中有货。
3、如果有货向下进行,如果无货退出交易。
4、进行交易,商品数量减少,锁定商品(注意,这里不讨论交易失败的问题)。
这样的步骤首先可以在服务端,也就是数据库请求部分使用脚本去实现,但是确实存在一些问题:
1、首先需要查询商品数量,然后再进行数量的递减,至少需要两次sql操作,当然实际情况下可能会更多,所以效率是一个很大的问题。
2、步骤的增多,很容易导致错误的发生,并且不同的业务调用购买功能,可能出现实现代码不一致导致的业务层面的数据误差。
3、根据第2点,那么功能的可维护性就下降了很多(一般来说,保证项目的可维护性,很重要的就是代码的一致性)。
有问题,就会衍生出解决方案,所以存储过程就产生了,对比在脚本当中执行多条sql的业务逻辑,存储过程更有以下的优点:
1、存储过程可以封装多条sql,进行基本的逻辑判断和数据返回,用调用存储过程来代替每次重复的执行多条sql,简化了操作的步骤,这一点可以减少90%的误操作,只要存储过程当中编写没有问题,那么错误的可能性就无限的下降了。
2、存储过程通常是以编译过的形式存储的,很显然执行效率要高很多,性能上是有优势的。
所以总结一下,存储过程是一个类似编程当中的函数的结构,可以包含多条sql语句,可以进行逻辑判断,可以传递参数,可以返回一个结果,并且以编译后的形式存在。
当然存储过程在不同的数据库当中的格式都是不一样的,比如,MySQL5.0之后才有了存储过程,所以在不同数据库之间,存储过程的可移植性不好。
购买案例
我们通过一个购买的案例来展示一下MySQL存储过程的创建和使用,首先是表结构:
商品表:(store_goods)
| 字段 | 描述 |
|---|---|
| id | 商品编号 |
| goods_name | 商品名称 |
| goods_price | 商品价格 |
| goods_count | 商品数量 |
用户表: (store_user)
| 字段 | 描述 |
|---|---|
| id | 用户编号 |
| username | 用户姓名 |
订单表: (goods_order)
| 字段 | 描述 |
|---|---|
| id | 订单编号 |
| user_id | 用户id |
| goods_id | 商品id |
| quantity | 购买数量 |
| payment | 支付金额 |
填入基本的数据
store_goods
| id | goods_name | goods_price | goods_count |
|---|---|---|---|
| 1 | 棒棒糖 | 0.5 | 100 |
| 2 | 阔爱多 | 3.5 | 100 |
| 3 | 大辣片 | 0.1 | 100 |
| 4 | 大阔乐 | 4.5 | 100 |
| 5 | 老冰棍 | 0.5 | 100 |
store_user
| id | username |
|---|---|
| 1 | 老边 |
| 2 | 老张 |
然后基于这个表结构,来创建一个存储过程,完成:购买生成订单记录功能。
在创建之前首先认识一条mysql命令:
DELIMITER // #修改mysql语句结束符,默认是 ;
因为在编写存储过程当时候需要写很多条语句,可能MySQL更具;就直接执行了,所以需要先修改结束符,然后等到编写完成再修改回来,所以可以人为是一种套路的写法:
DELIMITER // #修改结束符为 //
#创建存储过程
DELIMITER ; 改回结束符为 ;
然后这里说一下存储过程的一些需要关注的语法
1、整体结构
create procedure 存储过程名称(参数1,参数2...)
begin #开始存储过程体编写
set @变量 = (select 字段 from 表 where id = 值) #将表当中取出来的值定义为变量,这里取出的尽量是一个单一的值
if 判断条件
elseif 判断条件
else
end if #判断结束一定要加endif
end #结束存储过程体编写
2、参数
存储过程的函数形参定义比较需要声明 参数用来输入还是输出,参数名称,产生数据类型,比如:
in goods_id int #定义一个用来输入的参数goods_id,类型为整形
out goods_number int #定义一个用来输出的参数 goods_number,类型为整形
inout goods_price float #定义一个用来输入输出的参数 goods_number,类型为浮点型
最后代码如下
DELIMITER //
create procedure buy_process(in arg_user_id int,in arg_goods_id int, in arg_goods_count int)
begin
set @sum_price = (arg_goods_count*(select goods_price from store_goods where id = arg_goods_id));
set @g_count = (select goods_count from store_goods where id = arg_goods_id);
if arg_goods_count > @g_count then
select '库存不足';
else
update store_goods set goods_count = @g_count - arg_goods_count where id = arg_goods_id;
insert into goods_order(user_id,goods_id,payment,quantity) values(arg_user_id,arg_goods_id,@sum_price,arg_goods_count);
select '购买完成';
end if;
END //
DELIMITER ;
调用存储过程
可以说是苦尽甘来的一步骤啦,调用存储过程。
mysql> call buy_process(1,1,12);
+----------+
| 购买完成 |
+----------+
| 购买完成 |
+----------+
mysql> call buy_process(1,1,120);
+----------+
| 库存不足 |
+----------+
| 库存不足 |
+----------+
当然要记得删除存储过程的命令:
drop procedure buy_process;
最后吐槽一句,编写这个博客的过程当中,也确实体验到了大家对存储过程的一个吐槽,编写难度和复杂程度确实很高呀。
本文正在参加「技术专题19期 漫谈数据库技术」活动