項目需求:對表進行重新構建,這個用java的缺點是數據的傳送以及遍歷的話會消耗更多的資源,因此使用mysql的存儲過程進行構建。
具體要求:跳過原本設置的假期和課程本身的假期對數據進行重排。
1.游標從創建到關閉的過程: 注意class_id1 的屬性剛開始由declare設置的默認是空,但在打開游標之前只要給它重新賦值就不為空了。
DECLARE cc_2 CURSOR FOR SELECT id FROM pms_teach_example_day where class_id=class_id1 AND id>=(SELECT id FROM pms_teach_example_day WHERE class_id=class_id1 AND date is NULL ORDER BY id limit 1);
2.定義結束條件:02000是sql的狀態碼,意思是這條sql語句執行到最后了,當然在最開始的時候要定義done這個屬性,默認值設為0,如果這個done屬性還用在其他游標里,每次關閉游標前把done設置為0(重點),以下是定義結束條件的兩種寫法。
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1;
3.打開游標進行循環:循環也可以有兩種:一種就是repeat 另一種就是loop,loop的好處是可以控制循環何時跳出,記得打開loop要跟着跳出loop和關閉loop的結束語句,另外,在這里邊雖然沒有寫done 的值但是它默認結束后就會給你返回1,因此在關閉游標前依舊要把done的值設為0,這個很坑,因為mysql的變量都是全局變量,一處修改下次就不能用了,所以一定要重置,cc_2可以簡單理解為查詢的每一行。
寫法一:
OPEN cc_2;
tloop:LOOP
FETCH cc_2 INTO id2;
SET date2 = (SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(date3,',',j+1),',',-1));
IF i>j THEN
update pms_teach_example_day SET date = date2 WHERE id = id2 ;
SET j=j+1;
END IF;
IF i=j THEN
LEAVE tloop;
END if;
END LOOP tloop;
SET j =0;
SET done =0;
CLOSE cc_2;
寫法二:
OPEN t_index;
REPEAT
FETCH cc_2 INTO id2;
IF done!=1 THEN
//操作語句
END IF;
UNTIL DONE END REPEAT;
CLOSE t_index;
5.嵌套循環:嵌套循環最好用游標嵌套,然后兩個loop之類的,這里為了方便用了while-游標嵌套,b:BEGIN是寫了一個外部的標簽,因為我要循環365次,但是總不會真的要循環這么多次,當條件滿足后就可以通過這個標簽來進行跳出循環,跳出的語句跟loop一樣:LEAVE b; 嵌套循環實現跟java一樣,外部執行一次,里邊要完整的循環一次,注意END LOOP 之后這個內循環就已經關閉了,END LOOP 和CLOSE cc_2之間已經不算在循環里了,所以說標紅的之一段代碼是外循環里的,我的外循環是用jj來進行控制,內循環的話就是自然執行完,特別注意 j 這個屬性,這個屬性在內循環中一直使用,因此每次外循環調用的時候要重新給它賦值,這樣的話才能保證每次內循環 j 開始的值是固定的。
6.最后最后,一定要開始事務,將需要保持原子性的代碼都放在一個事務里,不然你有的執行成功有的執行不成功會造成數據混亂。
6.控制循環次數時,不要用內置函數,length,replace來控制,非常消耗性能。
7.注意,當沒有set的時候變量不會自己賦值,即declare 一個int 的變量時一定要初始化為0,這樣才能進入循環;
8.坑:done!=1和done=1,先看兩個截圖:
因為第二張圖的if后邊沒加結束的條件,所以循環到最后之后還是把i加1了,注意這個加一操作是在循環結束后加的,然后判斷done=1再跳出的時候就會發現i比正常的值多1。
9.代碼整理(沒加事務):