MySQL游標(Cursor)的定義及使用

2020-07-16 10:05:23
在 MySQL 中,儲存過程或函數中的查詢有時會返回多條記錄,而使用簡單的 SELECT 語句,沒有辦法得到第一行、下一行或前十行的資料,這時可以使用游標來逐條讀取查詢結果集中的記錄。游標在部分資料中也被稱為游標。

關聯式資料庫管理系統實質是面向集合的,在 MySQL 中並沒有一種描述表中單一記錄的表達形式,除非使用 WHERE 子句來限制只有一條記錄被選中。所以有時我們必須借助於游標來進行單條記錄的資料處理。

一般通過游標定位到結果集的某一行進行資料修改。

結果集是符合 SQL 語句的所有記錄的集合。

個人理解游標就是一個標識,用來標識資料取到了什麼地方,如果你了解程式語言,可以把他理解成陣列中的下標。

不像多數 DBMS,MySQL 游標只能用於儲存過程和函數。

 

下面介紹游標的使用,主要包括游標的宣告、開啟、使用和關閉。

1. 宣告游標

MySQL 中使用 DECLARE 關鍵字來宣告游標,並定義相應的 SELECT 語句,根據需要新增 WHERE 和其它子句。其語法的基本形式如下:

DECLARE cursor_name CURSOR FOR select_statement;

其中,cursor_name 表示游標的名稱;select_statement 表示 SELECT 語句,可以返回一行或多行資料。

例 1

下面宣告一個名為 nameCursor 的游標,程式碼如下:
mysql> DELIMITER //
mysql> CREATE PROCEDURE processnames()
    -> BEGIN
    -> DECLARE nameCursor CURSOR
    -> FOR
    -> SELECT name FROM tb_student;
    -> END//
Query OK, 0 rows affected (0.07 sec)
以上語句定義了 nameCursor 游標,游標只局限於儲存過程中,儲存過程處理完成後,游標就消失了。

2. 開啟游標

宣告游標之後,要想從游標中提取資料,必須首先開啟游標。在 MySQL 中,開啟游標通過 OPEN 關鍵字來實現,其語法格式如下:

OPEN cursor_name;

其中,cursor_name 表示所要開啟游標的名稱。需要注意的是,開啟一個游標時,游標並不指向第一條記錄,而是指向第一條記錄的前邊。

在程式中,一個游標可以開啟多次。使用者開啟游標後,其他使用者或程式可能正在更新資料表,所以有時會導致使用者每次開啟游標後,顯示的結果都不同。

3. 使用游標

游標順利開啟後,可以使用 FETCH...INTO 語句來讀取資料,其語法形式如下:

FETCH cursor_name INTO var_name [,var_name]...

上述語句中,將游標 cursor_name 中 SELECT 語句的執行結果儲存到變數引數 var_name 中。變數引數 var_name 必須在游標使用之前定義。使用游標類似高階語言中的陣列遍歷,當第一次使用游標時,此時游標指向結果集的第一條記錄。

MySQL 的游標是唯讀的,也就是說,你只能順序地從開始往後讀取結果集,不能從後往前,也不能直接跳到中間的記錄。

4. 關閉游標

游標使用完畢後,要及時關閉,在 MySQL 中,使用 CLOSE 關鍵字關閉游標,其語法格式如下:

CLOSE cursor_name;

CLOSE 釋放游標使用的所有內部記憶體和資源,因此每個游標不再需要時都應該關閉。

在一個游標關閉後,如果沒有重新開啟,則不能使用它。但是,使用宣告過的游標不需要再次宣告,用 OPEN 語句開啟它就可以了。

如果你不明確關閉游標,MySQL 將會在到達 END 語句時自動關閉它。游標關閉之後,不能使用 FETCH 來使用該游標。

例 2

建立 users 資料表,並插入資料,SQL 語句和執行結果如下:
mysql> CREATE TABLE `users`
    ->  (
    ->  `ID` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
    ->  `user_name` VARCHAR(60),
    ->  `user_pass` VARCHAR(64),
    ->  PRIMARY KEY (`ID`)
    -> );
Query OK, 0 rows affected (0.06 sec)

mysql> INSERT INTO users VALUES(null,'sheng','sheng123'),
    -> (null,'yu','yu123'),
    -> (null,'ling','ling123');
Query OK, 3 rows affected (0.01 sec)
建立儲存過程 test_cursor,並建立游標 cur_test,查詢 users 資料表中的第 3 條記錄,SQL 語句和執行過程如下:
mysql> DELIMITER //
mysql> CREATE PROCEDURE test_cursor (in param INT(10),out result VARCHAR(90))
    -> BEGIN
    -> DECLARE name VARCHAR(20);
    -> DECLARE pass VARCHAR(20);
    -> DECLARE done INT;
    -> DECLARE cur_test CURSOR FOR SELECT user_name,user_pass FROM users;
    -> DECLARE continue handler FOR SQLSTATE '02000' SET done = 1;
    -> IF param THEN INTO result FROM users WHERE id = param;
    -> ELSE
    -> OPEN cur_test;
    -> repeat
    -> FETCH cur_test into name,pass;
    -> SELECT concat_ws(',',result,name,pass) INTO result;
    -> until done
    -> END repeat;
    -> CLOSE cur_test;
    -> END IF;
    -> END //
Query OK, 0 rows affected (0.10 sec)

mysql> call test_cursor(3,@test)//
Query OK, 1 row affected (0.03 sec)

mysql> select @test//
+-----------+
| @test     |
+-----------+
| ling,ling123 |
+-----------+
1 row in set (0.00 sec)
建立 pro_users() 儲存過程,定義 cur_1 游標,將表 users 中的 user_name 欄位全部修改為 MySQL,SQL 語句和執行過程如下。
mysql> CREATE PROCEDURE pro_users()
    -> BEGIN
    -> DECLARE result VARCHAR(100);
    -> DECLARE no INT;
    -> DECLARE cur_1 CURSOR FOR SELECT user_name FROM users;
    -> DECLARE CONTINUE HANDLER FOR NOT FOUND SET no=1;
    -> SET no=0;
    -> OPEN cur_1;
    -> WHILE no=0 do
    -> FETCH cur_1 into result;
    -> UPDATE users SET user_name='MySQL'
    -> WHERE user_name=result;
    -> END WHILE;
    -> CLOSE cur_1;
    -> END //
Query OK, 0 rows affected (0.05 sec)

mysql> call pro_users() //
Query OK, 0 rows affected (0.03 sec)

mysql> SELECT * FROM users //
+----+-----------+-----------+
| ID | user_name | user_pass |
+----+-----------+-----------+
|  1 | MySQL     | sheng      |
|  2 | MySQL     | zhang     |
|  3 | MySQL     | ying      |
+----+-----------+-----------+
3 rows in set (0.00 sec)
結果顯示,users 表中的 user_name 欄位已經全部修改為 MySQL。