mysql之游標


游標

        在操作mysql的時候我們知道MySQL檢索操作返回一組稱為結果集的行。這組返回的行都是與 SQL語句相匹配的行(零行或多行)。使用簡單的 SELECT句,例如,沒有辦法得到第一行、下一行或前 10行,也不存在每次一行地處理所有行的簡單方法(相對於成批地處理它們)。有時,需要在檢索出來的行中前進或后退一行或多行。這就是使用游標的原因。游標( cursor)是一個存儲在MySQL服務器上的數據庫查詢,它不是一條 SELECT語句,而是被該語句檢索出來的結果集。在存儲了游標之后,應用程序可以根據需要滾動或瀏覽其中的數據。游標主要用於交互式應用,其中用戶需要滾動屏幕上的數據,並對數據進行瀏覽或做出更改。
注意:只能用於存儲過程。不像多數 DBMS,MySQL 游標只能用於存儲過程(和函數)。
 
使用游標
    使用游標涉及幾個明確的步驟。
        1、在能夠使用游標前,必須聲明(定義)它。這個過程實際上沒有檢索數據,它只是定義要使用的 SELECT語句。
        2、一旦聲明后,必須打開游標以供使用。這個過程用前面定義的SELECT語句把數據實際檢索出來。
        3、對於填有數據的游標,根據需要取出(檢索)各行。
        4、在結束游標使用時,必須關閉游標。
    在聲明游標后,可根據需要頻繁地打開和關閉游標。在游標打開后,可根據需要頻繁地執行取操作。
 
創建游標
    游標用 DECLARE語句創建 DECLARE命名游標,並定義相應的 SELECT語句,根據需要帶WHERE和其他子句。例如,下面的語句定義了名為 ordernumbers的游標,使用了可以檢索所有訂單的 SELECT語句。
mysql> delimiter //
mysql> create procedure duhuo()
    -> begin
    -> declare ordnums cursor
    -> for
    -> select order_num from orders;
    -> end
    -> //
    這個存儲過程並沒有做很多事情, DECLARE語句用來定義和命名游標,這里為 ordnums存儲過程處理完成后,游標就消失(因為它局限於存儲過程)。在我們定義了游標之后。可以打開它。
 
打開與關閉游標
    游標使用OPEN CURSOR語句來打開。如下:
OPEN ordnums;
    在處理 OPEN語句時執行查詢,存儲檢索出的數據以供瀏覽和滾動。
 
    游標處理完成后,應當使用如下語句關閉游標:
close ordnums;
    CLOSE釋放游標使用的所有內部內存和資源,因此在每個游標不再需要時都應該關閉。
    在一個游標關閉后,如果沒有重新打開,則不能使用它。但是,使用聲明過的游標不需要再次聲明,用 OPEN語句打開它就可以了。此外mysql具有隱含關閉功能。如果你不明確關閉游標, MySQL將會在到達END句時自動關閉它。
 
    下面是前面例子的修改版本:
mysql> delimiter //
mysql> create procedure duhuo1()
    -> begin
    -> declare dudu cursor
    -> for
    -> select order_num from orders;
    -> open dudu;
    -> close dudu;
    -> end
    -> //
    這個存儲過程聲明、打開和關閉一個游標。但對檢索出的數據什么也沒做。
 
使用游標數據
    在一個游標被打開后,可以使用 FETCH語句分別訪問它的每一行。FETCH指定檢索什么數據(所需的列),檢索出來的數據存儲在什么地方。它還向前移動游標中的內部行指針,使下一條FETCH語句檢索下一行(不重復讀取同一行)。
 
    第一個例子從游標中檢索單個行(第一行):
mysql> delimiter //
mysql>  create procedure demo0()
    -> begin
    -> declare tx int;
    -> declare du1 cursor
    -> for
    -> select order_num from orders;
    -> open du1;
    -> fetch du1 into tx;
    -> close du1;
    -> end
    -> //
Query OK, 0 rows affected (0.02 sec)
    其中 FETCH用來檢索當前行的order_num列(將自動從第一行開始)到一個名為 tx的局部聲明的變量中。對檢索出的數據不做任何處理。
 
    在下一個例子中,循環檢索數據,從第一行到最后一行:
mysql> create procedure demo0()
    -> begin
    -> delcare done boolean default 0;
    -> declare du int;
    -> declare ordernu cursor
    -> for
    -> select order_num from orders;
    -> declare continue handler for sqlstate '02000' set done = 1;
    -> open ordernu;
    -> repeat
    -> fetch ordernu into du;
    -> until done end repeat;
    -> close ordernu;
    -> end//
    與前一個例子一樣,這個例子使用 FETCH檢索當前order_num到聲明的名為 o的變量中。但與前一個例子不一樣的是,這個例子中的 FETCH是在REPEAT 內,因此它反復執行直到 done為真(由UNTILdone END REPEAT;規定)。為使它起作用,用一個 DEFAULT 0(假,不結束)定義變量 done。那么,done 怎樣才能在結束時被設置為真呢?答案是用以下語句:
declare continue handler for sqlstate '02000' set done = 1;
    這條語句定義了一個 CONTINUE HANDLER,它是在條件出現時被執行的代碼。這里,它指出當 SQLSTATE '02000'出現時,SET done=1 SQLSTATE '02000'是一個未找到條件,當REPEAT由於沒有更多的行供循環而不能繼續時,出現這個條件。
    關於MySQL 5使用的 MySQL錯誤代碼列表,請參閱 http://dev.mysql.com/doc/mysql/en/error-handling.html 
    注意:DECLARE語句的次序。DECLARE語句的發布存在特定的次序。DECLARE語句定義的局部變量必須在定義任意游標或句柄之前定義,而句柄必須在游標之后定義。不遵守此順序將產生錯誤消息。
    如果調用這個存儲過程,它將定義幾個變量和一個 CONTINUEHANDLER,定義並打開一個游標,重復讀取所有行,然后關閉游標。如果一切正常,你可以在循環內放入任意需要的處理(在 FETCH語句之后,循環結束之前)。
 
    為了把這些內容組織起來,下面給出我們的游標存儲過程樣例的更進一步修改的版本,這次對取出的數據進行某種實際的處理:
mysql> delimiter //
mysql> create procedure liwei()
    -> begin
    -> declare done boolean default 0;
    -> declare li int;
    -> declare wei decimal(8,2);
    -> declare numb cursor
    -> for
    -> select order_num from orders;
    -> declare continue handler for sqlstate '02000' set done=1;
    -> create table if not exists ordertotals
    -> (order_num int,total decimal(8,2));
    -> open numb;
    -> repeat
    -> fetch ordernumbers into li;
    -> call ordertotals(tx,1,t);
    -> insert into ordertotals(order_num, total)
    -> values(li,wei);
    -> until done end repeat;
    -> close numd;
    -> end
    -> //
    在這個例子中,我們增加了另一個名為 t的變量(存儲每個訂單的合計)。此存儲過程還在運行中創建了一個新表(如果它不存在的話),名為 ordertotals。這個表將保存存儲過程生成的結果。 FETCH像以前一樣取每個 order_num,然后用CALL 執行另一個存儲過程(我們在前一章中創建)來計算每個訂單的帶稅的合計(結果存儲到 t)。最后,INSERT保存每個訂單的訂單號和合計。
 
    調用存儲過程:
call liwei();
 
    此存儲過程不返回數據,但它能夠創建和填充另一個表,可以用一條簡單的 SELECT語句查看該表:
select *
from ordertotals;
    輸出:
+----------------+--------------+
| order_num | total |
+----------------+--------------+
|   20005   |   158.56    |
|   20006   |  25.78  |
|   20007   |  1068.00   | 
+----------------+--------------+
    這樣,我們就得到了存儲過程、游標、逐行處理以及存儲過程調用其他存儲過程的一個完整的工作樣例。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM