游标这个在我目前的项目里面用的还不多, 但是其功能还是很强大的.

动态sql以前都没用过, 是跟着富士康(不是张全蛋的富土康哦)过来的同事学的. 还是挺好用的. 我的数据库方面, 跟他学了不少. 在此, 感谢他一下, 建华锅锅.

事务在前面的篇章中其实已经出现过了, 这个东西好像还是程序中用的比较多一点.

由于之前的工作中碰到过一个场景, 正好将游标,动态sql,事务都用上了, 那么我也弄一个例子好了, 想了一个别的场景, 与工作的那个场景不相干, 并没有泄露公司业务机密之类的啊. 

先看例子吧, 然后在后面, 我补上语法.

一、例子

1. 建表

  既然是讲例子, 当然不能忘记建表嘛, 从0开始. 

create table Goods
(
    Id int not null PRIMARY key auto_increment,
    Code varchar(50) comment '编码',
    Name varchar(20) comment '名称',
    Count int comment '数量',
    Brand varchar(20) comment '品牌'
) default charset=utf8 comment '商品表';

create table GoodDetails
(
    Id int not null PRIMARY key auto_increment,
    GId int not null comment 'Goods表Id',
   Name varchar(20) comment '名称', Code varchar(
50) comment '编码明细', Remark varchar(100) comment '备注' ) default charset=utf8 comment '商品明细';

 

2. 加入基础数据

Mysql - 游标/动态sql/事务

 

3. 虚拟场景介绍

公司最近进了一批物品, 就是上面的Goods表了, 并且准备给每一个物品进行编码(编码规则就是用Goods表的Code加上流水号, 去生成), 并打上条形码.

这里的功能就是生成商品明细和流水号的问题了, 一键生成. 这里通常的实现方式有两种:

方式一 : 程序生成

  在程序中, 读取需要生成的数据, 比如上面这四条, 然后循环每一条, 给数据加上编码, 总共生成出12条数据, 在吧这12条数据, 存入明细表中. 在数据量少的时候还好, 完全可以接受, 但是如果数据量多了, 那速度, 慢的让人有砸电脑的冲动. Goods表的几条数据, 到GoodDetails表中, 会变成数百, 甚至上千, 上万. 

方式二 : 数据库生成

  如果不想读取出来再插入, 并且逻辑处理并不多,不复杂的情况下, 可以使用数据库去生成. 还是很方便的, 速度也提升非常多.

那这里, 我只介绍方式二了, 方式一, 只是处理麻烦一点.

 

4. 脚本:

delimiter $
drop PROCEDURE if EXISTS p_autocreate;
CREATE  PROCEDURE `p_autocreate`(IN g_ids VARCHAR(1000), IN nolength INT)
BEGIN
 DECLARE res_code INT;

 DECLARE res_msg VARCHAR (50);

 /*临时表的条数*/
 DECLARE t_count INT;

 /**游标内使用变量**begin**/
 DECLARE g_id INT;

 DECLARE g_code VARCHAR (50);

 DECLARE d_code VARCHAR (50);

 DECLARE g_count INT (11);

 DECLARE g_name VARCHAR (20);
 /**游标内使用变量**end**/

 /**游标的位置**/
 DECLARE v_index INT DEFAULT 1;

 DECLARE done BIT DEFAULT 0;

 /*声明游标*/
 DECLARE g_cursor CURSOR FOR SELECT id, CODE, NAME, COUNT FROM temp_goods;

 /*游标查询时, 如果找不到下一个了, 会将done置为1*/
 DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

 /**创建临时表**begin**/
 DROP TABLE IF EXISTS temp_goods;
 CREATE TEMPORARY TABLE temp_goods (
  Id INT NOT NULL, --  PRIMARY key auto_increment,
  CODE VARCHAR (50),
  NAME VARCHAR (20),
  COUNT INT
 ) DEFAULT CHARSET = utf8;
 /**创建临时表**end**/

 /**初始化返回值**begin**/
 SET res_code := "-99";

 SET res_msg := "OK";
 /**初始化返回值**end**/

 IF (g_ids IS NOT NULL OR LENGTH(g_ids) > 0) THEN
SET @v_sql := CONCAT("INSERT INTO temp_goods(Id,Code,Name,Count) ", " select Id,Code,Name,Count from Goods ", " where ", " find_in_set(id, ", CHAR(34), g_ids, CHAR(34), ")>0 ;"); /*预编译此动态sql, 并存入stmt中*/ PREPARE stmt FROM @v_sql; /*执行此动态sql, 此动态sql的作用, 是从Goods中提取有效数据*/ EXECUTE stmt; /*释放此资源*/ DEALLOCATE PREPARE stmt; SELECT COUNT(1) INTO t_count FROM temp_goods; START TRANSACTION; -- 开始事务 IF (t_count > 0) THEN /*打开游标*/ OPEN g_cursor; REPEAT /*这里的顺序要与之前的顺序保持一致*/ FETCH g_cursor INTO g_id, g_code, g_name, g_count; IF NOT done THEN SET v_index := 1; IF (IFNULL(g_count, 0) > 0) THEN WHILE (v_index <= g_count) DO SET d_code := CONCAT(g_code, LPAD(v_index, nolength, "0")); INSERT INTO GoodDetails(GId, NAME, CODE) VALUES (g_id, g_name, d_code); SET v_index := v_index + 1; END WHILE; END IF; END IF; UNTIL done END REPEAT; -- 结束repeat循环 CLOSE g_cursor; /*关闭游标*/ COMMIT; -- 提交事务 ELSE ROLLBACK; -- 回滚事务 SET res_code := "10"; SET res_msg := "系统中不存在相关记录."; END IF; ELSE SET res_code := "5"; SET res_msg := "请选择要生成的记录"; END IF; DROP TABLE IF EXISTS temp_goods; SELECT res_msg; END $ delimiter ;

 

5. 结果:

 执行这个存储过程

call p_autocreate('1,2,3,4', 3);

Mysql - 游标/动态sql/事务

ok, 执行成功, 接下来, 来看一下GoodDetails表的数据:

Mysql - 游标/动态sql/事务

我这里的例子, 已经是最简单的一个例子了, 在实际使用过程中, 可能比这个还要复杂一些, 数据更多一些.

 不过说到这个数据量, 我倒不介意, 多做一个实验.

 

6. 实验

  我将各自的数据量, 都修改为 10000, 如下图, 这个时候, 要生成 40000 条数据, 并且插入到表中去. 如果使用程序处理插入数据库的方式, 确实会慢一些.

  Mysql - 游标/动态sql/事务

数据库的方式, 确实会快很多. 如下图, 生成4w条数据, 然后插入GoodDetails表中, 花了不到4s的时间. 算是一个比较快的时间了.

  Mysql - 游标/动态sql/事务

OK, 接下来, 就来介绍一下他们的语法.

 

 二、游标

1. 语法

1.1 声明游标

  declare 游标名 cursor for select 列名 from 表

1.2 打开游标

  open 游标名

1.3 游标前进

  fetch 游标名 into 变量a, 变量b ... 

1.4 关闭游标

  close 游标名

2. 注

  既然游标执行的方式, 像是一个循环, 那么什么时候才知道这个循环要结束呢.

  例子里面, 有一句话,  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; 这句话的意思, 其实就是当游标找不到下一条数据的时候, 就回将变量 done 修改为1. 

  

三、动态sql

1. 语法

1.1 准备sql变量

  此sql变量必须是字符串格式的哦. 这样可以动态生成需要执行的sql.

1.2 预编译

  PREPARE stmt FROM @v_sql;

  这里的stmt是一个变量, 名称自己取

1.3 执行

  EXECUTE stmt;

1.4 释放资源

  DEALLOCATE PREPARE stmt;

 

四、事务

1. 语法

到这里, 我突然不知道说些什么了. 那就简单介绍下吧

1.1 开始事务

  start transation;

1.2 提交事务

  commit;

1.3 回滚事务

  rollback;

相关文章: