【MySQL】使用游標(cursor)走訪查詢結果中的資料


https://www.flickr.com/photos/8058098@N07/2718571627/

在預存程序中,可透過「SELECT」語句,得到想要的查詢結果。但如果我們想要的不是查看結果,而是像一般程式語言的陣列那樣,能夠逐一取出裡面的資料作處理,此時就需要用到「游標」(cursor)了。

本文會介紹游標的使用方式,並搭配「處理器」(handler)進行錯誤處理。



一、測試資料

以下是本文使用的測試資料。

員工表,包含員工編號與名字,共兩個欄位。

銷售紀錄表,包含員工編號、銷售日期與金額,共三個欄位。

根據以上的測試資料,以下的查詢語法,是統計 2023 年每位員工的銷售金額。

結果為:

emp_idsum_sales
20691000
207121940
20862800

讀者可以將查詢結果想像成「陣列」。在預存程序中,當我們想從查詢結果,逐一處理每一筆資料(就像處理陣列中的每一個元素),就需要用到「游標」(cursor)了。


二、游標(cursor)

以下的預存程序,是將上述查詢指定年度總銷售額的結果,儲存到叫做「employee_yearly_sales_snapshot」的獨立資料表中。

在上面的程式中,宣告了一個叫做「cursor_sales」的游標。語法為 DECLARE 名稱 CURSOR FOR 查詢語法;

游標有三個操作方式。

  • OPEN:開啟游標,將查詢結果放進快取。
  • FETCH INTO:取出下一筆資料的各個欄位值。
  • CLOSE:關閉游標,從快取清除資料。

Cursor 宛如一個「指針」,會在記憶體中移動到下一筆資料的位置,我們並無法從中得知總共有幾筆資料。於是在範例程式中,筆者暫時寫出 WHILE true DO 這樣的無窮迴圈,來不斷取出資料。到了第三節,我們再來做調整。

使用 FETCH 能從 cursor 取出下一筆資料,並透過 INTO,將欄位值賦予給各個變數。要注意的是,變數名稱的順序,在撰寫上必須與查詢結果的欄位相同。


三、錯誤處理

若讀者執行上面完成的預存程序,會發現資料確實有插入到新的資料表中。然而出現了以下的錯誤訊息:

Error Code: 1329. No data - zero rows fetched, selected, or processed

會出現此訊息,是因為 cursor 的 FETCH 操作已經走訪完所有資料,已經找不到下一筆了。讀者可在官方文件中搜尋「1329」這個錯誤碼(Error Code),會找到「02000」的狀態碼(SQLSTATE)。

為了因應這項錯誤,本節將介紹「處理器」(handler)來進行錯誤處理。處理器的宣告語法如下:

DECLARE 動作 HANDLER FOR 狀態
BEGIN
    處理流程
END;

宣告時有三個需要自定義的地方。

  • 動作(action):可選擇「EXIT」或「CONTINUE」。前者代表要停止執行當前的程式,後者則繼續執行。
  • 狀態(state):可提供的參數包含明確的狀態碼(如上述的 02000)、「SQLWARNING」(所有 01 開頭的狀態碼)、「NOT FOUND」(所有 02 開頭的狀態碼),以及「SQLEXCEPTION」(其他錯誤)。
  • 處理流程:發生錯誤時要執行的程式。

那麼,讓我們透過處理器來調整上一節的預存程序。以下會省略部份程式碼,讓讀者專注於使用方式。

此處用來取出 cursor 資料的迴圈,是透過名為「is_cursor_sales_done」的變數,來控制是否要循環。若試圖取出資料時發生「02000」狀態的錯誤,則處理器將該變數值設為 1,藉此離開迴圈。隨後繼續執行後面的程式碼。

在宣告處理器時,除了直接定義要處理的狀態碼,亦能考慮使用 `NOT FOUND` 關鍵字來囊括多個狀態碼,也能增加可讀性。


上一篇:【MySQL】使用選擇與迴圈敘述進行流程控制

下一篇:...

留言