MySQL----存储过程

基本命令

#声明变量
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 ; 

  

原文地址:https://www.cnblogs.com/yanxiaoge/p/13746323.html