1 使用視圖(VIEW)
1.1 視圖
視圖是虛擬的表,只包含使用時(shí)動(dòng)態(tài)檢索數(shù)據(jù)的查詢。
視圖的常見應(yīng)用:
- 重用SQL語(yǔ)句。
- 簡(jiǎn)化復(fù)雜的SQL操作。在編寫查詢后,可以方便地重用它而不必知道其基本查詢細(xì)節(jié)。
- 使用表的一部分而不是整個(gè)表。
- 保護(hù)數(shù)據(jù)。可以授予用戶訪問表的特定部分的權(quán)限,而不是整個(gè)表的訪問權(quán)限。
- 更改數(shù)據(jù)格式和提示。視圖可返回與底層表的表示和格式不同的數(shù)據(jù)。
創(chuàng)建視圖之后,可以用與表基本相同的方式使用它們。可以對(duì)視圖執(zhí)行SELECT操作,過濾和排序數(shù)據(jù),將視圖聯(lián)結(jié)到其他視圖或表,甚至添加和更新數(shù)據(jù)。
創(chuàng)建和使用視圖的一些常見規(guī)則和限制:
- 與表一樣,視圖必須唯一命名,不能給視圖取與別的視圖或表相同的名字。
- 對(duì)于可以創(chuàng)建的視圖數(shù)目沒有限制。
- 創(chuàng)建視圖,必須有足夠的訪問權(quán)限。
- 視圖可以嵌套,可以利用從其他視圖中檢索數(shù)據(jù)的查詢來構(gòu)造視圖。
- 視圖不能索引,也不能有關(guān)聯(lián)的觸發(fā)器或默認(rèn)值。
- 有些DBMS把視圖作為只讀的查詢,這表示可以從視圖檢索數(shù)據(jù),但不能將數(shù)據(jù)寫回底層表。
1.2 創(chuàng)建視圖CREATE VIEW
視圖是虛擬的表,只包含使用時(shí)動(dòng)態(tài)檢索數(shù)據(jù)的查詢。
- 視圖用CREATE VIEW語(yǔ)句來創(chuàng)建。
- 使用SHOW CREATE VIEW viewname;來查看創(chuàng)建視圖的語(yǔ)句。
- 用DROP刪除視圖,其語(yǔ)法為DROP VIEW viewname;。
- 更新視圖時(shí),可先用DROP再用CREATE,也可以直接用CREATE OR REPLACE VIEW。如果要更新的視圖不存在,則第2條更新語(yǔ)句會(huì)創(chuàng)建一個(gè)視圖;如果要更新的視圖存在,則第2條更新語(yǔ)句會(huì)替換原有視圖。
1.2.1 利用視圖簡(jiǎn)化復(fù)雜的聯(lián)結(jié)
常見的視圖應(yīng)用是隱藏復(fù)雜的SQL。
(1)先創(chuàng)建ProductCustomers視圖。
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;
(2)檢索訂購(gòu)了產(chǎn)品RGAN01的顧客。
SELECT cust_name, cust_contact
FROM ProductCustomers
WHERE prod_id = 'RGAN01';
利用視圖,可一次性編寫基礎(chǔ)的SQL,然后根據(jù)需要多次使用。
1.2.2 用視圖重新格式化檢索出的數(shù)據(jù)
將供應(yīng)商Vendors表中的vend_name和vend_country合并輸出顯示。
CREATE VIEW VendorLocations AS
SELECT concat(RTRIM(vend_name), '(', RTRIM(vend_country), ')')
AS vend_title
FROM Vendors;
SELECT *
FROM VendorLocations;
1.2.3 用視圖過濾不想要的數(shù)據(jù)
定義CustomerEmailList視圖,過濾沒有Email的顧客。
CREATE VIEW CustomerEmailList AS
SELECT cust_id, cust_name, cust_email
FROM Customers
WHERE cust_email IS NOT NULL;
SELECT *
FROM CustomerEmailList;
1.2.4 使用視圖與計(jì)算字段
檢索某個(gè)訂單的產(chǎn)品,計(jì)算每種產(chǎn)品的總價(jià)格。
CREATE VIEW OrderItemsExpanded AS
SELECT order_num, prod_id, quantity, item_price,
quantity * item_price AS expanded_price
FROM OrderItems;
SELECT *
FROM OrderItemsExpanded
WHERE order_num = 20008;
1.2.5 更新視圖
迄今為止的所有視圖都是和SELECT語(yǔ)句使用的。然而,視圖的數(shù)據(jù)能否更新?答案視情況而定。
通常,視圖是可更新的,即,可以對(duì)它們使用INSERT、UPDATE和DELETE。更新一個(gè)視圖將更新其基表,如果對(duì)視圖增加或者刪除行,實(shí)際上是對(duì)其基表增加或刪除行。
但是,并非所有視圖都是可更新的。基本上可以說,如果MySQL不能正確地確定被更新的基數(shù)據(jù),則不允許更新(包括插入和刪除)。
換句話說,如果視圖定義中有以下操作,則不能進(jìn)行視圖的更新。
- 分組(使用GROUP BY和HAVING)。
- 聯(lián)結(jié)。
- 子查詢。
- 并。
- 聚集函數(shù)(MIN()、COUNT()等)。
- DISTINCT。
- 導(dǎo)出(計(jì)算)列。
換句話說,本章的許多例子的視圖都是不可更新的。這聽上去好像是一個(gè)嚴(yán)重的限制,但實(shí)際上不是,因?yàn)橐晥D主要用于數(shù)據(jù)檢索。
一般,應(yīng)該將視圖用于檢索(SELECT語(yǔ)句)而不用于更新(INSERT、UPDATE和DELETE)。
2 使用存儲(chǔ)過程
2.1 存儲(chǔ)過程
迄今為止,使用的大多數(shù)SQL語(yǔ)句都是針對(duì)一個(gè)或多個(gè)表的單條語(yǔ)句。并非所有操作都這么簡(jiǎn)單,經(jīng)常會(huì)有一個(gè)完整的操作需要多條語(yǔ)句才能完成。
比如,考慮以下情況:
- 為了處理訂單,需要核對(duì)以保證庫(kù)存中有相應(yīng)的物品。
- 如果庫(kù)存有物品,這些物品需要預(yù)定以便不將它們?cè)儋u給別的人,并且要減少可用的物品數(shù)量以及反映正確的庫(kù)存量。
- 庫(kù)存中沒有的物品需要訂購(gòu),這需要與供應(yīng)商進(jìn)行某種交互。
- 關(guān)于哪些物品入庫(kù)(并且可以立即發(fā)貨)和哪些物品退訂,需要通知相應(yīng)的客戶。
執(zhí)行上述的例子,需要針對(duì)許多表的多條SQL語(yǔ)句。此外,需要執(zhí)行的具體語(yǔ)句及其次序也不是固定的,它們可能會(huì)(和將)根據(jù)哪些物品在庫(kù)存中,哪些不在而變化。
存儲(chǔ)過程簡(jiǎn)單來說,就是為以后的使用而保存的一條或多條SQL語(yǔ)句的集合。可將其視為批文件,但它們的作用不限于批處理。
2.2 為什么要使用存儲(chǔ)過程
使用存儲(chǔ)過程的主要理由:
- 通過把處理封裝在容易使用的單元中,簡(jiǎn)化復(fù)雜的操作。
- 由于不要求反復(fù)建立一系列處理步驟,這保證了數(shù)據(jù)的完整性。如果所有開發(fā)人員和應(yīng)用程序都使用同一(試驗(yàn)和測(cè)試)存儲(chǔ)過程,則使用的代碼都是相同的。(這一點(diǎn)的延伸就是防止錯(cuò)誤,需要執(zhí)行的步驟越多,出錯(cuò)的可能性就越大。防止錯(cuò)誤保證了數(shù)據(jù)的一致性。)
- 簡(jiǎn)化對(duì)變動(dòng)的管理。如果表名、列名或業(yè)務(wù)邏輯有變化,只需要更改存儲(chǔ)過程的代碼,使用它的人員甚至不需要知道這些變化。(這一點(diǎn)的延伸就是安全性,通過存儲(chǔ)過程限制對(duì)基礎(chǔ)數(shù)據(jù)的訪問減少了數(shù)據(jù)訛誤的機(jī)會(huì)。)
- 提高性能。因?yàn)槭褂么鎯?chǔ)過程比使用簡(jiǎn)單的SQL語(yǔ)句要快。
- 存在一些只能用在單個(gè)請(qǐng)求中的MySQL元素和特性,存儲(chǔ)過程可以使用它們來編寫功能更強(qiáng)更靈活的代碼。
使用存儲(chǔ)過程的3個(gè)主要好處就是:簡(jiǎn)單、安全、高性能。
存在的缺陷:
- 一般來說,存儲(chǔ)過程的編寫比基本SQL語(yǔ)句復(fù)雜,編寫存儲(chǔ)過程需要更高的技能,更豐富的經(jīng)驗(yàn)。
- 可能沒有創(chuàng)建存儲(chǔ)過程的安全訪問權(quán)限。許多數(shù)據(jù)庫(kù)管理員限制存儲(chǔ)過程的創(chuàng)建權(quán)限,允許用戶使用存儲(chǔ)過程,但不允許他們創(chuàng)建存儲(chǔ)過程。
2.3 使用存儲(chǔ)過程
2.3.1 執(zhí)行存儲(chǔ)過程
MySQL稱存儲(chǔ)過程的執(zhí)行為調(diào)用,因此MySQL執(zhí)行存儲(chǔ)過程的語(yǔ)句為CALL。CALL接受存儲(chǔ)過程的名字以及需要傳遞給它的任意參數(shù)。
CALL productpricing(@pricelow,@pricehigh,@priceaverage);
執(zhí)行名為productpricing的存儲(chǔ)過程,它計(jì)算并返回產(chǎn)品的最低、最高和平均價(jià)格。
2.3.2 創(chuàng)建存儲(chǔ)過程
CREATE PROCEDURE productpricing()
BEGIN
SELECT AVG(prod_price) AS priceaverage
FROM Products;
END;
由于默認(rèn)的MySQL語(yǔ)句分隔符為;,如果命令行實(shí)用程序要解釋存儲(chǔ)過程自身內(nèi)的;字符,則它們最終不會(huì)成為存儲(chǔ)過程的成分,這會(huì)導(dǎo)致存儲(chǔ)過程中的SQL出現(xiàn)句法錯(cuò)誤,正如上圖所示。
解決辦法就是使用更改命令行實(shí)用程序的語(yǔ)法分隔符,如下所示:
DELIMITER // # 臨時(shí)改成//分隔符
CREATE PROCEDURE productpricing()
BEGIN
SELECT AVG(prod_price) AS priceaverage
FROM Products;
END//
DELIMITER ; # 改成默認(rèn);分隔符,不然以后寫的SQL語(yǔ)句都要以//作為結(jié)尾,才不會(huì)報(bào)錯(cuò)。
調(diào)用存儲(chǔ)過程:
CALL productpricing();
CALL productpricing();執(zhí)行剛創(chuàng)建的存儲(chǔ)過程并顯示返回的結(jié)果。因?yàn)榇鎯?chǔ)過程實(shí)際上是一種函數(shù),所以存儲(chǔ)過程名后需要有()符號(hào),即使不傳遞參數(shù)也需要。
可以看作調(diào)用一個(gè)定義好的函數(shù),一定要帶()符號(hào)。
2.3.3 刪除存儲(chǔ)過程
刪除剛創(chuàng)建的存儲(chǔ)過程:
DROP PROCEDURE productpricing;
刪除存儲(chǔ)過程的時(shí)候,只需給出存儲(chǔ)過程名即可,不需要帶上()符號(hào)。
** 建議:如果指定的存儲(chǔ)過程不存在的話,則DROP PROCEDURE將會(huì)產(chǎn)生錯(cuò)誤,為防止出現(xiàn)錯(cuò)誤,可使用DROP PROCEDURE IF EXISTS。**
DROP PROCEDURE IF EXISTS productpricing;
2.3.4 使用帶參數(shù)的存儲(chǔ)過程
productpricing只是一個(gè)簡(jiǎn)單的存儲(chǔ)過程,它簡(jiǎn)單地顯示SELECT語(yǔ)句的結(jié)果。一般,存儲(chǔ)過程并不顯示結(jié)果,而是把結(jié)果返回給你指定的變量。
注意:創(chuàng)建下面的存儲(chǔ)過程的時(shí)候,記得先刪除以前創(chuàng)建的productpricing。
DELIMITER //
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 //
DELIMITER ;
此存儲(chǔ)過程接受3個(gè)參數(shù):pl存儲(chǔ)產(chǎn)品最低價(jià)格,ph存儲(chǔ)產(chǎn)品最高價(jià)格,pa存儲(chǔ)產(chǎn)品平均價(jià)格。
MySQL支持IN(傳遞給存儲(chǔ)過程)、OUT(從存儲(chǔ)過程傳出)和INOUT(對(duì)存儲(chǔ)過程傳入和傳出)類型的參數(shù)。
存儲(chǔ)過程的代碼位于BEGIN和END語(yǔ)句內(nèi),它們是一系列SELECT語(yǔ)句,用來檢索值,然后保存到相應(yīng)的變量(通過指定INTO關(guān)鍵字)。
為調(diào)用此存儲(chǔ)過程,必須指定3個(gè)變量名:
CALL productpricing(@pricelow,
@pricehigh,
@priceaverage);
所有MySQL變量都必須以@開始。
為了顯示檢索出的產(chǎn)品平均價(jià)格:
SELECT @priceaverage;
為獲得3個(gè)值,可使用以下語(yǔ)句:
SELECT @pricehigh, @pricelow, @priceaverage;
創(chuàng)建另一個(gè)存儲(chǔ)過程,這次使用IN和OUT參數(shù)。ordertotal接受訂單號(hào)并返回該訂單的合計(jì):
DELIMITER //
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 //
DELIMITER ;
調(diào)用該存儲(chǔ)過程時(shí),必須給ordertotal傳遞兩個(gè)參數(shù):第一個(gè)參數(shù)為訂單號(hào),第二個(gè)參數(shù)為包含計(jì)算出來的合計(jì)的變量名。
CALL ordertotal(20005, @total);
SELECT @total;
CALL ordertotal(20009, @total);
SELECT @total;
2.3.5 建立智能存儲(chǔ)過程
上述的所有存儲(chǔ)過程基本上都是封裝MySQL簡(jiǎn)單的SELECT語(yǔ)句,只有在存儲(chǔ)過程內(nèi)包含業(yè)務(wù)規(guī)則和智能處理時(shí),它們的作用才能真正顯現(xiàn)出來。
考慮如下場(chǎng)景,你需要獲得與以前一樣的訂單合并,但需要對(duì)合計(jì)增加營(yíng)業(yè)稅,不過只針對(duì)某些顧客。那么,你需要做下面幾件事情:
- 獲得合計(jì)。
- 把營(yíng)業(yè)稅有條件地添加到合計(jì)。
- 返回合計(jì)(帶稅或者不帶稅)。
存儲(chǔ)過程的完整工作如下(記得先刪除以前創(chuàng)建的ordertotal存儲(chǔ)過程):
DELIMITER //
CREATE PROCEDURE ordertotal(
IN onumber INT,
IN taxable BOOLEAN,
OUT ototal DECIMAL(8,2)
)
BEGIN
# 聲明局部變量
DECLARE total DECIMAL(8,2);
DECLARE taxrate INT DEFAULT 6;
SELECT SUM(item_price * quantity)
FROM OrderItems
WHERE order_num = onumber
INTO total;
IF taxable THEN
SELECT total + (total/100 * taxrate) INTO total;
END IF;
# 結(jié)果保存到ototal中
SELECT total INTO ototal;
END //
DELIMITER ;
DECLARE語(yǔ)句定義了兩個(gè)局部變量,DECLARE要求指定變量名和數(shù)據(jù)類型,它支持可選的默認(rèn)值。IF語(yǔ)句檢查taxable是否為真,如果為真,則用另一個(gè)SELECT語(yǔ)句增加營(yíng)業(yè)稅到局部變量total。最后,將局部變量total保存到ototal。
調(diào)用該存儲(chǔ)過程:
CALL ordertotal(20005, 0, @total);
SELECT @total;
CALL ordertotal(20005, 1, @total);
SELECT @total;
2.3.6 檢查存儲(chǔ)過程
為顯示用來創(chuàng)建一個(gè)存儲(chǔ)過程的CREATE語(yǔ)句,使用SHOW CREATE PROCEDURE語(yǔ)句:
SHOW CREATE PROCEDURE ordertotal;
為了獲得包括何時(shí)、由誰(shuí)創(chuàng)建等詳細(xì)信息的存儲(chǔ)過程列表,使用SHOW PROCEDURE STATUS。
SHOW PROCEDURE STATUS;
可看出,SHOW PROCEDURE STATUS;顯示了太多無(wú)關(guān)緊要的信息,為限制其輸出,可以使用LIKE指定一個(gè)過濾模式。例如:
SHOW PROCEDURE STATUS LIKE 'ordertotal';
如果您發(fā)現(xiàn)文中有不清楚或者有問題的地方,請(qǐng)?jiān)谙路皆u(píng)論區(qū)留言,我會(huì)根據(jù)您的評(píng)論,更新文中相關(guān)內(nèi)容,謝謝!