# MySQL存储过程

# 创建游标存储过程

-- 将语句的结束符号从分号;临时改为两个$$(可以是自定义)
DELIMITER $$
USE `table_name`$$
-- 判断删除存储过程
DROP PROCEDURE IF EXISTS select_review$$
-- IN输入参数 OUT输出参数
CREATE DEFINER=`root`@`%` PROCEDURE select_review ( IN gs BIGINT,IN rid BIGINT,OUT result VARCHAR(255)) 
BEGIN

	DECLARE _email VARCHAR(32);
	DECLARE i INT;
	DECLARE r VARCHAR(255);
	DECLARE s INT DEFAULT 0;
	-- 遍历数据结束标志
	DECLARE done INT DEFAULT FALSE;
	-- 游标
	DECLARE r_name CURSOR FOR (SELECT email FROM company c JOIN u_user u ON c.id=u.company_id JOIN u_user_role ur ON u.id=ur.uid WHERE ur.rid=rid AND c.id=gs);
	-- 设定捕捉异常的结束标志
	DECLARE CONTINUE HANDLER FOR SQLWARNING, NOT FOUND, SQLEXCEPTION SET done = TRUE;
	-- 统计循环条数并赋值给变量i
	-- 赋值多列使用:列名1 ,列名2 INTO 变量名1,变量名2
	SELECT COUNT(nickname) INTO i FROM company c JOIN u_user u ON c.id=u.company_id JOIN u_user_role ur ON u.id=ur.uid WHERE ur.rid=rid AND c.id=gs;
	
	-- 打开游标
	OPEN r_name;
	-- 自定义循环体开始
	rLoop:LOOP
		-- 取值 赋值给变量,多个以,号分割
		-- FETCH NEXT FROM r_name INTO _email,password1,name1;
		FETCH r_name INTO _email;
				
		SET s=s+1;-- 循环一次加1
		IF(i=1) then
			SET r=CONCAT('[{"Push_MC":"',_email,'"}]');
		ELSEIF(s=1) THEN
			SET r=CONCAT('[{"Push_MC":"',_email,'"},');
		ELSEIF(s=i) THEN
			SET r=CONCAT(r,'{"Push_MC":"',_email,'"}]');
		ELSE
			SET r=CONCAT(r,'{"Push_MC":"',_email,'"},');
		END IF;
		
		-- 判断是否继续循环
		-- IF done OR s=i THEN
		IF s=i THEN
			LEAVE rLoop;-- 结束循环
			-- ITERATE rLoop;-- 跳过继续循环
		END IF;
		
	END LOOP rLoop;-- 结束自定义循环体
	CLOSE r_name;-- 关闭游标
		SET result=r;-- 返回结果
	-- 自定义异常
	-- SIGNAL SQLSTATE 'HY000' SET MESSAGE_TEXT = '错误!' ;
-- 使用自定义结束标志结束
END$$
-- 还原结束标志
DELIMITER ;

# 调用存储过程,查询返回结果

-- 调用存储过程,@sum变量为返回结果
call select_review(输入参数一,输入参数二,@sum);
-- 查询返回结果
select @sum e;

# 多次调用存储过程,并拼接结果

-- 调用存储过程,@sum变量为返回结果
call select_review(输入参数一,输入参数二,@sum);
-- 定义变量接收拼接返回结果
SET @e=CONCAT('[',@sum);

call select_review(输入参数一,输入参数二,@sum);
SET @e=CONCAT(@e,',',@sum);

SET @declaration=(SELECT email FROM u_user WHERE id=48);
SET @e=CONCAT(@e,',{"Push_MC":"',@declaration,'"}]');

-- 查询最终拼接结果
SELECT @e e;

# 循环

LOOPWHILEREPEAT

-- 将语句的结束符号从分号;临时改为两个$$(可以是自定义)
DELIMITER $$
USE `table_name`$$
-- 判断删除存储过程
DROP PROCEDURE IF EXISTS `add_year_month_info`$$

CREATE DEFINER=`yc`@`%` PROCEDURE `add_year_month_info`()
BEGIN
		/*
		CALL add_year_month_info()
		*/
		
		-- 统计循环次数变量
		DECLARE i INT DEFAULT 1;
		-- 需要循环次数变量
		DECLARE a INT DEFAULT 12;
		-- 设定捕捉异常的结束标志
		-- DECLARE CONTINUE HANDLER FOR SQLWARNING, NOT FOUND, SQLEXCEPTION SET done = TRUE;
		
		-- 自定义LOOP循环体开始
		rLoop:LOOP
			-- 判断是否继续循环
			IF (i>a) THEN
				-- 结束循环,意思同boeak
				LEAVE rLoop;
				-- 跳过继续循环
				-- ITERATE rLoop;
			END IF;
			-- 循环一次加1
			SET i=i+1;
			
			SELECT * FROM USER WHERE id=i;
			
		-- 结束自定义循环体
		END LOOP rLoop;
		
		-- 恢复初始值
		SET i=1;
		
		-- WHILE循环开始
		WHILE i<=a DO
			SET i=i+1;
			
			SELECT * FROM USER WHERE id=i;
			
		-- 循环结束
		END WHILE;
		
		-- 恢复初始值
		SET i=1;
		
		-- REPEAT循环开始
		REPEAT
			SET i=i+1;
			
			SELECT * FROM USER WHERE id=i;
			
		-- 循环结束
		UNTIL i>a END REPEAT;
	-- 使用自定义结束标志结束
	END$$

DELIMITER ;

# 异常处理

DELIMITER $$
-- 判断删除存储过程
DROP PROCEDURE IF EXISTS err;
CREATE PROCEDURE err() 
BEGIN
	DECLARE code VARCHAR(5) DEFAULT '00000';
	DECLARE msg TEXT;
	DECLARE errno INT;
	
	-- EXIT:发生错误时退出当前代码块
	-- DECLARE EXIT HANDLER FOR SQLEXCEPTION,SQLWARNING,NOT FOUND
	
	/*CONTINUE: 发送错误时继续执行后续代码
	SQLWARNING是对所有以01开头的SQLSTATE代码的速记
	NOT FOUND是对所有以02开头的SQLSTATE代码的速记
	SQLEXCEPTION是对所有没有被SQLWARNING或NOT FOUND捕获的SQLSTATE代码的速记*/
	DECLARE CONTINUE  HANDLER FOR SQLEXCEPTION,SQLWARNING,NOT FOUND
	
	BEGIN -- 捕获到异常时执行的代码块
	-- 赋值错误代码给code变量,赋值错误信息给msg变量
	GET DIAGNOSTICS CONDITION 1 code = RETURNED_SQLSTATE,errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
	-- 保存错误信息到日志表中
	-- INSERT INTO sql_log(errno,code,description,create_time,message) VALUES(errno,code,'存储过程:err',NOW(),msg);
	
	SELECT CONCAT("ERROR ", errno,'[', code, '] ', msg) sqlerrm;
		
	END;
	SELECT * FROM a;
	
END $$
DELIMITER;
CALL err();

# 插入或更新upsert

delimiter $
CREATE PROCEDURE merge_a_to_b () BEGIN
-- 定义需要插入从a表插入b表的过程变量
DECLARE _ID VARCHAR (16);
DECLARE _NAME VARCHAR (16);
-- 游标遍历数据结束标志
DECLARE done INT DEFAULT FALSE;
-- 游标指向a表结果集第一条-1位置
DECLARE cur_account CURSOR FOR SELECT ID, NAME FROM test_a;
-- 游标指向a表结果集最后一条加1位置 设置结束标志
DECLARE CONTINUE HANDLER FOR NOT FOUND  SET done = TRUE;
-- 打开游标
OPEN cur_account;
-- 遍历游标
read_loop :
LOOP
--  取值a表当前位置数据到临时变量
	FETCH NEXT FROM cur_account INTO _ID,_NAME;

-- 如果取值结束 跳出循环
IF done THEN LEAVE read_loop; 
END IF;

-- 当前数据做 对比 如果b表存在则更新时间 不存在则插入
IF NOT EXISTS ( SELECT 1 FROM TEST_B WHERE ID = _ID AND NAME=_NAME ) 
	THEN
		INSERT INTO TEST_B (ID, NAME,operatime) VALUES (_ID,_NAME,now());
	ELSE 
		UPDATE TEST_B  set operatime = now() WHERE ID = _ID AND NAME=_NAME;
END IF;

END LOOP;
CLOSE cur_account;

END $