游標 是預存程序/函數/觸發器中,用於逐行處理查詢結果集的資料庫物件。MySQL 游標特性:
| 陳述式 | 語法 | 用途 |
|---|---|---|
| 宣告 | DECLARE cursor_name CURSOR FOR SELECT_statement; | 定義游標對應的查詢 |
| 開啟 | OPEN cursor_name; | 執行查詢、建立結果集 |
| 擷取 | FETCH cursor_name INTO var_list; | 取得當前列並移至下一列 |
| 關閉 | CLOSE cursor_name; | 釋放記憶體資源 |
因為 MySQL 游標無 %NOTFOUND 屬性,必須宣告 CONTINUE HANDLER FOR NOT FOUND 來偵測結束:
DECLARE done INT DEFAULT FALSE;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
DELIMITER //
CREATE FUNCTION city_list() RETURNS VARCHAR(255)
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE v_city VARCHAR(50);
DECLARE v_result VARCHAR(255) DEFAULT '';
DECLARE cur CURSOR FOR SELECT city FROM employee;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO v_city;
IF done THEN LEAVE read_loop; END IF;
SET v_result = CONCAT(v_result, ', ', v_city);
END LOOP;
CLOSE cur;
RETURN SUBSTR(v_result, 3); -- 移除開頭逗號
END//
DELIMITER ;
SELECT city_list(); -- Toronto, Vancouver, Vancouver, ...
DELIMITER $$
CREATE PROCEDURE process_employees()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE v_id INT;
DECLARE v_name VARCHAR(30);
DECLARE v_city VARCHAR(30);
DECLARE cur CURSOR FOR SELECT id, first_name, city FROM employee;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: WHILE NOT done DO
FETCH cur INTO v_id, v_name, v_city;
IF done THEN LEAVE read_loop; END IF;
-- 這裡處理單列商業邏輯,例如:
SELECT v_id, v_name, v_city; -- 示範輸出
END WHILE;
CLOSE cur;
END$$
DELIMITER ;
CALL process_employees();
外層游標迴圈中可開啟內層游標,但每個游標都需獨立的 Handler 變數,否則內層結束會誤觸發外層結束。
MySQL 不支援 PREPARE 產生的陳述式直接作為游標來源,需先建立暫存表再對暫存表開啟游標。
語法自 MySQL 5.0 起支援,MariaDB 完全相容。MySQL 8.0+ 建議搭配 DECLARE ... CURSOR FOR EXECUTE stmt 使用預處理陳述式。
CREATE TABLE employee (
id INT, first_name VARCHAR(15), last_name VARCHAR(15),
start_date DATE, end_date DATE, salary FLOAT(8,2),
city VARCHAR(10), description VARCHAR(15)
);
INSERT INTO employee VALUES
(1,'Jason','Martin','1996-07-25','2006-07-25',1234.56,'Toronto','Programmer'),
(2,'Alison','Mathews','1976-03-21','1986-02-21',6661.78,'Vancouver','Tester'),
(3,'James','Smith','1978-12-12','1990-03-15',6544.78,'Vancouver','Tester'),
(4,'Celia','Rice','1982-10-24','1999-04-21',2344.78,'Vancouver','Manager'),
(5,'Robert','Black','1984-01-15','1998-08-08',2334.78,'Vancouver','Tester'),
(6,'Linda','Green','1987-07-30','1996-01-04',4322.78,'New York','Tester'),
(7,'David','Larry','1990-12-31','1998-02-12',7897.78,'New York','Manager'),
(8,'James','Cat','1996-09-17','2002-04-15',1232.78,'Vancouver','Tester');
掌握游標語法與 Handler 模式,即可在預存程序中實現複雜的逐列邏輯,但請記住:能用集合式 SQL 解決的,就不要用游標。