1. 程式人生 > >mysql遊標的用法及作用

mysql遊標的用法及作用

1
當前有三張表A、B、C其中A和B是一對多關係,B和C是一對多關係,現在需要將B中A表的主鍵存到C中;
常規思路就是將B中查詢出來然後通過一個update語句來更新C表就可以了,但是B表中有2000多條資料,
難道要執行2000多次?顯然是不現實的;最終找到寫一個儲存過程然後通過迴圈來更新C表,
然而儲存過程中的寫法用的就是遊標的形式。


【簡介】
遊標實際上是一種能從包括多條資料記錄的結果集中每次提取一條記錄的機制。

遊標充當指標的作用。

儘管遊標能遍歷結果中的所有行,但他一次只指向一行。

遊標的作用就是用於對查詢資料庫所返回的記錄進行遍歷,以便進行相應的操作。


【用法】
一、宣告一個遊標: declare 遊標名稱 CURSOR for table;(這裡的table可以是你查詢出來的任意集合)
二、開啟定義的遊標:open 遊標名稱;
三、獲得下一行資料:FETCH 遊標名稱 into testrangeid,versionid;
四、需要執行的語句(增刪改查):這裡視具體情況而定
五、釋放遊標:CLOSE 遊標名稱;
注:mysql儲存過程每一句後面必須用;結尾,使用的臨時欄位需要在定義遊標之前進行宣告。


【例項】

-
BEGIN

--定義變數
declare testrangeid BIGINT;
declare versionid BIGINT;
declare done int;
--建立遊標,並存儲資料
declare cur_test CURSOR for
select id as testrangeid,version_id as versionid from tp_testrange;
--遊標中的內容執行完後將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 testrangeid,versionid;
--執行更新操作
update tp_data_execute set version_id=versionid where testrange_id = testrangeid;
END LOOP posLoop;
--釋放遊標
CLOSE cur_test;

 

END - ​
例子2:

我們現在要用儲存過程做一個功能,統計iphone的總庫存是多少,並把總數輸出到控制檯。

--在windows系統中寫儲存過程時,如果需要使用declare宣告變數,需要新增這個關鍵字,否則會報錯。
delimiter //
drop procedure if exists StatisticStore;
CREATE PROCEDURE StatisticStore()
BEGIN
--建立接收遊標資料的變數
declare c int;
declare n varchar(20);
--建立總數變數
declare total int default 0;
--建立結束標誌變數
declare done int default false;
--建立遊標
declare cur cursor for select name,count from store where name = 'iphone';
--指定遊標迴圈結束時的返回值
declare continue HANDLER for not found set done = true;
--設定初始值
set total = 0;
--開啟遊標
open cur;
--開始循環遊標裡的資料
read_loop:loop
--根據遊標當前指向的一條資料
fetch cur into n,c;
--判斷遊標的迴圈是否結束
if done then
leave read_loop; --跳出遊標迴圈
end if;
--獲取一條資料時,將count值進行累加操作,這裡可以做任意你想做的操作,
set total = total + c;
--結束遊標迴圈
end loop;
--關閉遊標
close cur;

--輸出結果
select total;
END;
--呼叫儲存過程
call StatisticStore();

fetch是獲取遊標當前指向的資料行,並將指標指向下一行,當遊標已經指向最後一行時繼續執行會造成遊標溢位。
使用loop循環遊標時,他本身是不會監控是否到最後一條資料了,像下面程式碼這種寫法,就會造成死迴圈;

read_loop:loop
fetch cur into n,c;
set total = total+c;
end loop;

在MySql中,造成遊標溢位時會引發mysql預定義的NOT FOUND錯誤,所以在上面使用下面的程式碼指定了當引發not found錯誤時定義一個continue 的事件,指定這個事件發生時修改done變數的值。

declare continue HANDLER for not found set done = true;
1
所以在迴圈時加上了下面這句程式碼:

--判斷遊標的迴圈是否結束
if done then
leave read_loop; --跳出遊標迴圈
end if;

如果done的值是true,就結束迴圈。繼續執行下面的程式碼

使用方式

遊標有三種使用方式:
第一種就是上面的實現,使用loop迴圈;
第二種方式如下,使用while迴圈:

drop procedure if exists StatisticStore1;
CREATE PROCEDURE StatisticStore1()
BEGIN
declare c int;
declare n varchar(20);
declare total int default 0;
declare done int default false;
declare cur cursor for select name,count from store where name = 'iphone';
declare continue HANDLER for not found set done = true;
set total = 0;
open cur;
fetch cur into n,c;
while(not done) do
set total = total + c;
fetch cur into n,c;
end while;

close cur;
select total;
END;

call StatisticStore1();

第三種方式是使用repeat執行:

drop procedure if exists StatisticStore2;
CREATE PROCEDURE StatisticStore2()
BEGIN
declare c int;
declare n varchar(20);
declare total int default 0;
declare done int default false;
declare cur cursor for select name,count from store where name = 'iphone';
declare continue HANDLER for not found set done = true;
set total = 0;
open cur;
repeat
fetch cur into n,c;
if not done then
set total = total + c;
end if;
until done end repeat;
close cur;
select total;
END;

call StatisticStore2();

遊標巢狀
在mysql中,每個begin end 塊都是一個獨立的scope區域,由於MySql中同一個error的事件只能定義一次,如果多定義的話在編譯時會提示Duplicate handler declared in the same block。

drop procedure if exists StatisticStore3;
CREATE PROCEDURE StatisticStore3()
BEGIN
declare _n varchar(20);
declare done int default false;
declare cur cursor for select name from store group by name;
declare continue HANDLER for not found set done = true;
open cur;
read_loop:loop
fetch cur into _n;
if done then
leave read_loop;
end if;
begin
declare c int;
declare n varchar(20);
declare total int default 0;
declare done int default false;
declare cur cursor for select name,count from store where name = 'iphone';
declare continue HANDLER for not found set done = true;
set total = 0;
open cur;
iphone_loop:loop
fetch cur into n,c;
if done then
leave iphone_loop;
end if;
set total = total + c;
end loop;
close cur;
select _n,n,total;
end;
begin
declare c int;
declare n varchar(20);
declare total int default 0;
declare done int default false;
declare cur cursor for select name,count from store where name = 'android';
declare continue HANDLER for not found set done = true;
set total = 0;
open cur;
android_loop:loop
fetch cur into n,c;
if done then
leave android_loop;
end if;
set total = total + c;
end loop;
close cur;
select _n,n,total;
end;
begin

end;
end loop;
close cur;
END;

call StatisticStore3();

上面就是實現一個巢狀迴圈,當然這個例子比較牽強。湊合看看就行。

動態SQL
Mysql 支援動態SQL的功能,

set @sqlStr='select * from table where condition1 = ?';
prepare s1 for @sqlStr;
--如果有多個引數用逗號分隔
execute s1 using @condition1;
--手工釋放,或者是 connection 關閉時, server 自動回收
deallocate prepare s1;