1. 程式人生 > >一次使用儲存過程遊標遇到的坑

一次使用儲存過程遊標遇到的坑

一次使用儲存過程遊標遇到的坑

  有這樣一個需求:統計某省某市某區前6個月的資料,直接sql查詢效率很低,於是打算做定時任務,用定時器執行儲存過程的方式在每月初統計上月的相關資料。

  使用儲存過程就要用到遊標了,之前很少寫儲存過程,對遊標也不是熟悉,咋辦呢,現學現用啦。

建立儲存過程

 1 CREATE
 2     [DEFINER = { user | CURRENT_USER }]
 3  PROCEDURE sp_name ([proc_parameter[,...]])
 4     [characteristic ...] routine_body
 5  
 6 proc_parameter:
 7     [ IN | OUT | INOUT ] param_name type
 8  
 9 characteristic:
10     COMMENT 'string'
11   | LANGUAGE SQL
12   | [NOT] DETERMINISTIC
13   | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
14   | SQL SECURITY { DEFINER | INVOKER }
15  
16 routine_body:
17   Valid SQL routine statement
18  
19 [begin_label:] BEGIN
20   [statement_list]
21     ……
22 END [end_label]

MYSQL 儲存過程中的關鍵語法

宣告語句結束符,可以自定義:

1 DELIMITER $$
2 或
3 DELIMITER //

宣告儲存過程:

1 CREATE PROCEDURE demo_in_parameter(IN p_in int)

儲存過程開始和結束符號:

1 BEGIN .... END

變數賦值:

1 SET @p_in=1

變數定義:

1 DECLARE l_int int unsigned default 0;

建立mysql儲存過程、儲存函式:

1 create procedure 儲存過程名(引數)

儲存過程體:

1 create function 儲存函式名(引數)

 

遊標

簡介

    遊標實際上是一種能從包括多條資料記錄的結果集中每次提取一條記錄的機制。

    遊標充當指標的作用。

    儘管遊標能遍歷結果中的所有行,但他一次只指向一行。

    遊標的作用就是用於對查詢資料庫所返回的記錄進行遍歷,以便進行相應的操作。

 

用法

    一、宣告一個遊標: declare 遊標名稱 CURSOR for table;(這裡的table可以是查詢出來的任意集合)
    二、開啟定義的遊標:open 遊標名稱;
    三、獲得下一行資料:FETCH  遊標名稱 into testrangeid,versionid(和查出的欄位順序保持一致);
    四、需要執行的語句(增刪改查):這裡視具體情況而定
    五、釋放遊標:CLOSE 遊標名稱;
   注:mysql儲存過程每一句後面必須用;結尾,使用的臨時欄位需要在定義遊標之前進行宣告。

先寫之前出現的錯誤寫法:

  這樣寫出的雙重迴圈,裡面的會執行6次,外層只會執行一次,如果在後面把done置為0就會出現死迴圈。 

 1 CREATE DEFINER=`root`@`%` PROCEDURE `p_month_count_init`()
 2 BEGIN 
 3             
 4             -- 定義變數
 5             DECLARE done int DEFAULT 0;
 6             declare i int DEFAULT 0;
 7             declare v_yearMonth varchar(6);
 8             declare v_provinceCode varchar(50) default '';-- 省
 9             declare v_cityCode varchar(50) default '';-- 市
10 
11             -- 定義遊標,並將sql結果集賦值到遊標中
12             DECLARE cur CURSOR FOR 
13                 select province_code,city_code from dt_lift_info where is_delete = 0 group by province_code,city_code;
14             -- 聲明當遊標遍歷完後將標誌變數置成某個值
15             DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1;
16             -- 開啟遊標
17             open cur;
18             
19                     fetch cur into v_provinceCode,v_cityCode;
20                     -- 當done不等於1,也就是未遍歷完時,會一直迴圈
21                     while done<>1 do
22                             while i<6 DO -- 迴圈開始
23                                 set i=i+1;
24                                 set v_yearMonth = DATE_FORMAT( DATE_SUB(CURDATE(), INTERVAL i MONTH), '%Y%m');
25                                 call p_month_count(v_yearMonth,v_provinceCode,v_cityCode);
26                             end while; -- 迴圈結束
27                             -- 將遊標中的值再賦值給變數,供下次迴圈使用
28                             fetch cur into v_provinceCode,v_cityCode;
29                     -- 當s等於1時表明遍歷以完成,退出迴圈
30                     end while;
31             -- 關閉遊標
32             close cur;
33     END

 

  最後改成這樣寫就可以了。
 1 CREATE DEFINER=`root`@`localhost` PROCEDURE `p_month_count_init`()
 2     BEGIN 
 3             -- 定義變數
 4             DECLARE done int DEFAULT 0;
 5             declare i int DEFAULT 0;
 6             declare v_yearMonth varchar(6);
 7             declare v_provinceCode varchar(50) default '';-- 省
 8             declare v_cityCode varchar(50) default '';-- 市
 9             declare v_areaCode varchar(50) default '';-- 區
10 
11             -- 定義遊標,並將sql結果集賦值到遊標中
12             DECLARE cur CURSOR FOR 
13                 select province_code,city_code,area_code from dt_lift_info where is_delete = 0 group by province_code,city_code,area_code;
14             -- 聲明當遊標遍歷完後將標誌變數置成某個值
15             DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1;
16             
17             first_loop:LOOP
18                 IF i >= 6 THEN
19                     LEAVE first_loop;
20                 END IF;
21                 set i=i+1;
22                 set v_yearMonth = DATE_FORMAT( DATE_SUB(CURDATE(), INTERVAL i MONTH), '%Y%m');
23                 -- 開啟遊標
24                 open cur;
25                     second_loop:LOOP
26                         fetch cur into v_provinceCode,v_cityCode,v_areaCode;
27                         IF done = 1 THEN
28                             LEAVE second_loop;
29                         END IF;
30                         call p_month_count(v_yearMonth,v_provinceCode,v_cityCode,v_areaCode);
31                     end LOOP second_loop;
32                 SET done = 0; -- 注意這個別漏了
33                 -- 關閉遊標
34                 close cur;
35             END LOOP first_loop;
36     END

  做完這個儲存過程之後,瞬間感覺對儲存過程理解的深了一點。

&n