MySQL存储过程/触发器/计划事件

存储过程

存储过程是存储在数据库目录中的一段声明性SQL语句。

创建存储过程

DELIMITER //
DROP PROCEDURE IF EXISTS procedure_name;
CREATE PROCEDURE procedure_name()
  BEGIN
    ...
  END //
DELIMITER ;

DELIMITER将标准分隔符;暂时换成//,并在创建存储过程结束后将标准分隔符改回;。

删除存储过程

DROP PROCEDURE IF EXISTS procedure_name;

调用存储过程

CALL procedure_name();

声明变量

DECLARE var_name datatype(size) DEFAULT default_value;

DECLARE 可以声明多个同一类型的变量。

变量设值

使用SET设值

SET var_name = var_value;

使用SELECT INTO设值

SELECT COUNT(0) INTO count FROM tb_name;

变量作用域

声明的普通变量的作用域在BEGIN END块内,声明以@开头的变量表明其为会话变量,直到会话结束前它可用和可访问。

存储过程参数

DELIMITER //
DROP PROCEDURE IF EXISTS procedure_name;
CREATE PROCEDURE procedure_name(Mode param_name param_type(param_size))
  BEGIN
    ...
  END //
DELIMITER ;

Mode的可选值:IN(传入参数,不可被修改)、OUT(交由存储过程修改)、INOUT(传入且可以被修改)。
示例:

DELIMITER //
DROP PROCEDURE IF EXISTS param_test;
CREATE PROCEDURE param_test(IN step INT, INOUT distance INT)
  BEGIN
    SET distance = distance + step;
  END //
DELIMITER ;
SET @distance = 0;

CALL param_test(5, @distance);
SELECT @distance;  -- @distance=5

CALL param_test(7, @distance);
SELECT @distance;  -- @distance=12

条件判断

if
    IF ... THEN ... 
    ELSEIF ... THEN ... 
    ELSE ...
    END IF;
简单case(必须包含else)
    CASE ...
      WHEN ... THEN ...
      WHEN ... THEN ...
    ELSE ...
    END CASE;
可搜索case(必须包含else)

在when语句中做条件判断

    CASE ...
      WHEN expression... THEN ...
      WHEN expression... THEN ...
    ELSE ...
    END CASE;

循环

while循环
    WHILE ... DO
      ...
    END WHILE;
repeat循环
    REPEAT
      statements;
    UNTIL expression
    END REPEAT;
loop、leave和iterate

leave相当于break、iterate相当于continue

    loop_label: LOOP
      IF ... THEN
        LEAVE loop_label;
      END IF;
  
      IF ... THEN
        ITERATE loop_label;
      END IF;
    END LOOP;

MySQL游标

用来遍历SELECT语句的结果集。
只读、不可滚动、敏感

声明

声明游标必须在声明变量之后,游标必须始终与SELECT语句相关联。

DECLARE cursor_name CURSOR FOR SELECT_statement;
处理空行
DECLARE end INT DEFAULT 0;
/* 当右边移动到空行,将end置为1 */
DECLARE CONTINUE HANDLER FOR NOT FOUND SET end = 1;
初始化游标
OPEN cursor_name;
检索光标下一行并将光标移动到结果集下一行

将游标检索结果赋值给定义的变量

FETCH cursor_name INTO var [var2, ...];
停用光标、释放内存
CLOSE cursor_name;

示例:

DELIMITER //
DROP PROCEDURE IF EXISTS cursor_test;
CREATE PROCEDURE cursor_test(OUT res VARCHAR(255))
  BEGIN
    DECLARE end INT DEFAULT 0;
    DECLARE id VARCHAR(255);
    DECLARE cursor_name CURSOR FOR SELECT id FROM tb_name;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET end = 1;

    OPEN cursor_name;
    WHILE end != 1 DO
      FETCH cursor_name INTO id;
      SET res = CONCAT(res, ';', id);
    END WHILE;
    CLOSE cursor_name;
  END //
DELIMITER ;

事务

/* 开始事务 */
START TRANSACTION;
/* 回滚 */
ROLLBACK;
/* 提交 */
COMMIT;

查看存储过程详情

SHOW PROCEDURE STATUS [LIKE 'pattern' | WHERE expr];
SHOW CREATE PROCEDURE procedure_name;

声明处理程序

DECLARE action_name HANDLER FOR condition_value ...;

action_name可选值:

  • CONTINUE(继续执行封闭代码块)
  • EXIT(处理程序声明封闭代码块的执行终止)

condition_value可选值:

  • 一个MySQL错误代码
  • 标准SQLSTATE值(SQLWARNING、SQLEXCEPTION、NOT FOUND)
  • 其它

触发器

触发器是一种特殊类型的存储过程,当对相关联的表上的数据进行更改时,会自动调用该语句。触发器可以被定义为在INSERT、UPDATE、DELETE语句更改数据BEFORE或AFTER调用。

创建触发器
DELIMITER //
DROP TRIGGER IF EXISTS trigger_name;
CREATE TRIGGER trigger_name
[BEFORE|AFTER] [INSERT|UPDATE|DELETE] ON tb_name
FOR EACH ROW
  BEGIN
    ...
  END //
DELIMITER ;

MySQL5.7.2+支持在同一触发事件和动作时间创建多个触发器,可以通过FOLLOWS或PRECEDES来指定触发顺序。

DELIMITER //
DROP TRIGGER IF EXISTS trigger_name;
CREATE TRIGGER trigger_name
[BEFORE|AFTER] [INSERT|UPDATE|DELETE] ON tb_name
FOR EACH ROW [FOLLOWS|PRECEDES] existing_trigger_name
  BEGIN
    ...
  END //
DELIMITER ;
管理触发器
/* 查询所有触发器 */
SELECT * FROM information_schema.triggers;
/* 查询本库触发器*/
SHOW TRIGGERS;

动态sql

通过传递不同参数执行不同sql语句

DELIMITER //
DROP PROCEDURE IF EXISTS procedure_name;
CREATE PROCEDURE procedure_name(IN col_name VARCHAR(255))
  BEGIN
      SET @sql_exe = CONCAT('SELECT ', col_name, ' FROM tb_name)');
      PREPARE stmt FROM @sql_exe;
      EXECUTE stmt;
      /* 释放prepare */
      DEALLOCATE PREPARE stmt;
  END;
DELIMITER ;

另一种写法,不适用参数为字段名的情况

DELIMITER //
DROP PROCEDURE IF EXISTS procedure_name;
CREATE PROCEDURE procedure_name(IN id INT)
  BEGIN
    SET @id = id;
    SET @sql_exe = 'SELECT col_name FROM tb_name WHERE id = ?';
    PREPARE stmt FROM @sql_exe;
    EXECUTE stmt USING @id;
    /* 释放prepare */
    DEALLOCATE PREPARE stmt;
  END //
DELIMITER ;

计划事件

开启MySQL计划事件
/* 查看计划事件状态 */
SHOW VARIABLES LIKE 'event_scheduler'

/* 用脚本启动计划事件 */
SET GLOBAL event_scheduler = ON;
SET @@global.event_scheduler = ON;
SET GLOBAL event_scheduler = 1;
SET @@global.event_scheduler = 1;

/* 通过my.ini文件设置MySQL支持计划事件 */
event_scheduler = 1
创建计划事件
DELIMITER //
DROP EVENT IF EXISTS event_name;
CREATE EVENT event_name
ON SCHEDULE schedule
[ON COMPLETION [NOT] PRESERVE]
[ENABLE | DISABLE]
[COMMENT] 
DO sql_statement;
DELIMITER ;

shecdule是计划时间设置,使用AT + 时间戳来实现单次计划事件,使用EVERY + 数量 + 时间单位 + [STRATS 时间戳] [ENDS 时间戳] 来实现重复的计划事件。
[ON COMPLETION [NOT] PRESERVE]用来说明计划事件不会执行后该事件是否保留。
[ENABLE | DISABLE]表示是否激活事件,通过ALTER EVENT event_name ENABLE/DISABLE来激活/关闭计划事件。
DO可以执行复合sql语句,用BEGIN ... END包含。
例:

DELIMITER //
DROP EVENT IF EXISTS event_name;
CREATE EVENT event_name
ON SCHEDULE EVERY 20 SECOND STARTS '2049-01-01 00:00:00'
ON COMPLETION PRESERVE
ENABLE
DO CALL procedure_name();
DELIMITER ;

在Mybatis中调用存储过程

存储过程
DELIMITER //
DROP PROCEDURE IF EXISTS mybatis_test;
CREATE PROCEDURE mybatis_test(IN col_name VARCHAR(255), OUT count INT)
  BEGIN
    SELECT COUNT(0) INTO count FROM tb_name;
    SET @sql_exe = CONCAT('SELECT ', col_name, ' FROM tb_name');
    PREPARE stmt FROM @sql_exe;
    EXECUTE stmt;
    /* 释放prepare */
    DEALLOCATE PREPARE stmt;
  END //
DELIMITER ;
Mybatis xml文件

parameterMap为传入参数(当参数数量为1个时,可以直接用#{ }传入),存储过程中使用了SELECT语句,同样可以通过resultType返回

    <select id="mybatisTest" parameterMap="paramMap" statementType="CALLABLE" resultType="xxx">
        CALL mybatis_test(?, ?);
    </select>

    <parameterMap id="paramMap" type="java.util.Map">
        <parameter property="colName" mode="IN" jdbcType="VARCHAR"/>
        <parameter property="count" mode="OUT" jdbcType="INTEGER"/>
    </parameterMap>

不用parameterMap的方式

     CALL mybatis_test(
        #{colName, jdbcType = VARCHAR, mode = IN},
        #{userPhone, jdbcType = INTEGER, mode = OUT}
     );
mapper接口
    List<xxx> mybatisTest(Map<String, Object> map);
调用
    @Test
    public void mybatisTest() throws Exception {
        Map<String, Object> map = new HashMap<>();
        map.put("colName", "id");

        List<xxx> xxxs = xxxMapper.mybatisTest(map);
        log.info("xxxs: {}", xxxs);
        log.info("count: {}", map.get("count"));
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • 任务需求:定时执行的任务,调用存储过程,进行数据迁移。 存储过程相关总结:(存储过程的创建 不能伴随有if exi...
    时待吾阅读 3,060评论 0 4
  • 转载自这里 存储过程简介 我们常用的操作数据库语言SQL语句在执行的时候需要要先编译,然后执行,而存储过程(Sto...
    杜七阅读 2,379评论 4 27
  • 原文链接 MySQL存储过程详解 1.存储过程简介 我们常用的操作数据库语言SQL语句在执行的时候需要要先编译,然...
    亚斯咪妮阅读 2,670评论 1 30
  • 存储过程简介 我们常用的操作数据库语言SQL语句在执行的时候需要要先编译,然后执行,而存储过程(Stored Pr...
    麦兜叮叮当阅读 2,137评论 0 1
  • 之前在项目上遇到一个问题,实施人员在数据库中建了许多临时的测试数据,在正式客户环境中是要删掉的,但是产品页面上没有...
    Walkerc阅读 2,700评论 0 1