3.9 MySQL 不支持的功能
本節(jié)介紹其他數(shù)據(jù)庫中有而MySQL 中無的功能。它介紹省略了什么功能,以及在需要這些功能時怎么辦。一般情況下, MySQL 之所以忽略某些功能是因?yàn)樗鼈冇胸?fù)面性能影響。有的功能正在開發(fā)者的計(jì)劃清單上,一旦找到一種方法可以實(shí)現(xiàn)相應(yīng)的功能而又不致于影響
良好性能的目標(biāo),就會對它們進(jìn)行實(shí)現(xiàn)。
■ 子選擇。子選擇是嵌套在另一個SELECT 語句內(nèi)的SELECT 語句,如下面的查詢所示:
SELECT * FROM score
WHERE event_id IN (SELECT event_id FROM event WHERE type = "T")
子選擇打算在MySQL 3.24 中給出,到那時它們就不會忽略了。但到那時,許多用子選擇撰寫的查詢也可以用連接來編寫。請參閱3 . 8 . 1節(jié)“將子選擇編寫為連接”。
■ 事務(wù)處理和提交/回退。事務(wù)處理是由其他客戶機(jī)作為一個整體不中斷執(zhí)行的一組S Q L語句。提交/回退功能允許規(guī)定數(shù)條語句作為一個整體執(zhí)行或不執(zhí)行。即,如果事務(wù)處理中的任何一條語句失敗,那么直到該語句前執(zhí)行的所有語句的作用都被撤消。
ySQL 自動進(jìn)行單一SQL 語句的同步以免客戶機(jī)互相干擾。(例如,兩個客戶機(jī)不能對相同的表進(jìn)行同時寫入。)此外,可利用LOCK TABLES 和UNLOCK TA B L ES將數(shù)條語句組成一個整體,這使您能夠完成單條語句的并發(fā)控制所不能滿足的操作。MySQL 與事務(wù)處理有關(guān)的問題是,它不能自動對數(shù)條語句進(jìn)行組織,而且如果這些語句中有某一條失敗后也不能對它們進(jìn)行回退。
為了弄清事務(wù)處理為什么有用,可舉例說明。假如您在服裝銷售業(yè)工作,無論何時,只要您的銷售人員進(jìn)行了一次銷售,都要更新庫存數(shù)目。下面的例子說明了在多個銷售人員同時更新數(shù)據(jù)庫時可能出現(xiàn)的問題(假如初始的襯衫庫存數(shù)目為4 7):
t1 銷售人員1賣出3件襯衫
t2 銷售人員檢索當(dāng)前襯衫計(jì)數(shù)( 4 7):
SELECT quantity FROM inventory WHERE item = "shirt"
t3 銷售人員2賣出2件襯衫
t4 銷售人員2檢索當(dāng)前襯衫計(jì)數(shù)( 4 7)
SELECT quantity FROM inventory WHERE item = "shirt"
t5 銷售人員1計(jì)算庫存的新數(shù)目為47 - 3 = 44 并設(shè)置襯衫計(jì)數(shù)為44:
UPDATE inventory SET quantity = 44 WHERE item = "shirt"
t6 銷售人員2計(jì)算庫存的新數(shù)目為47 - 2 = 45 并設(shè)置襯衫計(jì)數(shù)為45:
UPDATE inventory SET quantity = 45 WHERE item = "shirt"
在這個事件序列結(jié)束時,您已經(jīng)賣掉了5 件襯衫,但庫存數(shù)目卻是45 而不是4 2。問題是如果在一條語句中查看庫存而在另一條語句中更新其值,這是一個多語句的事務(wù)處理。第二條語句中所進(jìn)行的活動取決于第一條語句中檢索出的值。但是如果在重疊的時間范圍內(nèi)出現(xiàn)獨(dú)立的事務(wù)處理,則每個事務(wù)處理的語句會糾纏在一起,并且互相干擾。在事務(wù)處理型的數(shù)據(jù)庫中,每個銷售人員的語句可作為一個事務(wù)處理執(zhí)行,這樣,銷售人員2 的語句在銷售人員1 的語句完成之前不會被執(zhí)行。在MySQL 中,可用兩種方法達(dá)到這個目的:
■ 方法1:作為一個整體執(zhí)行一組語句。可利用LOCK TABLES 和UNLOCK TABLES將語句組織在一起,并將它們作為一個原子單元執(zhí)行:鎖住所需使用的表,發(fā)布查詢,然后釋放這些鎖。這樣阻止了其他人在您鎖住這些表時使用它們。利用表同步,庫存情況如下所示:
t1 銷售人員1賣出3件襯衫
t2 銷售人員1請求一個鎖并檢索當(dāng)前襯衫計(jì)數(shù)(47)
LOCK TABLES inventory WRITE
SELECT quantity FROM inventory WHERE item = "shirt"
t3 銷售人員2賣出2件襯衫
t4 銷售人員2試圖取得一個鎖:這被阻塞,因?yàn)殇N售人員1 已經(jīng)占住了鎖:
LOCK TABLES inventory WRITE
t5 銷售人員1計(jì)算庫存的新數(shù)目為47 - 3 = 44 并設(shè)置襯衫計(jì)數(shù)為44,然后釋放鎖:
UPDATE inventory SET quantity = 44 WHERE item = "shirt"
UNLOCK TABLES
t6 現(xiàn)在銷售人員2的鎖請求成功。銷售人員2檢索當(dāng)前襯衫計(jì)數(shù)( 44)
SELECT quantity FROM inventory WHERE item = "shirt"
t7 銷售人員2計(jì)算庫存的新數(shù)目為44 - 2 = 42,設(shè)置襯衫計(jì)數(shù)為4 2,然后釋放鎖:
UPDATE inventory SET quantity = 42 WHERE item = "shirt"
UNLOCK TABLES
現(xiàn)在來自兩個事務(wù)處理的語句不混淆了,并且?guī)齑嬉r衫數(shù)也正確進(jìn)行了設(shè)置。我們在這里使用了一個WRITE 鎖,因?yàn)槲覀冃枰薷膇nventory 表。如果只是讀取表,可使用READ 鎖。當(dāng)您正在使用表時,這個鎖允許其他客戶機(jī)讀取表。在剛才舉的例子中,銷售人員2大概不會注意到執(zhí)行速度上的差異,因?yàn)槠渲械氖聞?wù)處理都很短,執(zhí)行速度很快。但是,作為一個具有普遍意義的規(guī)則,那就是應(yīng)該盡量避免長時間地鎖住表。
如果您正在使用多個表,那么在您執(zhí)行成組查詢之前,必須鎖住他們。如果只是從某個特定的表中讀取數(shù)據(jù),那么只需給該表加一個讀出鎖而不是寫入鎖。假如有一組查詢,其中想對inventory 表作某些更改,而同時需要從customer 表中讀取某些數(shù)據(jù)。在此情形下,inventory 表上需要一個寫入鎖,而customer 表上需要一個讀出鎖:
LOCK TABLES inventory WRITE,customer READ
...
UNLOCK TABLES
這里要求您自己對表進(jìn)行加鎖和解鎖。支持事務(wù)處理的數(shù)據(jù)庫系統(tǒng)將會自動完成這些工作。但是,在作為一個整體執(zhí)行的分組語句方面,無論在是否支持事務(wù)處理的數(shù)據(jù)庫中都是相同的。
■ 方法2:使用相對更新而不是絕對更新。要解決來自多個事務(wù)處理的語句混淆問題,應(yīng)消除語句之間的依賴性。雖然這樣做并不都總是可能的,它只針對我們的庫存例子可行。對于方法1中所用的庫存更新方法,其中事務(wù)處理需要查看當(dāng)前庫存數(shù)目,并依據(jù)銷售襯衫的數(shù)目計(jì)算新值,然后更新襯衫的數(shù)目。有可能通過相對于當(dāng)前襯衫數(shù)目進(jìn)行計(jì)數(shù)更新,在一個步驟中完成工作。如下所示:
t1 銷售人員1賣出3件襯衫
t2 銷售人員1將襯衫計(jì)數(shù)減3:
UPDATE inventory SET quantity = quantity - 3 WHERE item = "shirt"
t3 銷售人員2賣出2件襯衫
t4 銷售人員2將襯衫計(jì)數(shù)減2:
UPDATE inventory SET quantity = quantity - 2 WHERE item = "shirt"
因此,這里根本不需要多條語句的事務(wù)處理,從而也不需要鎖住表以模擬事務(wù)處理功能。如果所使用的事務(wù)處理類型與這里類似,那么就可以不用事務(wù)處理也能完成工作。上面的例子說明了在特殊情形下怎樣避免對事務(wù)處理功能的需求。但這并不是說不存在那種確實(shí)需要事務(wù)處理功能的場合。典型的例子是財(cái)務(wù)轉(zhuǎn)賬,其中錢從一個賬戶轉(zhuǎn)到另一個賬戶。假如Bill 給Bob 開了一張$100 的支票,Bob 兌現(xiàn)了這張支票。Bill 的戶頭上應(yīng)該減掉$100 而Bob 的戶頭上應(yīng)該增加相同數(shù)量的錢:
UPDATE account SET balance = balance -100 WHERE name = "Bill"
UPDATE account SET balance = balance +100 WHERE name = "Bob"
如果在這兩條語句執(zhí)行中,系統(tǒng)發(fā)生了崩潰,此事務(wù)處理就不完整了。具有真正事務(wù)處理和提交/回退功能的數(shù)據(jù)庫系統(tǒng)能夠處理這種情況(至少從理論上能夠處理。您可能仍然必須判斷遇到了哪些事務(wù)處理并重新發(fā)布它們,但至少不會擔(dān)心事務(wù)只處理了一半)。在MySQL 中,系統(tǒng)崩潰時可通過檢查更新日志來判斷事務(wù)處理的狀態(tài),雖然這可能需要對日志進(jìn)行某種手工檢查。
■ 外部鍵和引用完整性。外部鍵允許定義一個表中的鍵與另一個表中的鍵相關(guān),而引用完整性允許放置對包含外部鍵的表可以做什么的約束。例如, samp_db 樣例數(shù)據(jù)庫的score 表中包含一個student_id 列,我們用它來將學(xué)分記錄關(guān)聯(lián)到student 表中的學(xué)生。score.student_id 將定義為支持此概念的數(shù)據(jù)庫中的一個外部鍵,我們將在其上加上一條約束,使不能為student 表中不存在的學(xué)生輸入學(xué)分記錄。此外,應(yīng)該允許級聯(lián)刪除,以便如果某個學(xué)生從student 表中被刪除后,該學(xué)生的任何學(xué)分記錄將會自動地從
score 表中刪除。
外部鍵有助于保持?jǐn)?shù)據(jù)的一致性,而且還提供了某種方便的手段。MySQL 不支持外部鍵的原因主要是由于它對數(shù)據(jù)庫的實(shí)現(xiàn)與維護(hù)有負(fù)作用。(MySQL 參考指南詳細(xì)列出了這些原因。)注意,對于外部鍵的這種看法與其他數(shù)據(jù)庫文獻(xiàn)中的看法有些不同,有的數(shù)據(jù)庫文獻(xiàn)通常將它們描述成“基本的”。MySQL 的開發(fā)者并不贊同這個觀點(diǎn)。如果您贊成,那么最好是考慮采用其他提供外部鍵支持的數(shù)據(jù)庫。如果數(shù)據(jù)具有特別復(fù)雜的關(guān)系,您可能不希望擔(dān)負(fù)在應(yīng)用程序中實(shí)現(xiàn)這些相關(guān)性的工作。(即使這樣做的工作量要比增加幾個額外的DELETE 語句的工作量要稍少一些。)除了在一定程度上能夠在CREATE TABLE 語句中分析FOREIGN KEY 子句外,MySQL 不支持外部鍵。(這有助于使從其他數(shù)據(jù)庫移植代碼到MySQL 更為容易。)MySQL 不強(qiáng)制讓外部鍵作為一種約束,也不提供級聯(lián)刪除功能。
外部鍵強(qiáng)制實(shí)施的約束一般不難用程序邏輯來實(shí)現(xiàn)。有時,它只是一個怎樣進(jìn)行數(shù)據(jù)錄入處理的問題。例如,為了將一個新記錄插入score 表,不太可能插入不存在的學(xué)生的學(xué)分。顯然,輸入一組學(xué)分的方法應(yīng)該是根據(jù)從student 表得出的學(xué)生名單,對每個學(xué)生,取其學(xué)分并利用該學(xué)生的ID 號產(chǎn)生一個score 表的記錄。對于這個過程,不存在錄入一個不存在的學(xué)生的記錄的可能。您不會憑空造出一個學(xué)分記錄來插入score 表。要實(shí)現(xiàn)DELETE 的級聯(lián)效果,必須用自己的應(yīng)用程序邏輯來完成。假如想要刪除13 號學(xué)生。這也隱含表示需要刪除該學(xué)生的學(xué)分記錄。在支持級聯(lián)刪除的數(shù)據(jù)庫中,只需要用如下的語句就可以刪除student 表的記錄和相應(yīng)的score 表的記錄:
DELETE FROM student WHERE student_id = 13
而在MySQL 中,必須明確地用DELETE 語句自己進(jìn)行第二個刪除語句:
DELETE FROM student WHERE student_id = 13
DELETE FROM score WHERE student_id = 13
■ 存儲過程和觸發(fā)器。存儲過程是編譯和存放在服務(wù)器中的SQL 代碼。它可在以后調(diào)用而無需從客戶機(jī)發(fā)送并分析??梢詫σ粋€過程進(jìn)行更改以影響使用它的任何客戶機(jī)應(yīng)用程序。觸發(fā)器功能使一個過程在某個事件發(fā)生時被激活,如從表中刪除某個記錄時,激活相應(yīng)的過程。例如,某個作為累計(jì)成分的記錄被刪除時,應(yīng)該重新進(jìn)行累計(jì),使累計(jì)數(shù)反映最新情況。存儲過程語言已列入了MySQL 準(zhǔn)備實(shí)現(xiàn)的計(jì)劃。
■ 視圖。視圖是一個邏輯概念,其功能像表但本身不是表。它提供了一種查看不同表中的列的途徑,在查看時好像這些列屬于同一個表一樣。視圖有時也稱為虛表。M y S Q L也準(zhǔn)備實(shí)現(xiàn)視圖功能。
■ 記錄級權(quán)限和鎖定。MySQL 支持各種權(quán)限,從全局權(quán)限到數(shù)據(jù)庫、表、列的權(quán)限。但它不支持記錄級的權(quán)限。不過,可在應(yīng)用程序中利用GET_LOCK( ) 和R E L E A S E _LOCK( ) 函數(shù)來實(shí)現(xiàn)協(xié)同記錄鎖。這個過程在附錄C“運(yùn)行算符和函數(shù)參考”中相應(yīng)的項(xiàng)目下介紹。
■ “- -”作為注釋的開始。MySQL 不支持這種注釋風(fēng)格,因?yàn)樗且粋€有歧義的結(jié)構(gòu),雖然自MySQL 3.23 以來,注釋可用兩個短劃線加一個空格開始。更詳細(xì)的信息,請參閱3 . 7節(jié)“加注釋”。