本學習筆記參考《MySQL必知必會》和官方手冊MySQL 5.6 Reference Manual
本文內容:
- MySQL存儲過程
- MySQL遊標
- MySQL觸發器
六、MySQL存儲過程
6.1 什麼是存儲過程
簡單來說,就是爲以後的使用而保存的一條或多條MySQL語句的集合。
6.2 使用存儲過程
(1) 創建存儲過程
mysql> DELIMITER //
mysql> CREATE PROCEDURE productpricing( )
-> BEGIN
-> SELECT Avg(prod_price) AS priceaverage
-> FROM products;
-> END //
Query OK, 0 rows affected (0.09 sec)
mysql> DELIMITER ;
上面的語句創建了一個名爲 productpricing 的存儲過程,productpricing( ) 的括號裏可以加入參數列表,BEGIN 和 END 之間爲過程體。由於 MySQL 語句的分隔符爲 ; ,而 mysql 命令行實用程序的分隔符也爲 ; ,爲了避免存儲過程體裏的 ; 不被 mysql 實用程序解釋,解決辦法是臨時更改命令行實用程序的語句分隔符。DELIMITER // 語句重新定義分隔符爲 // ,在創建完存儲過程後再用 DELIMITER ; 把分隔符改回來。
(2) 使用存儲過程
mysql> CALL productpricing( );
+————–+
| priceaverage |
+————–+
| 16.133571 |
+————–+
(3) 刪除存儲過程
存儲過程在創建之後,就被保存在服務器上以供使用,直至被刪除,刪除命令如下:
mysql> DROP PROCEDURE productpricing;
注意:存儲過程名後面沒有括號。
如果指定要刪除的存儲過程存在則刪除,如果不存在就會出錯。爲了使在不存在時也不至於出錯可使用這樣的語句:
mysql> DROP PROCEDURE IF EXISTS productpricing;
(4) 使用參數
mysql> DELIMITER //
mysql> CREATE PROCEDURE productpricing(
-> OUT pl DECIMAL(8, 2),
-> OUT ph DECIMAL(8, 2),
-> OUT pa DECIMAL(8, 2)
-> )
-> BEGIN
-> SELECT Min(prod_price)
-> INTO pl
-> FROM products;
-> SELECT Max(prod_price)
-> INTO ph
-> FROM products;
-> SELECT Avg(prod_price)
-> INTO pa
-> FROM products;
-> END //
Query OK, 0 rows affected (0.07 sec)
mysql> DELIMITER ;
此存儲過程接收3個參數:pl 存儲產品的最低價格,ph 存儲產品的最高價格,pa 存儲產品的平均價格。每個參數必須有指定的類型,這裏使用十進制。關鍵字 OUT 指出相應的參數用來從存儲過程傳出一個值(返回給調用者)。MySQL 支持 IN (傳遞給存儲過程)、OUT (從存儲過程傳出)和 INOUT (對存儲過程傳入和傳出)類型的參數。
調用這個存儲過程:
mysql> CALL productpricing(@pricelow, @pricehigh, @priceaverage);
Query OK, 1 row affected, 1 warning (0.04 sec)
mysql> SELECT @pricelow, @pricehigh, @priceaverage;
+———–+————+——————–+
| @pricelow | @pricehigh | @priceaverage |
+———–+————+——————–+
| 2.50 | 55.00 | 16.13 |
+———–+————+——————–+
所有 MySQL 變量都必須以 @ 開始。
另外一個例子:
mysql> DELIMITER //
mysql> CREATE PROCEDURE ordertotal(
-> IN onumber INT,
-> OUT ototal DECIMAL(8, 2)
-> )
-> BEGIN
-> SELECT Sum(item_price * quantity)
-> FROM orderitems
-> WHERE order_num = onumber
-> INTO ototal;
-> END //
onumber 定義爲 IN ,因爲訂單號被傳入存儲過程。ototal 定義爲 OUT ,因爲要從存儲過程返回合計。
mysql> CALL ordertotal(20005, @total);
mysql> SELECT @total;
+——–+
| @total |
+——–+
| 149.87 |
+——–+
(5) 建立智能存儲過程
-- create_procedure.sql
-- Name: ordertotal
-- Parameters: onumber = order number
-- taxable = 0 if not taxable, 1 if taxable
-- ototal = order total variable
DELIMITER //
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
-- Yes, so add taxrate to the total
SELECT total + (total / 100 * taxrate) INTO total;
END IF;
-- And finally, save to out variable
SELECT total INTO ototal;
END//
DELIMITER ;
此存儲過程有很大的變動。首先,增加了註釋(前面放置 –)。添加了另外一個參數 taxable,它是一個布爾值(如果要增加稅則爲真,否則爲假)。在存儲過程體中,用 DECLARE 語句定義了兩個局部變量。DECLARE 要求指定變量名和數據類型,它也支持可選的默認值。
COMMENT 關鍵字是可選的,如果給出,將在 SHOW PROCEDURE STATUS 的結果中顯示。
mysql> CALL ordertotal(20005, 0, @total);
mysql> SELECT @total;
+——–+
| @total |
+——–+
| 149.87 |
+——–+
mysql> CALL ordertotal(20005, 0, @total);
mysql> SELECT @total;
+——–+
| @total |
+——–+
| 158.86 |
+——–+
(6) 檢查存儲過程
檢查創建存儲過程的SQL語句:
SHOW CREATE PROCEDURE ordertotal;
如果想獲得詳細信息使用:
SHOW PROCEDURE STATUS; // 列出所有的存儲過程的詳細信息
可以使用 LIKE 起到過濾的作用:
SHOW PROCEDURE STATUS LIKE 'ordertotal';
七、MySQL遊標
7.1 什麼是遊標
有時,需要在檢索出來的行中前進或後退一行或多行。遊標(cursor)是一個存儲在MySQL服務器上的數據庫查詢,它不是一條 SELECT 語句,而是被該語句檢索出來的結果集。在存儲了遊標之後,應用程序可以根據需要滾動或瀏覽器中的數據。
7.2 使用遊標
步驟:
1) 在能夠使用遊標前,必須聲明(定義)它,這個過程實際上沒有檢索數據,它只是定義要使用的 SELECT 語句。
2) 一旦聲明後,必須打開遊標以供使用。這個過程用前面定義的 SELECT 語句把數據實際檢索出來。
3) 對於填有數據的遊標,根據需要取出各行。
4)在結束遊標使用時,必須關閉遊標。
(1) 創建遊標
-- create_cursor.sql
DELIMITER //
CREATE PROCEDURE processorder( )
BEGIN
-- Declare the cursor
DECLARE ordernumbers CURSOR
FOR
SELECT order_num FROM orders;
-- Open the cursor
OPEN ordernumbers;
-- Close the cursor
CLOSE ordernumbers;
END//
DELIMITER ;
MySQL中的遊標只能用於存儲過程,DECLARE 語句用來定義和命名遊標,這裏爲 ordernumbers,在存儲過程處理完成後,遊標就會消失,因爲它侷限於存儲過程。該存儲過程只是打開和關閉了遊標,並沒有使用裏面的數據。CLOSE 釋放遊標使用的內存資源,因此在每個遊標不再需要時都應該關閉。如果你不明確關閉遊標,MySQL 將會在到達END語句時自動關閉它。
(2) 使用遊標
-- use_cursor.sql
DELIMITER //
CREATE PROCEDURE processorder( )
BEGIN
-- Declare local variables
DECLARE done BOOLEAN DEFAULT 0;
DECLARE onumber INT ;
DECLARE t DECIMAL(8, 2);
-- Declare the 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
-- Get order number
FETCH ordernumbers INTO onumber;
-- Get the total for this order
CALL ordertotal(onumber, 1, t);
-- Insert order and total into ordertotals
INSERT INTO ordertotals(order_num, total) VALUES(onumber, t);
-- End of loop
UNTIL done END REPEAT;
-- Close the sursor
CLOSE ordernumbers;
END//
DELIMITER ;
在一個遊標被打開後,可以使用 FETCH 語句分別訪問它的每一行。FETCH 取出檢索的數據同時它還向前移動遊標中的內部行指針。該例子中的 FETCH 是在 REPEAT 內,因此它反覆執行直到 done 爲真(由 UNTIL done END REPEAT; 實現)。結束循環條件的語句爲:
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
這條語句定義了一個 CONTINUE HANDLER,它是在條件出現時被執行的代碼。這裏,它指出當 SQLSTATE ‘02000’ 出現時,SET done = 1。SQLSTATE ‘02000’ 是一個未找到條件,當 REPEAT 由於沒有更多的行共循環時,出現這個條件。
該存儲過程,計算出每個訂單號的帶稅的合計,並新建一個表,把這些數據插入到新建的表中。
mysql> source ./work/MySQL/use_cursor.sql;
mysql> CALL processorder( );
mysql> SELECT * FROM ordertotals;
+————+———–+
| order_num | total |
+————+———–+
| 20005 | 158.86 |
| 20009 | 40.78 |
| 20006 | 58.30 |
| 20007 | 1060.00 |
| 20008 | 132.50 |
| 20008 | 132.50 |
+————+———–+
八、MySQL觸發器
8.1 什麼是觸發器
如果你先讓某些語句在事件發生時自動執行,就需要用到觸發器。觸發器是 MySQL 響應以下任意語句而自動執行的一條 MySQL 語句:
DELETE; INSERT; UPDATE;
其它MySQL語句不支持觸發器。
8.2 創建觸發器
mysql> CREATE TRIGGER newproduct AFTER INSERT ON ordertotals
> FOR EACH ROW SELECT 'Product added' INTO @q;
創建觸發器的語法:
CREATE
[DEFINER = { user | CURRENT_USER }]
TRIGGER trigger_name
trigger_time trigger_event
ON tbl_name FOR EACH ROW
trigger_body
trigger_time: { BEFORE | AFTER }
trigger_event: { INSERT | UPDATE | DELETE }
注意:MySQL5.6 中的觸發器不能返回結果集; 只有表支持觸發器,視圖和臨時表都不支持。
這個觸發器,在每次向表 ordertotals 插入數據時(對於每行)都會執行 SELECT ‘Product added’ INTO @q 。
8.3 刪除觸發器
mysql> DROP TRIGGER newproduct;
觸發器不能更新或覆蓋,爲了修改一個觸發器,必須先刪除它然後重新創建。
8.4 使用觸發器
(1) INSERT觸發器
在 INSERT 觸發器代碼內,可引用一個名爲 NEW 的虛擬表,訪問被插入的行;
在 BEFORE INSERT 觸發器中, NEW 中的值也可以被更新(允許更改將要被插入的值);
對於 AUTO_INCREMENT 列,NEW在 INSERT 之前包含0,在INSERT 之後包含新的自動生成值。
mysql> CREATE TRIGGER neworder AFTER INSERT ON orders
-> FOR EACH ROW SELECT NEW.order_num INTO @o_num;
mysql> INSERT INTO orders(order_date, cust_id)
-> VALUES(Now(), 10001);
mysql> SELECT @o_num;
+———-+
| @o_num |
+———-+
| 20010 |
+———-+
(2) DELETE觸發器
在 DELETE 觸發器代碼內,你可以引用一個名爲 OLD 的虛擬表,訪問被刪除的行;
OLD 中的值全部都是隻讀的,不能更新;
mysql> DELIMITER //
mysql> CREATE TRIGGER deletemytable BEFORE DELETE ON mytable
-> FOR EACH ROW
-> BEGIN
-> SELECT OLD.name INTO @n;
-> INSERT INTO mytable_new(name, myphone) VALUES(OLD.name, OLD.phone);
-> END//
mysql> DELETE FROM mytable WHERE name = 'Joy';
mysql> SELECT * FROM mytable_new;
mysql> SELECT @n;
+——+
| @n |
+——+
| Joy |
+——+
在觸發器中使用 BEGIN END 塊的好處是觸發器能容納多條SQL語句。
(3) UPDATE觸發器
在 UPDATE 觸發器代碼中,你可以引用一個名爲 OLD 的虛擬表訪問以前(UPDATE語句執行前)的值,引用一個名爲 NEW 的虛擬表訪問新更新的值;
在 BERFORE UPDATE 觸發器中,NEW 中的值可能也被更新(允許更改將要用於UPDATE語句中的值);
OLD中的值全都是隻讀的,不能更新。
mysql> CREATE TRIGGER updatemytable BEFORE UPDATE ON mytable
-> FOR EACH ROW SET NEW.name = Upper(NEW.name);
mysql> UPDATE mytable SET name = 'John' WHERE myid = 1004;
mysql> SELECT name FROM mytable WHERE myid = 1004;
+——+
| name |
+——+
| JOHN |
+——+
早期版本(具體哪個版本開始可以不知)不允許在觸發器代碼中使用 CALL 調用存儲過程,在 MySQL 5.6 中是可以的。