《MySQL必知必會》[05] 存儲過程和游標



1、存儲過程

存儲過程是什么,簡單來講,就像Java中的 方法(函數),不過它是SQL世界中的方法。

大部分時候,我們所使用都是單條SQL,用來針對一個或多表連接。但是也有情況,是據判斷先對表A執行操作,變動后再結合表B進行操作。即SQL的執行可能需要 考慮包含業務規則在內的智能處理。封裝操作的好處就不過多說明,無非是簡化,復用,降低耦合等,同時,它還具有更高的性能。

考慮這種業務情況,現在你需要獲得訂單總額,但同時需要增加營業稅,且只針對某些顧客,那么你需要:
  • 獲得基本的訂單總額
  • 將營業稅有條件地添加到合計中
  • 返回合計

1.1 基本語句

先看基本的語句,然后再看示例,就豁然開朗了:
--創建存儲過程 
CREATE PROCEDURE <存儲過程的名稱>(<變量的類型定義>)
BEGIN
  <執行操作>
END;

--執行存儲過程
CALL <存儲過程的名稱>(<@變量名>);

--刪除存儲過程
DROP PROCEDURE <存儲過程的名稱>;

1.2 創建

然后,根據剛才我們說到的返回包含營業稅的訂單總額,創建如下存儲過程:
-- Name: ordertotal
-- Parameters: onumber = order number
--             taxable = 0 if not taxable, 1 if taxable
--             ototal  = order total variable

CREATE PROCEDURE ordertotal(
  IN  onumber INT,
  IN  taxable BOOLEAN,
  OUT ototal DECIMAL(8, 2)
) COMMENT 'Obtain order total, optionally adding tax'
BEGIN
  --Declare variable for total
  DECLARE total DECIMAL(8, 2);
  --Declare tax percentage
  DECLARE taxrate INT DEFAULT 6;
  
  --GET the order total
  SELECT Sum(item_price*quantity)
  FROM orderitems
  WHERE order_num = onumber
  INTO total;

  --Is this taxable
  IF taxable THEN
    SELECT total+(total/100*taxrate) INTO total;
  END IF;

  SELECT total INTO ototal;
END;

看起來這么長好像挺唬人,其實很清楚:
CREATE PROCEDURE ordertotal(
  IN  onumber INT,
  IN  taxable BOOLEAN,
  OUT ototal DECIMAL(8, 2)
) COMMENT 'Obtain order total, optionally adding tax'
  • 使用CREATE PROCEDURE關鍵詞創建了名為ordertotal的存儲過程
  • 該存儲過程定義了三個變量,IN表示要求輸入的參數,OUT表示輸出的結果。INT、BOOLEAN等表示變量的數據類型
  • COMMENT非必需,如果有,那么在SHOW PROCEDURE STATUS的結果時會顯示(簡單說,類似於方法的說明)

BEGIN
  ...
END;
  • BEGIN和END用來界定存儲過程操作執行的語句

  --Declare variable for total
  DECLARE total DECIMAL(8, 2);
  --Declare tax percentage
  DECLARE taxrate INT DEFAULT 6;
  
  --GET the order total
  SELECT Sum(item_price*quantity)
  FROM orderitems
  WHERE order_num = onumber
  INTO total;
  • DECLARE用來定義存儲過程中的局部變量
  • INTO表示賦值到變量

  --Is this taxable
  IF taxable THEN
    SELECT total+(total/100*taxrate) INTO total;
  END IF;
  • IF <boolean> THEN <do something> END IF 為條件執行語句,記得END IF結尾

假如用Java來寫的話,大概是這么個意思:
public void ordertotal(int onumber, boolean taxable, double ototal) {

    double total;
    int taxrate = 6;
    
    total = getOrderTotal(onumber);
    if (taxable) {
        total += total / (100 * taxrate);
    }

    ototal = total;
}

1.3 執行

在1.2我們定義了存儲過程ordertotal(),則執行方式為:
--不含營業稅
CALL ordertotal(20005, 0, @total);
SELECT @total

+----------+
|  @total  |
+----------+
|  149.87  |
+----------+

--包含營業稅
CALL ordertotal(20005, 1, @total);
SELECT @total

+-----------------+
|      @total     |
+-----------------+
|  158.862200000  |
+-----------------+

定義時我們說過,IN表示定義輸入,OUT表示定義輸出,所以這里的三個變量中,前兩者由調用者傳入,而第三個變量,則作為返回結果的變量。
調用存儲過程時,用於臨時存儲返回數據的變量必須以@開頭

1.4 檢查

用來顯示“創建某個存儲過程的CREATE語句”,使用SHOW CREATE PROCEDURE語句:
SHOW CREATE PROCEDURE ordertotal;

1.5 刪除

DROP PROCEDURE ordertotal;
注意,檢查和刪除存儲過程,都不用加后面的(),只需要給出存儲過程的名稱即可。



2、游標

在檢索出來的行中,前進或者后退一行或多行,就需要用到所謂的“游標”。游標不是某個SELECT語句,但是它是被該 語句檢索出來的結果集,另外, MySQL游標只能用於存儲過程(和函數)。

2.1 創建游標

使用 DECLARECURSOR關鍵字:
CREATE PROCEDURE processorders()
BEGIN
  DECLARE ordernumbers CURSOR
  FOR
  SELECT order_num FROM orders;
END;

2.2 打開和關閉游標

因為游標局限於存儲過程,所以如果存儲過程處理完成后,游標就會消失。所以往往在存儲過程中要關鍵字 OPEN進行打開。另,游標相關的SELECT查詢語句,在定義時是不執行的,在OPEN時才執行查詢,存儲檢索出的數據以供瀏覽和滾動。在游標使用完成后,使用 CLOSE進行關閉:
CREATE PROCEDURE processorders()
BEGIN
  --Declare
  DECLARE ordernumbers CURSOR
  FOR
  SELECT order_num FROM orders;

  --Open
  OPEN ordernumbers;
  
  --Close
  CLOSE ordernumbers;
END;

2.3 使用游標數據

打開游標之后,我們就可以使用關鍵字 FETCH訪問數據了,FETCH是從第一行開始,獲取當前行的數據, 每次執行后會移動內部行指針,再次調用FETCH則會檢索到下一行(不會重復讀取同一行):
CREATE PROCEDURE processorders()
BEGIN
  --Declare
  DECLARE o INT;
  DECLARE ordernumbers CURSOR
  FOR
  SELECT order_num FROM orders;
 
  --Open
  OPEN ordernumbers;
 
  --Get
  FETCH ordernumbers INTO o;  
 
  --Close
  CLOSE ordernumbers;
END;

看一個復雜些的例子:
CREATE PROCEDURE processorders()
BEGIN
  --Declare local variables
  DELCARE done BOOLEAN DEFAULT 0;
  DECLARE o    INT;
  DECLARE t    DECIMAL(8,2);

  --Declare cursor
  DECLARE ordernumbers CURSOR
  FOR
  SELECT order_num FROM orders;
 
  --Declare continue handler
  DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1;

  --Create a table to store the results
  CREATE TABLE IF NOT EXISTS ordertotals(order_num INT, total DECIMAL(8,2));  

  --Open the cursor
  OPEN ordernumbers;
 
  --Loop through all rows
  REPEAT
    FETCH ordernumbers INTO o;
    CALL ordertotal(o, 1, t);
    INSERT INTO ordertotals(order_num, total) VALUES(o, t);
  UNTIL done END REPEAT;

  --Close the cursor
  CLOSE ordernumbers;
END;
  • 以上存儲過程,游標不斷讀取訂單號,並以此為參調用另一個存儲過程,將最終的值填入到表ordertotals中
  • CONTINUE HANDLER 是在條件出現時執行的代碼,SQLSTATE '02000' 表沒有找到更多的行(MySQL錯誤代碼)

SELECT * FROM ordertotals;

+---------+---------+
|  20005  |  158.86 |
|  20006  |   58.30 |
|  20007  | 1060.00 |
|  20008  |  132.50 |
|  20009  |   40.78 |
+---------+---------+



免責聲明!

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



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