MySQL 5.0+ 支援游標,**僅限預存程序/函數/觸發器** 中使用,且為**唯讀、單向、不可捲動** (敏感游標)。核心流程:DECLARE → OPEN → FETCH 迴圈 → CLOSE。
CREATE PROCEDURE demo_cursor()
BEGIN
-- 1. 宣告變數
DECLARE done INT DEFAULT FALSE;
DECLARE v_seqname VARCHAR(50);
DECLARE v_value BIGINT;
-- 2. 宣告游標 (SELECT 不含 INTO)
DECLARE cur CURSOR FOR SELECT seqname, value FROM sys_sequence;
-- 3. 宣告 NOT FOUND 錯誤處理器 (關鍵:游標結束觸發)
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
-- 4. 開啟游標
OPEN cur;
-- 5. 迴圈 FETCH
read_loop: LOOP
FETCH cur INTO v_seqname, v_value;
IF done THEN
LEAVE read_loop;
END IF;
-- 處理邏輯
SELECT v_seqname, v_value;
END LOOP;
-- 6. 關閉游標
CLOSE cur;
END;
done=TRUE,迴圈判斷退出。**必須宣告**,否則 FETCH 結束拋出錯誤中斷程序。CONTINUE 執行完處理器繼續後續語句 (配合旗標);EXIT 直接離開當前 BEGIN...END 區塊。UPDATE ... WHERE CURRENT OF cur (MySQL 不支援)。利用 BEGIN ... END 區塊隔離變數作用域,每層宣告自己的 done 旗標與 NOT FOUND 處理器 (同一區塊內不可重複宣告同類處理器)。
CREATE PROCEDURE nested_cursor()
BEGIN
DECLARE done_outer INT DEFAULT FALSE;
DECLARE v_id INT;
DECLARE cur_outer CURSOR FOR SELECT id FROM parent_table;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done_outer = TRUE;
OPEN cur_outer;
outer_loop: LOOP
FETCH cur_outer INTO v_id;
IF done_outer THEN LEAVE outer_loop; END IF;
-- 內層區塊:獨立變數與處理器
BEGIN
DECLARE done_inner INT DEFAULT FALSE;
DECLARE v_child_name VARCHAR(50);
DECLARE cur_inner CURSOR FOR SELECT name FROM child_table WHERE parent_id = v_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done_inner = TRUE;
OPEN cur_inner;
inner_loop: LOOP
FETCH cur_inner INTO v_child_name;
IF done_inner THEN LEAVE inner_loop; END IF;
SELECT v_id, v_child_name;
END LOOP;
CLOSE cur_inner;
END;
END LOOP;
CLOSE cur_outer;
END;
游標的 SELECT 必須靜態,若需動態表名/欄位/條件,改用 PREPARE / EXECUTE / DEALLOCATE PREPARE:
CREATE PROCEDURE dynamic_sql(IN tbl_name VARCHAR(64), IN cond_val INT)
BEGIN
SET @sql = CONCAT('SELECT * FROM `', tbl_name, '` WHERE id > ?');
PREPARE stmt FROM @sql;
SET @val = cond_val;
EXECUTE stmt USING @val;
DEALLOCATE PREPARE stmt;
END;
? 佔位符對應 USING 變數,防注入。USING @a, @b, @c。DEALLOCATE 釋放。LIMIT、調大 max_execution_time。SELECT ... INTO @var 觀察中間值,或寫入臨時表/日誌表。