1、存儲過程
存儲過程是什么,簡單來講,就像Java中的
方法(函數),不過它是SQL世界中的方法。
大部分時候,我們所使用都是單條SQL,用來針對一個或多表連接。但是也有情況,是據判斷先對表A執行操作,變動后再結合表B進行操作。即SQL的執行可能需要
考慮包含業務規則在內的智能處理。封裝操作的好處就不過多說明,無非是簡化,復用,降低耦合等,同時,它還具有更高的性能。
考慮這種業務情況,現在你需要獲得訂單總額,但同時需要增加營業稅,且只針對某些顧客,那么你需要:
- 獲得基本的訂單總額
- 將營業稅有條件地添加到合計中
- 返回合計
1.1 基本語句
先看基本的語句,然后再看示例,就豁然開朗了:
--創建存儲過程
CREATE PROCEDURE <存儲過程的名稱>(<變量的類型定義>)
BEGIN
<執行操作>
END;
--執行存儲過程
CALL <存儲過程的名稱>(<@變量名>);
--刪除存儲過程
DROP PROCEDURE <存儲過程的名稱>;
11
1
--創建存儲過程
2
CREATE PROCEDURE <存儲過程的名稱>(<變量的類型定義>)
3
BEGIN
4
<執行操作>
5
END;
6
7
--執行存儲過程
8
CALL <存儲過程的名稱>(<@變量名>);
9
10
--刪除存儲過程
11
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;
29
1
-- Name: ordertotal
2
-- Parameters: onumber = order number
3
-- taxable = 0 if not taxable, 1 if taxable
4
-- ototal = order total variable
5
6
CREATE PROCEDURE ordertotal(
7
IN onumber INT,
8
IN taxable BOOLEAN,
9
OUT ototal DECIMAL(8, 2)
10
) COMMENT 'Obtain order total, optionally adding tax'
11
BEGIN
12
--Declare variable for total
13
DECLARE total DECIMAL(8, 2);
14
--Declare tax percentage
15
DECLARE taxrate INT DEFAULT 6;
16
17
--GET the order total
18
SELECT Sum(item_price*quantity)
19
FROM orderitems
20
WHERE order_num = onumber
21
INTO total;
22
23
--Is this taxable
24
IF taxable THEN
25
SELECT total+(total/100*taxrate) INTO total;
26
END IF;
27
28
SELECT total INTO ototal;
29
END;
看起來這么長好像挺唬人,其實很清楚:
CREATE PROCEDURE ordertotal(
IN onumber INT,
IN taxable BOOLEAN,
OUT ototal DECIMAL(8, 2)
) COMMENT 'Obtain order total, optionally adding tax'
5
1
CREATE PROCEDURE ordertotal(
2
IN onumber INT,
3
IN taxable BOOLEAN,
4
OUT ototal DECIMAL(8, 2)
5
) COMMENT 'Obtain order total, optionally adding tax'
- 使用CREATE PROCEDURE關鍵詞創建了名為ordertotal的存儲過程
- 該存儲過程定義了三個變量,IN表示要求輸入的參數,OUT表示輸出的結果。INT、BOOLEAN等表示變量的數據類型
- COMMENT非必需,如果有,那么在SHOW PROCEDURE STATUS的結果時會顯示(簡單說,類似於方法的說明)
BEGIN
...
END;
3
1
BEGIN
2
...
3
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;
10
1
--Declare variable for total
2
DECLARE total DECIMAL(8, 2);
3
--Declare tax percentage
4
DECLARE taxrate INT DEFAULT 6;
5
6
--GET the order total
7
SELECT Sum(item_price*quantity)
8
FROM orderitems
9
WHERE order_num = onumber
10
INTO total;
- DECLARE用來定義存儲過程中的局部變量
- INTO表示賦值到變量
--Is this taxable
IF taxable THEN
SELECT total+(total/100*taxrate) INTO total;
END IF;
4
1
--Is this taxable
2
IF taxable THEN
3
SELECT total+(total/100*taxrate) INTO total;
4
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;
}
12
1
public void ordertotal(int onumber, boolean taxable, double ototal) {
2
3
double total;
4
int taxrate = 6;
5
6
total = getOrderTotal(onumber);
7
if (taxable) {
8
total += total / (100 * taxrate);
9
}
10
11
ototal = total;
12
}
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 |
+-----------------+
19
1
--不含營業稅
2
CALL ordertotal(20005, 0, @total);
3
SELECT @total
4
5
+----------+
6
| @total |
7
+----------+
8
| 149.87 |
9
+----------+
10
11
--包含營業稅
12
CALL ordertotal(20005, 1, @total);
13
SELECT @total
14
15
+-----------------+
16
| @total |
17
+-----------------+
18
| 158.862200000 |
19
+-----------------+
定義時我們說過,IN表示定義輸入,OUT表示定義輸出,所以這里的三個變量中,前兩者由調用者傳入,而第三個變量,則作為返回結果的變量。
(
調用存儲過程時,用於臨時存儲返回數據的變量必須以@開頭)
1.4 檢查
用來顯示“創建某個存儲過程的CREATE語句”,使用SHOW CREATE PROCEDURE語句:
SHOW CREATE PROCEDURE ordertotal;
1
1
SHOW CREATE PROCEDURE ordertotal;
1.5 刪除
DROP PROCEDURE ordertotal;
1
1
DROP PROCEDURE ordertotal;
注意,檢查和刪除存儲過程,都不用加后面的(),只需要給出存儲過程的名稱即可。
2、游標
在檢索出來的行中,前進或者后退一行或多行,就需要用到所謂的“游標”。游標不是某個SELECT語句,但是它是被該
語句檢索出來的結果集,另外,
MySQL游標只能用於存儲過程(和函數)。
2.1 創建游標
使用
DECLARE和
CURSOR關鍵字:
CREATE PROCEDURE processorders()
BEGIN
DECLARE ordernumbers CURSOR
FOR
SELECT order_num FROM orders;
END;
6
1
CREATE PROCEDURE processorders()
2
BEGIN
3
DECLARE ordernumbers CURSOR
4
FOR
5
SELECT order_num FROM orders;
6
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;
13
1
CREATE PROCEDURE processorders()
2
BEGIN
3
--Declare
4
DECLARE ordernumbers CURSOR
5
FOR
6
SELECT order_num FROM orders;
7
8
--Open
9
OPEN ordernumbers;
10
11
--Close
12
CLOSE ordernumbers;
13
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;
17
1
CREATE PROCEDURE processorders()
2
BEGIN
3
--Declare
4
DECLARE o INT;
5
DECLARE ordernumbers CURSOR
6
FOR
7
SELECT order_num FROM orders;
8
9
--Open
10
OPEN ordernumbers;
11
12
--Get
13
FETCH ordernumbers INTO o;
14
15
--Close
16
CLOSE ordernumbers;
17
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;
31
1
CREATE PROCEDURE processorders()
2
BEGIN
3
--Declare local variables
4
DELCARE done BOOLEAN DEFAULT 0;
5
DECLARE o INT;
6
DECLARE t DECIMAL(8,2);
7
8
--Declare cursor
9
DECLARE ordernumbers CURSOR
10
FOR
11
SELECT order_num FROM orders;
12
13
--Declare continue handler
14
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1;
15
16
--Create a table to store the results
17
CREATE TABLE IF NOT EXISTS ordertotals(order_num INT, total DECIMAL(8,2));
18
19
--Open the cursor
20
OPEN ordernumbers;
21
22
--Loop through all rows
23
REPEAT
24
FETCH ordernumbers INTO o;
25
CALL ordertotal(o, 1, t);
26
INSERT INTO ordertotals(order_num, total) VALUES(o, t);
27
UNTIL done END REPEAT;
28
29
--Close the cursor
30
CLOSE ordernumbers;
31
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 |
+---------+---------+
x
1
SELECT * FROM ordertotals;
2
3
+---------+---------+
4
| 20005 | 158.86 |
5
| 20006 | 58.30 |
6
| 20007 | 1060.00 |
7
| 20008 | 132.50 |
8
| 20009 | 40.78 |
9
+---------+---------+