MySQL存储过程实现商品购买流程

363 阅读5分钟

本文正在参加「技术专题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

idgoods_namegoods_pricegoods_count
1棒棒糖0.5100
2阔爱多3.5100
3大辣片0.1100
4大阔乐4.5100
5老冰棍0.5100

store_user

idusername
1老边
2老张

然后基于这个表结构,来创建一个存储过程,完成:购买生成订单记录功能。

在创建之前首先认识一条mysql命令:

DELIMITER // #修改mysql语句结束符,默认是 ;

因为在编写存储过程当时候需要写很多条语句,可能MySQL更具;就直接执行了,所以需要先修改结束符,然后等到编写完成再修改回来,所以可以人为是一种套路的写法:

DELIMITER // #修改结束符为 //
#创建存储过程
DELIMITER ; 改回结束符为 ;

然后这里说一下存储过程的一些需要关注的语法

1、整体结构

create procedure 存储过程名称(参数1,参数2...) 
begin #开始存储过程体编写
    set @变量 = (select 字段 fromwhere 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期 漫谈数据库技术」活动