MySQL必知必會——視圖、存儲過程及遊標

一、視圖

1、瞭解視圖

視圖是虛擬的表,與包含數據的表不一樣,視圖只包含使用時動態檢索數據的查詢。

SELECT cust_name,cust_contact
FROM customers,orders,orderitems
WHERE customers.cust_id = orders.cust_id
AND orderitems.order_num = orders.order_num
AND  prod_id = 'TNT2';

該例檢索了訂購TNT2的客戶,任何需要知道這個數據的人都必須理解相關的表結構,並且知道如何創建查詢和對錶進行聯結。爲了檢索其他產品的相同數據,還必須修改最後的WHERE子句。
現在,假如可以把整個查詢包裝成一個名爲productcustomers的虛擬表。

SELECT cust_name,cust_contact
FROM productcustomers
WHERE prod_id = 'TNT2';

這就是視圖的作用,productcustomers是一個視圖,作爲視圖,它不包含表中應該有的任何列或數據。它包含的是一個SQL查詢。

1.1、視圖應用

視圖的常見應用

  • 重用SQL語句
  • 簡化複雜的SQL操作,在編寫查詢後,可以方便地重用它而不必知道它地基本查詢細節。
  • 使用表的組成部分而不是整個表。
  • 保護數據,可以給用用戶授予部分的訪問權限而不是整個表的訪問權限。
  • 更改數據格式和表示,視圖可返回與底層表的表示和格式不同的數據。

在視圖創建之後,可以用與表基本相同的方式利用它們,可以對視圖進行SELECT操作,過濾和排序數據,將視圖聯結到其他視圖或表,甚至能添加和更新數據。
視圖僅僅是用來查看存儲在別處的數據的一種設施, 視圖本身不包含數據,因此它們返回的數據是從其他表中檢索出來的。在添加或更改這些表中的數據時,視圖將返回改變過的數據。

性能問題: 因爲視圖不包含數據,所以每次使用視圖時,都必須處理查詢執行時所需的任一個檢索。如果你用多個聯結和過濾創建了複雜的視圖或者嵌套了視圖,性能會下降地很厲害。因此,在部署使用了大量視圖的應用前,應該進行測試。

1.2、視圖的規則和限制

  • 與表名一樣視圖必須唯一命名
  • 對於可以創建的視圖數目沒有限制
  • 爲了創建視圖,必須有足夠的訪問權限。這些限制通常由DBA授予。
  • 視圖可以嵌套,即可以利用從其他視圖中檢索數據的查詢來構造視圖。
  • ORDER BY可以在視圖中使用,但如果從該視圖檢索數據的SELECT語句中也包含有ORDER BY,那麼該視圖的ORDER BY則會被覆蓋。
  • 視圖不能索引,也不能有關聯的觸發器或默認值。
  • 視圖可以和表一起使用,例如編寫一條聯結表和視圖的SELECT語句。

2、使用視圖

視圖的創建

  • 視圖使用CREATE VIEW語句來創建
  • 使用SHOW CREATE VIEW viewname;來查看創建視圖的語句
  • 使用DROP刪除視圖,語法爲DROP VIEW viewname
  • 更新視圖時,可以先用DROP再用CREATE,也可以直接用CREATE OR REPLACE VIEW。如果要更新的視圖不存在,則第二條更新語句會創建一個視圖。若存在,則會替換原有視圖。

2.1、利用視圖簡化複雜的聯結

視圖最常用的應用之一就是隱藏複雜的SQL,這通常會涉及到聯結。

MariaDB [course]> CREATE VIEW productcustomers AS
    -> SELECT cust_name,cust_contact,prod_id
    -> FROM customers,orders,orderitems
    -> WHERE customers.cust_id = orders.cust_id
    -> AND orderitems.order_num = orders.order_num;
Query OK, 0 rows affected (0.01 sec)

上例創建了一個名爲productcustomers的視圖,它聯結了三個 表,以返回已經訂購了任意產品的所有客戶的列表。
現在可以使用該視圖進行檢索訂購了TNT2的客戶:

MariaDB [course]> SELECT cust_name,cust_contact
    -> FROM productcustomers
    -> WHERE prod_id = 'TNT2';
+----------------+--------------+
| cust_name      | cust_contact |
+----------------+--------------+
| Coyote Inc.    | Y Lee        |
| Yosemite Place | Y Sam        |
+----------------+--------------+
2 rows in set (0.00 sec)

上例通過WHERE子句從視圖中檢索特定數據,在MySQL處理查詢時,它將指定的WHERE子句添加到視圖查詢中已有WHERE子句中,以便正確過濾數據。
視圖極大地簡化了複雜 SQL語句的使用,使用視圖,可以一次性編寫基礎的SQL,然後根據需要多次使用。

2.2、利用視圖重新格式化檢索出的數據

視圖的另一種常見用途就是重新格式化檢索出的數據。

MariaDB [course]> CREATE VIEW vendorlocations AS 
    -> SELECT Concat(RTrim(vend_name),'(',RTrim(vend_country),')')
    ->        AS vend_title
    -> FROM vendors
    -> ORDER BY vend_name;
Query OK, 0 rows affected (0.00 sec)
MariaDB [course]> SELECT * FROM vendorlocations;
+------------------------+
| vend_title             |
+------------------------+
| ACME(USA)              |
| Anvils R Us(USA)       |
| Furball Inc.(USA)      |
| Jet Set(England)       |
| Jouets Et Ours(France) |
| LT Supplies(USA)       |
+------------------------+
6 rows in set (0.00 sec)

2.3、利用視圖過濾不想要的數據

視圖對於普通的WHERE子句也很有用,例如,可以定義customermaillist視圖,它過濾沒有郵箱的客戶。

MariaDB [course]> CREATE VIEW  customeremaillist AS 
SELECT cust_id,cust_name,cust_email  
FROM customers 
WHERE cust_email IS NOT NULL;
Query OK, 0 rows affected (0.00 sec)
MariaDB [course]> SELECT * FROM customeremaillist;
+---------+----------------+---------------------+
| cust_id | cust_name      | cust_email          |
+---------+----------------+---------------------+
|   10001 | Coyote Inc.    | ylee@coyote.com     |
|   10003 | Wascals        | rabbit@wascally.com |
|   10004 | Yosemite Place | sam@yosemite.com    |
|   10005 | The Fudds      | fudd@xupt.com       |
+---------+----------------+---------------------+
4 rows in set (0.00 sec)

2.4、使用視圖與計算字段

視圖對於簡化計算字段的使用特別有用,看下例:

MariaDB [course]> CREATE VIEW orderitemsexpanded AS 
    -> SELECT order_num,prod_id,quantity,item_price,quantity * item_price AS expanded_price
    -> FROM orderitems;
Query OK, 0 rows affected (0.01 sec)
MariaDB [course]> SELECT *
    -> FROM orderitemsexpanded
    -> WHERE order_num = 20005;
+-----------+---------+----------+------------+----------------+
| order_num | prod_id | quantity | item_price | expanded_price |
+-----------+---------+----------+------------+----------------+
|     20005 | ANV01   |       10 |       5.99 |          59.90 |
|     20005 | ANV02   |        3 |       9.99 |          29.97 |
|     20005 | TNT2    |        5 |      10.00 |          50.00 |
|     20005 | FB      |        1 |      10.00 |          10.00 |
+-----------+---------+----------+------------+----------------+
4 rows in set (0.00 sec)

視圖非常容易創建,而且很好使用,正確使用可以極大地簡化複雜的數據處理。

2.5、視圖的更新

視圖是可更新的,更新一個視圖將更新其基表,如果對視圖進行增加或刪除行,實際上是對其基表增加或刪除行。但是並非所有的視圖都是可更新的,如果MySQL不能正確地確定被更新的數據,則不允許更新。
即,視圖如果有如下操作則不能更新:
分組、聯結、子查詢、並、聚集函數、DISTINCT、導出列。
視圖主要用途還是進行數據檢索。

3、小結

視圖爲虛擬的表,它們包含的不是數據而是根據需要檢索數據的查詢。視圖提供了一種MySQL的SELECT語句層次的封裝,可用來簡化數據處理以及重新格式化基礎數據或保護基礎數據。

二、存儲過程

1、瞭解存儲過程

1.1、存儲過程概念

存儲過程(Stored Procedure): 存儲過程就是作爲可執行對象存放在數據庫中的一個或多個SQL命令。

通俗來講:存儲過程其實就是能完成一定操作的一組SQL語句。存儲過程是爲了完成特定功能的SQL語句集,經編譯創建並保存在數據庫中,用戶可通過指定存儲過程的名字並給定參數(需要時)來調用執行。
存儲過程思想上很簡單,就是數據庫 SQL 語言層面的代碼封裝與重用。
即SQL的執行可能需要考慮包含業務規則在內的智能處理。封裝操作的好處就不過多說明,無非是簡化,複用,降低耦合等,同時,它還具有更高的性能。

1.2、存儲過程特點

1.存儲過程只在創造時進行編譯,以後每次執行存儲過程都不需再重新編譯,而一般SQL語句每執行一次就編譯一次,所以使用存儲過程可提高數據庫執行速度。
2.當對數據庫進行復雜操作時,可將此複雜操作用存儲過程封裝起來與數據庫提供的事務處理結合一起使用。
3.存儲過程可以重複使用,可減少數據庫開發人員的工作量。
4.安全性高,可設定只有某些用戶才具有對指定存儲過程的使用權

2、存儲過程基本語句

基本語法:

--創建存儲過程 
CREATE PROCEDURE <存儲過程的名稱>(<變量的類型定義>)
BEGIN
  <執行操作>
END;

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

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

創建一個簡單的存儲過程

MariaDB [course]> DELIMITER $$      告訴命令行使用$$作爲新的分隔符
MariaDB [course]> CREATE PROCEDURE productpricing()
 BEGIN   
   SELECT AVG(prod_price) AS priceaverage   
   FROM  products; 
 END $$
Query OK, 0 rows affected (0.02 sec)
MariaDB [course]> DELIMITER ;   恢復爲原來的語句分隔符

BEGIN和END語句用來限定存儲過程體,過程體本身僅是一個簡單的SELECT語句。

mysql命令行的分隔符:如果命令行要解釋存儲過程自身內的;字符,則 它們最終不會成爲存儲過程的成分。這會使存儲過程中的SQL出現語法錯誤。解決辦法是臨時更改命令行的語句分隔符;

DELIMITER $$
或
DELIMITER //

調用存儲過程

MariaDB [course]> CALL productpricing();
+--------------+
| priceaverage |
+--------------+
|    16.133571 |
+--------------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)

刪除存儲過程

DROP PROCEDURE roductpricing;

3、帶有參數的存儲過程

MySQL存儲過程的參數用在存儲過程的定義,共有三種參數類型,IN,OUT,INOUT,形式如:

CREATEPROCEDURE 存儲過程名([[IN |OUT |INOUT ] 參數名 數據類形...])
  • IN 輸入參數:表示調用者向過程傳入值(傳入值可以是字面量或變量)
  • OUT 輸出參數:表示過程向調用者傳出值(可以返回多個值)(傳出值只能是變量)
  • INOUT 輸入輸出參數:既表示調用者向過程傳入值,又表示過程向調用者傳出值(值只能是變量)

上面的例子中的存儲過程簡單地顯示SELECT語句的結果。一般情況下,存儲過程並不顯示結果,而是把結果返回給你指定的變量。

CREATE PROCEDURE productpricing(
	OUT p1 DECIMAL(8,2),
	OUT ph DECIMAL(8,2),
	OUT pa DECIMAL(8,2)
)
BEGIN
	SELECT MIN(prod_price)
	INTO p1
	FROM products;
	SELECT MAX(prod_price)
	INTO ph
	FROM products;
	SELECT AVG(prod_price)
	INTO pa 
	FROM products;
END;

對該存儲過程進行調用,必須指定3個變量名:

CALL productpricing ( @pricelow, @pricehigh, @priceaverage );

在調用時這條語句並不顯示任何數據,它返回以後可以顯示(或在其他處理中使用)的變量。

MariaDB [course]> SELECT @pricehigh,@pricelow,@priceaverage;
+------------+-----------+---------------+
| @pricehigh | @pricelow | @priceaverage |
+------------+-----------+---------------+
|      55.00 |      2.50 |         16.13 |
+------------+-----------+---------------+
1 row in set (0.00 sec)

再來看一個使用IN和OUT參數的例子。

MariaDB [course]> CREATE PROCEDURE ordertotal(
 IN onum INT,
 OUT ototal DECIMAL(8,2) )
   BEGIN 
     SELECT SUM(item_price * quantity) 
     FROM orderitems 
     WHERE order_num = onum 
     INTO ototal; 
    END$$
Query OK, 0 rows affected (0.00 sec)
MariaDB [course]> DELIMITER ;

onum定義爲IN,因爲訂單號從調用中傳入存儲過程。ototal定義爲OUT,因爲要從存儲過程返回給調用。SELECT語句使用這兩個參數,WHERE子句使用onum選擇正確的行,INTO使用ototal存儲計算出來的合計。

MariaDB [course]> CALL  ordertotal(20005,@total);
Query OK, 1 row affected (0.00 sec)

MariaDB [course]> SELECT @total;
+--------+
| @total |
+--------+
| 149.87 |
+--------+
1 row in set (0.00 sec)

4、建立智能存儲過程

迄今爲止使用的所有存儲過程都是封裝MySQL簡單的SELECT語句,雖然它們全是有效的例子。但是它們所能完成的工作直接使用這些被封裝的語句就能完成。只有在從存儲過程內包含業務規則和智能處理時,它們才能展現真正的功能。
有這樣的場景,你需要獲得與以前一樣的訂單合計,但需要對訂單合計增加營業稅,不過只對某些顧客。
1、需要獲得合計。 2、把營業稅有條件的添加到合計 3、返回合計

-- 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;	

DECLARE用來定義存儲過程中的局部變量
INTO表示賦值到變量
IF <boolean> THEN <do something> END IF 爲條件執行語句,記得以END IF結尾
COMMENT非必需,如果有,那麼在SHOW PROCEDURE STATUS的結果時會顯示(簡單說,類似於方法的說明)
使用--來對SQL語句添加註釋


--不含營業稅
CALL ordertotal(20005, 0, @total);

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

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

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

5、檢查存儲過程

顯示用來創建一個存儲過程的CREATE語句,使用SHOW CREATE PROCEDUER語句。

SHOW CREATE PROCEDURE ordertotal;

爲了顯示包括何時、由誰創建等詳細信息的存儲過程列表,使用SHOW PROCEDURE STATUS;

三、遊標

1、簡介

遊標(cursor)是一個存儲在MySQL服務器上的數據庫查詢,它不是一條SELECT語句,而是被該語句檢索出來的結果集。
在存儲了遊標之後,應用程序就可以根據需要滾動或瀏覽其中的數據。遊標主要用於交互式應用,其中用戶需要滾動屏幕上的數據,並對數據進行瀏覽或做出更改。遊標只能用於存儲過程和函數。

2、使用方法

  • 聲明一個遊標: declare 遊標名稱 CURSOR for table;(這裏的table可以是你查詢出來的任意集合)
  • 打開定義的遊標:open 遊標名稱;
  • 獲得下一行數據:FETCH 遊標名稱 into testrangeid,versionid;
  • 需要執行的語句(增刪改查):這裏視具體情況而定
  • 釋放遊標:CLOSE 遊標名稱;

注:mysql存儲過程每一句後面必須用;結尾,使用的臨時字段需要在定義遊標之前進行聲明。

3、創建遊標

使用 DECLARE和CURSOR關鍵字:
DECLARE命名遊標,並定義相應的SELECT語句,根據需要帶WHERE和其他子句。

CREATE PROCEDURE processorders()
BEGIN
	DECLARE ordernumbers CURSOR
	FOR
	SELECT order_num FROM orders;
END;

存儲過程處理完成後,遊標就消失了。(它只侷限於存儲過程)

4、打開和關閉遊標

在存儲過程中要關鍵字OPEN進行打開。另,遊標相關的SELECT查詢語句,在定義時是不執行的,在OPEN時才執行查詢,存儲檢索出的數據以供瀏覽和滾動。在遊標使用完成後,使用CLOSE進行關閉:

	CREATE PROCEDURE processorders()
	BEGIN
		--Declare the cursor
		DECLARE ordernumbers CURSOR
		FOR
		SELECT order_num FROM orders;
		--Open the cursor
		OPEN ordernumbers;
		--Close the cursor
		CLOSE ordernumbers;
	END;

在一個遊標關閉後,如果沒有重新打開,則不能使用它。但是,使用聲明過的遊標不需要再次聲明,使用OPEN語句打開它就可以了。

4、使用遊標數據

打開了遊標之後,就可以使用關鍵字FETCH訪問每一行的數據了,FETCH是從第一行開始,獲取當前行的數據,每次執行後會移動內部行指針,再次調用FETCH則會檢索到下一行(不會重複讀取同一行):

CREATE PROCEDURE processorders()
	
BEGIN
	--Declare loval variable
	DECLARE a	INT
	--Declare the cursor
	DECLARE  ordernumbers CURSOR
    FOR
    SELECT prod_num FROM products;
    --Open the cursor 	
	OPEN ordernumbers;
	--Get order number;
	FETCH ordernumbers INTO a;
	--Close the cursor;
	CLOSE ordernumbers;
END;

其中FETCH 用來檢索當前行的order_num列(從第一行開始)到一個名爲a的局部變量中。對檢索出來的變量不做處理。

再看一個使用循環檢索數據的例子

CREATE PROCEDURE processorders()
BEGIN
	--Declare local variables
	DECLARE done BOOLEAN DEFAULT 0;
	DECLARE a 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 throught all rows
	REPEAT
		--Get order number
		FETCH ordernumbers INTO a;
		--Get total  for this order
		CALL ordertotal(a,1,t);
		-- Insert order and total into ordertotals
		INSERT INTO ordertotals
		VALUES(a,t);

	--End of the loop
	UNTIL done END REREAT;
	--Close the cursor
	CLOSE ordernumbers;
END;

在這個存儲過程中,遊標讀取訂單號,其中還調用了存儲過程ordertotal以獲得t值,最終將訂單號a和帶稅的合計值t循環插入ordertotals表中。
CONTINUE HANDLER是在條件出現時被執行的代碼,這裏它指出了當SQLSTATE '02000'出現時,SET done=1.SQLSTATE ‘02000’是一個未找到條件,當REPEAT由於沒有更多的行供循環而不能繼續時,就會出現這個條件。
MySQL錯誤代碼大全
上述存儲過程不返回數據,但它能夠創建和填充另一個表,可以用一個簡單的SELECT語句查看該表:

SELECT * FROM ordertotals;
+---------+---------+
|  20005  |  158.86 |
|  20006  |   58.30 |
|  20007  | 1060.00 |
|  20008  |  132.50 |
|  20009  |   40.78 |
+---------+---------+

這個樣例包含了存儲過程、遊標、逐行處理以及存儲過程調用其他存儲過程。
ordertotal存錯過程的代碼如下:

-- 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;	

發佈了115 篇原創文章 · 獲贊 30 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章