基本命令
#声明变量 DECLARE a INT; #对变量赋值 SET a = 5;
基本使用
创建存储过程
DROP PROCEDURE if exists p8; CREATE PROCEDURE p8 () BEGIN DECLARE a INT; DECLARE b INT; DECLARE @TIMES INT;-- SET a = 5; SET b = 5; INSERT INTO user(name, age) VALUES (a,b); select * from user; END;
调用存储过程
call p8();
游标
- 游标的声明需要在所有其他变量之后,游标常常要配合一个状态值来实现功能,需要声明一个休止标识
- declare continue handler for not found set 休止标识=1;
- 游标使用要先open,最后需要close
- 游标值的获取用fetch my_cursor into 某变量
基本使用
delimiter $$ CREATE procedure changeName() begin declare stopflag int default 0; declare myname varchar(20) default ''; declare my_cursor cursor for select sname from student where sid%2=0; declare continue handler for not found set stopflag=1; open my_cursor; while(stopflag=0) do begin fetch my_cursor into myname; update student set sname = concat(sname,'aab') where sname = myname; end; end while; close my_cursor; end; $$
利用存储过程进行秒杀
实用有并发,但是并发量不大,请求压力在后台。如果高并发的情况下,需要做额外的处理
建表
-- 整个项目的数据库脚本 -- 开始创建一个数据库 CREATE DATABASE seckill; -- 使用数据库 USE seckill; -- 创建秒杀库存表 CREATE TABLE seckill( `seckill_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '商品库存ID', `name` VARCHAR(120) NOT NULL COMMENT '商品名称', `number` INT NOT NULL COMMENT '库存数量', `start_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP() COMMENT '秒杀开启的时间', `end_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP() COMMENT '秒杀结束的时间', `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP() COMMENT '创建的时间', PRIMARY KEY (seckill_id), KEY idx_start_time(start_time), KEY idx_end_time(end_time), KEY idx_create_time(create_time) )ENGINE =InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT='秒杀库存表'; -- 插入初始化数据 insert into seckill(name,number,start_time,end_time) values ('1000元秒杀iphone6',100,'2016-5-22 00:00:00','2016-5-23 00:00:00'), ('500元秒杀iPad2',200,'2016-5-22 00:00:00','2016-5-23 00:00:00'), ('300元秒杀小米4',300,'2016-5-22 00:00:00','2016-5-23 00:00:00'), ('200元秒杀红米note',400,'2016-5-22 00:00:00','2016-5-23 00:00:00'); -- 秒杀成功明细表 -- 用户登录相关信息 create table success_killed( `seckill_id` BIGINT NOT NULL COMMENT '秒杀商品ID', `user_phone` BIGINT NOT NULL COMMENT '用户手机号', `state` TINYINT NOT NULL DEFAULT -1 COMMENT '状态标示:-1无效 0成功 1已付款', `create_time` TIMESTAMP NOT NULL COMMENT '创建时间', PRIMARY KEY (seckill_id,user_phone), /*联合主键*/ KEY idx_create_time(create_time) )ENGINE =InnDB DEFAULT CHARSET =utf8 COMMENT ='秒杀成功明细表'
建立存储过程
-- 秒杀执行储存过程 DELIMITER $$ -- console ; 转换为 $$ -- 定义储存过程 -- 参数: in 参数 out输出参数 -- row_count() 返回上一条修改类型sql(delete,insert,update)的影响行数 -- row_count:0:未修改数据 ; >0:表示修改的行数; <0:sql错误 CREATE PROCEDURE `seckill`.`execute_seckill` (IN v_seckill_id BIGINT, IN v_phone BIGINT, IN v_kill_time TIMESTAMP, OUT r_result INT) BEGIN DECLARE insert_count INT DEFAULT 0; START TRANSACTION; #先插入,seckill_id, user_phone建立了主键,如果重复插入会报错,我们使用IGNORE,忽略重复插入,如果重复插入,SELECT row_count()返回0,就会rollback; INSERT IGNORE INTO success_killed (seckill_id, user_phone, create_time) VALUES (v_seckill_id, v_phone, v_kill_time); SELECT row_count() INTO insert_count; IF (insert_count = 0) THEN ROLLBACK; SET r_result = -1; ELSEIF (insert_count < 0) THEN ROLLBACK; SET r_result = -2; ELSE #更新仓库数量,如果更新成功,SELECT row_count()返回值>0,就可以继续走; UPDATE seckill SET number = number - 1 WHERE seckill_id = v_seckill_id AND end_time > v_kill_time AND start_time < v_kill_time AND number > 0; SELECT row_count() INTO insert_count; IF (insert_count = 0) THEN ROLLBACK; SET r_result = 0; ELSEIF (insert_count < 0) THEN ROLLBACK; SET r_result = -2; ELSE COMMIT; SET r_result = 1; END IF; END IF; END; $$
测试
set @r_result =111; CALL execute_seckill(1003, 13502172891, now(), @r_result); SELECT @r_result;
在mapper接口
void killByProcedure(Map<String,Object> paramMap);
xml
<!--调用储存过程--> <select id="killByProcedure" statementType="CALLABLE"> CALL execute_seckill( #{seckillId,jdbcType=BIGINT,mode=IN}, #{phone,jdbcType=BIGINT,mode=IN}, #{killTime,jdbcType=TIMESTAMP,mode=IN}, #{result,jdbcType=INTEGER,mode=OUT} ) </select>
ps:关于select row_count(),在控制台可以测试出来,返回上一次update,insert,delete影响行数。
if not exists注意事项
在if not exists中出现异常,不会报错,直接跳出if判断。所以很容易出现问题,尽量少用,参考上面(秒杀)使用。
delimitr $$ CREATE PROCEDURE `test_proc`( pid int ) begin if not exists (select * from t where id=pid) then insert into t(id) values(pid); end if; update t set total=total+1 where id=pid; end $$ delimiter ;