目錄
一、簡介
1、概念
遊標實際上是一種能從包括多條數據記錄的結果集中每次提取一條記錄的機制。
- 使用遊標(cursor)的一個主要的原因就是把集合操作轉換成單個記錄處理方式。
- 遊標充當指針的作用。
- 儘管遊標能遍歷結果中的所有行,但他一次只指向一行。
- 用SQL語言從數據庫中檢索數據後,結果放在內存的一塊區域中,且結果往往是一個含有多個記錄的集合。
- 遊標機制允許用戶逐行地訪問這些記錄,按照用戶自己的意願來顯示和處理這些記錄。
2、優點
允許程序對由查詢語句select返回的行集合中的每一行執行相同或不同的操作,而不是對整個行集合執行同一個操作。
2、提供對基於遊標位置的表中的行進行刪除和更新的能力。
3、遊標實際上作爲面向集合的數據庫管理系統(RDBMS)和麪向行的程序設計之間的橋樑,使這兩種處理方式通過遊標溝通起來。
3、原理
遊標就是把數據按照指定要求提取出相應的數據集,然後逐條進行數據處理。
二、使用遊標
1、聲明遊標
DECLARE cursor_name CURSOR FOR select_statement (table)
這個語句聲明一個遊標。也可以在子程序中定義多個遊標,但是一個塊中的每一個遊標必須有唯一的名字
2、遊標OPEN語句
-- 打開之前聲明的遊標
OPEN cursor_name
3、遊標FETCH語句
-- 這個語句用指定的打開遊標讀取下一行,並且前進遊標指針
FETCH cursor_name INTO var_name[,var_name]
4、遊標CLOSE語句
-- 關閉之前打開的
CLOSE cursor_name
三、實例
1、實例1
1.需求
實例說明:編寫兩個表 sys_user和 user, 編寫存儲過程,當 兩個表的 id 相同時 將 user 表中的 name 更新爲 sys_user 中的 user_name
2. sys_user 和 user 表的創建
-- sys_user 表的創建
CREATE TABLE `sys_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_name` varchar(128) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
-- user 表的創建
CREATE TABLE `user` (
`id` int(11) DEFAULT NULL,
`name` varchar(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
3. 數據的對比,編寫的存儲過程會將 user表中的 name 全部更新
4. 存儲過程的編寫
DELIMITER $$
CREATE PROCEDURE user_test()
BEGIN
-- 定義變量
DECLARE sys_user_id BIGINT;
DECLARE sys_user_name VARCHAR(11);
DECLARE done INT;
-- 創建遊標,並存儲數據
DECLARE cur_test CURSOR FOR
SELECT id AS user_id,user_name AS sys_user_name FROM `sys_user`;
-- 遊標中的內容執行完後將 done 設置爲 1
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1;
-- 打開遊標
OPEN cur_test;
-- 執行循環
posLoop:LOOP
-- 判斷是否結束循環
IF done=1 THEN
LEAVE posLoop;
END IF;
-- 取遊標中的值
FETCH cur_test INTO sys_user_id,sys_user_name;
-- 執行更新操作
UPDATE `user` SET NAME=sys_user_name WHERE id=sys_user_id;
END LOOP posLoop;
-- 釋放遊標
CLOSE cur_test;
END $$
DELIMITER
DROP PROCEDURE user_test;
-- 調用存儲過程
CALL user_test;
我們再次對比兩張表的數據變化
利用存儲過程和遊標我們將 user 表的 name 字段進行了 更新。
2、實例2
1.需求:統計 tb_user 表中 香港演員的數量
2.tb_user 表的創建
CREATE TABLE `tb_user` (
`user_id` int(12) NOT NULL AUTO_INCREMENT,
`username` varchar(32) DEFAULT NULL,
`birthday` date DEFAULT NULL,
`gender` varchar(2) DEFAULT NULL,
`address` varchar(128) DEFAULT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8
3. tb_user 表中的數據
4.存儲過程的創建
-- 統計 香港的演員有多少個
SELECT COUNT(*) AS username_cnt FROM mybatisdb.`tb_user` WHERE address='香港';
DELIMITER $$
CREATE PROCEDURE statisticUser()
BEGIN
-- 創建接收遊標數據的變量
DECLARE c INT;
-- 創建總數變量
DECLARE total INT DEFAULT 0;
-- 創建結束標誌變量
DECLARE done INT DEFAULT 0;
-- 創建遊標
DECLARE cur_test CURSOR FOR
SELECT COUNT(*) AS username_cnt FROM mybatisdb.`tb_user` WHERE address='香港';
-- 指定遊標循環結束時的返回值
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
-- 設置初始值
SET total = 0;
-- 打開遊標
OPEN cur_test;
-- 開始循環遊標裏的數據
read_loop:LOOP
-- 根據遊標當前指向的一條數據
FETCH cur_test INTO c;
-- 判斷遊標的循環是否結束
IF done THEN
LEAVE read_loop; -- 跳出遊標循環
END IF;
-- 獲取一條數據時,將 count 值進行累加操作
SET total = total + c;
-- 結束遊標循環
END LOOP;
CLOSE cur_test;
-- 輸出結果
SELECT total;
END $$
DELIMITER
DROP PROCEDURE statisticUser;
CALL statisticUser();
5. 結果
五、補充
fetch是獲取遊標當前指向的數據行,並將指針指向下一行,當遊標已經指向最後一行時繼續執行會造成遊標溢出。
使用loop循環遊標時,他本身是不會監控是否到最後一條數據了,像下面代碼這種寫法,就會造成死循環;
read_loop:LOOP
FETCH cur_test INTO c;
SET total = total + c;
END LOOP;
在MySql中,造成遊標溢出時會引發mysql預定義的NOT FOUND錯誤,所以在上面使用下面的代碼指定了當引發not found錯誤時定義一個continue 的事件,指定這個事件發生時修改done變量的值。
所以在循環時加上了下面這句代碼:
--判斷遊標的循環是否結束
if done then
leave read_loop; --跳出遊標循環
end if;
如果done的值是true,就結束循環。繼續執行下面的代碼