### 附錄I:特性限制
** 目錄**
[ I.1. 對存儲子程序和觸發程序的限制](#)[ I.2. 對服務器端光標的限制](#)[ I.3. 對子查詢的限制](#)[ I.4. 對視圖的限制](#)[ I.5. 對XA事務的限制](#)
在本附錄中,介紹了使用諸如子查詢或視圖等MySQL特性時存在的限制。
### I.1.?對存儲子程序和觸發程序的限制
這里介紹的某些限制適用于所有的存儲子程序,即存儲程序和存儲函數。某些限制僅適用于存儲函數而不是存儲程序。
對存儲函數的的所有限制也適用于觸發程序。
**注釋:**如果SQL語句,如SELECT ... INTO語句包含具有相同名稱的對列的引用以及聲明的局部變量,MySQL會將引用解釋為變量的名稱。這是一種非標準的行為,優先順序通常是列名、然后是SQL變量和參數。請參見[20.2.9.3節,“SELECT ... INTO語句”](# "20.2.9.3.?SELECT ... INTO Statement")。
存儲子程序不能包含任意SQL語句。在存儲子程序中,禁止使用下述語句:
·???????? CHECK TABLES
·???????? LOCK TABLES, UNLOCK TABLES
·???????? LOAD DATA, LOAD TABLE
·???????? SQL預處理語句(PREPARE、EXECUTE、DEALLOCATE PREPARE)。隱含意義:不能在存儲子程序中使用動態SQL語句(其中,能夠以字符串形式構造動態語句,然后執行它們)。從MySQL 5.0.13開始,對于存儲程序放寬了該限制,但該限制仍適用于存儲函數和觸發程序。
·???????? OPTIMIZE TABLE
對于存儲函數(而不是存儲程序),禁止下述額外語句:
·???????? 執行顯式或隱式提交或回滾操作的語句。
·???????? 返回結果集的語句。包括沒有INFO子句的SELECT語句,以及SHOW語句。能夠用SELECT … INTO,或使用光標和FETCH語句處理結果集的函數。
·???????? FLUSH語句。注意,盡管能夠在存儲程序中使用FLUSH,但不能從存儲函數或觸發程序調用這類存儲程序。
注意,盡管某些限制在正常情況下適用于存儲函數和觸發程序,不適用于存儲程序,如果它們是從存儲函數或觸發程序中調用的,這些限制也適用于存儲程序。
使用存儲子程序會導致復制問題。關于這方面的進一步討論,請參見[20](#)[.4節,“存儲子程序和觸發程序的二進制日志功能”](# "20.4.?Binary Logging of Stored Routines and Triggers")。
INFORMATION_SCHEMA尚不包含PARAMETERS表,因此,對于需要在運行時獲取子程序參數信息的應用程序來說,必須采用相應的規避錯誤,如解析SHOW CREATE語句的輸出。
沒有存儲子程序調試工具。
存儲子程序使用了具體化的光標,而不是固有光標(在服務器端生成結果集并對結果集進行高速緩沖處理,然后在客戶端獲取結果集時按行返回)。
不能提前處理CALL語句。無論是對服務器端預處理語句還是SQL預處理語句,均成立。
為了防止服務器線程間的交互問題,當客戶端發出語句時,服務器將使用可用的、用于語句執行的子程序和觸發程序快照。也就是說,服務器將計算出可在語句執行期間使用的存儲程序、函數和觸發程序的列表,加載它們,然后進入語句執行。這意味著,在語句執行的同時,它不會看到其他線程對子程序所作的變更。
### I.2.?對服務器端光標的限制
從MySQL 5.0.2開始,通過mysql_stmt_attr_set() C API函數實現了服務器端光標。服務器端光標允許在服務器端生成結果集,但不會將其傳輸到客戶端,除非客戶端請求這些行。例如,如果客戶端執行了查詢,但僅對第1行感興趣,那么不會傳輸剩余的行。
光標是只讀的,不能使用光標來更新行。
未實施UPDATE WHERE CURRENT OF和DELETE WHERE CURRENT OF,這是因為不支持可更新的光標。
光標是不可保持的(提交后不再保持打開)。
光標是不敏感的。
光標是不可滾動的。
光標是未命名的。語句處理程序起著光標ID的作用。
對于每條預處理語句,僅能打開1個光標。如果需要多個光標,必須處理多條語句。
如果在預處理模式下不支持語句,不能在生成結果集的語句上使用光標。包括CHECK TABLES、HANDLER READ和SHOW BINLOG EVENTS語句。
### I.3.?對子查詢的限制
隨后將更正的一致缺陷:如果將NULL值與使用ALL、ANY或SOME的子查詢進行比較,而且子查詢返回空結果,比較操作將對NULL的非標準結果進行評估,而不是TRUE或FALSE。
子查詢的外部語句可以是SELECT、INSERT、UPDATE、DELETE、SET或DO中的任何一個。
僅部分支持行比較操作:
·???????? 對于*expr* IN (*subquery*),*expr*可以是*n*-tuple(通過行構造程序語法指定),而且子查詢能返回*n*-tuples個行。
·???????? 對于*expr**op* {ALL|ANY|SOME} (*subquery*),*expr*必須是標度值,子查詢必須是列子查詢,不能返回多列行。
換句話講,對于返回*n*-tuples行的子查詢,支持:
~~~
(val_1, ..., val_n) IN (subquery)
~~~
但不支持:
~~~
(val_1, ..., val_n) op {ALL|ANY|SOME} (subquery)
~~~
支持針對IN的行比較,但不支持針對其他的行比較,原因在于,IN實施是通過將其重新編寫為“=”比較和AND操作的序列完成的。該方法不能用于ALL、ANY或SOME。
未良好優化行構造程序。下面的兩個表達式是等效的,但只有第2個表達式能被優化:
~~~
(col1, col2, ...) = (val1, val2, ...)
~~~
~~~
col1 = val1 AND col2 = val2 AND ...
~~~
對于IN的子查詢優化不如對“=”的優化那樣有效。
對于不良IN性能的一種典型情況是,當子查詢返回少量行,但外部查詢返回將與子查詢結果相比較的大量行。
FROM子句中的子查詢不能與子查詢有關系。在評估外部查詢之前,將對它們進行具體化處理(執行以生成結果集),因此,不能按照外部查詢的行對它們進行評估。
一般而言,不能更改表,并從子查詢內的相同表進行選擇。例如,該限制適用于具有下述形式的語句:
~~~
DELETE FROM t WHERE ... (SELECT ... FROM t ...);
~~~
~~~
UPDATE t ... WHERE col = (SELECT ... FROM t ...);
~~~
~~~
{INSERT|REPLACE} INTO t (SELECT ... FROM t ...);
~~~
例外:如果為FROM子句中更改的表使用子查詢,前述禁令將不再適用。例如:
~~~
UPDATE t ... WHERE col = (SELECT (SELECT ... FROM t...) AS _t ...);
~~~
禁令在此不適用,這是因為FROM中的子查詢已被具體化為臨時表,因此“t”中的相關行已在滿足“t”條件的情況下、在更新時被選中。
與子查詢相比,針對聯合的優化程序更成熟,因此,在很多情況下,如果將其改寫為join(聯合),使用子查詢的語句能夠更有效地執行。
但下述情形例外:IN子查詢可被改寫為SELECT DISTINCT聯合。例如:
~~~
SELECT col FROM t1 WHERE id_col IN (SELECT id_col2 FROM t2 WHERE condition);
~~~
可將該語句改寫為:
~~~
SELECT DISTINCT col FROM t1, t2 WHERE t1.id_col = t2.id_col AND condition;
~~~
但在該情況下,聯合需要額外的DISTINCT操作,而且與子查詢相比,效率并不高。
可能的未來優化:MySQL不改寫針對子查詢評估的聯合順序。在某些情況下,如果MySQL將其改寫為聯合,能夠更有效地執行子查詢。這樣,優化程序就能在更多的執行方案間進行選擇。例如,它能決定是否首先讀取某一表或其他。
例如:
~~~
SELECT a FROM outer_table AS ot
~~~
~~~
WHERE a IN (SELECT a FROM inner_table AS it WHERE ot.b = it.b);
~~~
對于該查詢,MySQL總會首先掃描outer_table,如然后針對每一行在inner_table上執行子查詢。如果outer_table有很多行而inner_table只有少量行,查詢的執行速度或許要慢于本應有的速度。
前述查詢可改寫為:
~~~
SELECT a FROM outer_table AS ot, inner_table AS it
~~~
~~~
WHERE ot.a = it.a AND ot.b = it.b;
~~~
在該情況下,我們能掃描小的表(inner_table)并查詢outer_table中的行,如果在“ot.a,ot.b”上有索引,速度會更快。
可能的未來優化:對外部查詢的每一行評估關聯的子查詢。更好的方法是,如果外部行的值與之前的行相比沒有變化,不對子查詢進行再次評估,而是使用以前的結果。
可能的未來優化:通過將結果具體化到臨時表,而且該表不使用索引,對FROM子句中的子查詢進行評估。在查詢中與其他表進行比較時,盡管可能是有用的,但不允許使用索引。
可能的未來優化:如果FROM子句中的子查詢類似于可施加MERGE算法的視圖,改寫查詢并采用MERGE算法,以便能夠使用索引。下述語句包含這類子查詢:
~~~
SELECT * FROM (SELECT * FROM t1 WHERE t1.t1_col) AS _t1, t2 WHERE t2.t2_col;
~~~
該語句可被改寫為聯合,如下所示:
~~~
SELECT * FROM t1, t2 WHERE t1.t1_col AND t2.t2_col;
~~~
這類改寫具有兩個優點:
1.??? 避免使用那些不能使用索引的臨時表。在改寫的查詢中,優化程序可在t1上使用索引。
2.??? 優化程序在選擇不同的執行計劃方面具有更大的自由。例如,將查詢改寫為聯合,那么就允許優化程序首先使用t1或t2。
可能的未來優化:對于沒有關聯子查詢的IN、= ANY、<> ANY、= ALL、以及<> ALL,為結果使用“內存中”哈希處理,或對較大的結果使用具有索引的臨時表。例如:
~~~
SELECT a FROM big_table AS bt
~~~
~~~
WHERE non_key_field IN (SELECT non_key_field FROM table WHERE condition)
~~~
在該情況下,可創建臨時表:
~~~
CREATE TABLE t (key (non_key_field))
~~~
~~~
(SELECT non_key_field FROM table WHERE condition)
~~~
然后,對big_table中的每一行,根據bt.non_key_field,在“t”中進行鍵查找。
### I.4.?對視圖的限制
視圖處理功能概念未優化:
·???????? 不能在視圖上創建索引。
·???????? 對于使用MERGE算法處理的視圖,可以使用索引。但是,對于使用臨時表算法處理的視圖,不能在其基表上利用索引提供的優點(盡管能夠在臨時表的生成過程中使用索引)。
在視圖的FROM子句中不能使用子查詢。未來該限制將被放寬。
存在一個一般原則,不能更改某一表并在子查詢的相同表內進行選擇。請參見[I.3節,“對子查詢的限制”](# "I.3.?Restrictions on Subqueries")。
如果從表選擇了視圖并接著從視圖進行了選擇,同樣的原理也適用,如果在子查詢中從表選擇了視圖并使用MERGE算法評估了視圖,也同樣。例如:
~~~
CREATE VIEW v1 AS
~~~
~~~
SELECT * FROM t2 WHERE EXISTS (SELECT 1 FROM t1 WHERE t1.a = t2.a);
~~~
~~~
?
~~~
~~~
UPDATE t1, v2 SET t1.a = 1 WHERE t1.b = v2.b;
~~~
如果視圖是使用臨時表評估的,可從視圖子查詢中的表進行選擇,并仍能更改外部查詢中的表。在該情況下,視圖將被具體化,因此,你實際上不能從子查詢的表中進行選擇并“同時”更改它(這是你打算強制MySQL使用臨時表算法的另一原因,其方法是在視圖定義中指定ALGORITHM = TEMPTABLE關鍵字)。
可以使用DROP TABLE或ALTER TABLE來舍棄或更改視圖定義中使用的表(它會是視圖失效),而且舍棄或更改操作不會導致告警。但在以后使用視圖時會出錯。
視圖定義是通過特定語句“凍結”的:
·???????? 如果PREPARE預處理的語句引用了視圖,以后每次執行語句時看到的視圖內容與預處理視圖時的內容相同。即使在語句預處理完成之后、在執行語句之前更改了視圖定義,情況也同樣。例如:
~~~
·??????????????? CREATE VIEW v AS SELECT 1;
~~~
~~~
·??????????????? PREPARE s FROM 'SELECT * FROM v';
~~~
~~~
·??????????????? ALTER VIEW v AS SELECT 2;
~~~
~~~
·??????????????? EXECUTE s;
~~~
EXECUTE語句返回的結果是1,而不是2。
·???????? 如果存儲子程序中的語句引用了視圖,語句所見到的視圖內容與首次執行語句時的相同。這意味著,如果語句是以循環方式執行的,進一步的語句迭代見到的視圖內容是相同的,即使在循環過程中更改了視圖定義也同樣。例如:
~~~
·??????????????? CREATE VIEW v AS SELECT 1;
~~~
~~~
·??????????????? delimiter //
~~~
~~~
·??????????????? CREATE PROCEDURE p ()
~~~
~~~
·??????????????? BEGIN
~~~
~~~
·??????????????? ??DECLARE i INT DEFAULT 0;
~~~
~~~
·??????????????? ??WHILE i < 5 DO
~~~
~~~
·??????????????? ????SELECT * FROM v;
~~~
~~~
·??????????????? ????SET i = i + 1;
~~~
~~~
·??????????????? ????ALTER VIEW v AS SELECT 2;
~~~
~~~
·??????????????? ??END WHILE;
~~~
~~~
·??????????????? END;
~~~
~~~
·??????????????? //
~~~
~~~
·??????????????? delimiter ;
~~~
~~~
·??????????????? CALL p();
~~~
如果調用了程序p(),每次通過循環時SELECT返回1,即使在循環內更改了視圖定義也同樣。
關于視圖的可更新性,對于視圖,其總體目標是,如果任何視圖從理論上講是可更新的,在實際上也應是可更新的。這包括在其定義中有UNION的視圖。目前,并非所有理論上可更新的視圖均能被更新。最初的視圖實施有意采用該方式,為的是盡快地在MySQL中獲得有用的可更新視圖。很多理論上可更新的視圖現已能更新,但限制依然存在:
·???????? 其子查詢位于WHERE子句之外任何位置的可更新視圖。對于某些其子查詢位于SELECT列表中的視圖,也是可更新的。
·???????? 不能使用UPDATE來更新定義為Join的視圖的1個以上的基表。
·???????? 不能使用DELETE來更新定義為Join的視圖。
### I.5.?對XA事務的限制
XA事務支持限于InnoDB存儲引擎。
MySQL XA實施是針對外部XA的,其中,MySQL服務器作為資源管理器,而客戶端程序作為事務管理器。未實施“內部XA”。這樣,就允許MySQL服務器內的單獨存儲引擎作為RM(資源管理器),而服務器本身作為TM(事務管理器)。處理包含1個以上存儲引擎的XA事務時,需要內部XA。內部XA的實施是不完整的,這是因為,它要求存儲引擎在表處理程序層面上支持兩階段提交,目前僅對InnoDB實現了該特性。
對于XA START,不支持JOIN和RESUME子句。
對于XA END,不支持SUSPEND [FOR MIGRATE]子句。
在全局事務內,對于每個XA事務,xid值的bqual部分應是不同的,該要求是對當前MySQL XA實施的限制。它不是XA規范的組成部分。
如果XA事務達到PREPARED狀態而且MySQL服務器宕機,當服務器重啟后,能夠繼續處理事務。就像原本應當的那樣。但是,如果客戶端連接中止而服務器繼續運行,服務器將回滾任何未完成的XA事務,即使該事務已達到PREPARED狀態也同樣。它應能提交或回滾PREPARED XA事務,但在不更改二進制日志機制的情況下不能這樣。
這是MySQL參考手冊的翻譯版本,關于MySQL參考手冊,請訪問[dev.mysql.com](http://dev.mysql.com/doc/mysql/en)。原始參考手冊為英文版,與英文版參考手冊相比,本翻譯版可能不是最新的。
- 目錄
- 前言
- 1. 一般信息
- 2. 安裝MySQL
- 3. 教程
- NoName
- 4. MySQL程序概述
- 5. 數據庫管理
- 6. MySQL中的復制
- 7. 優化
- 8. 客戶端和實用工具程序
- 9. 語言結構
- 10. 字符集支持
- 11. 列類型
- 12. 函數和操作符
- 13. SQL語句語法
- 14. 插件式存儲引擎體系結構
- 15. 存儲引擎和表類型
- 16. 編寫自定義存儲引擎
- 17. MySQL簇
- 18. 分區
- 19. MySQL中的空間擴展
- 20. 存儲程序和函數
- 21. 觸發程序
- 22. 視圖
- 23. INFORMATION_SCHEMA信息數據庫
- 24. 精度數學
- 25. API和庫
- 26. 連接器
- 27. 擴展MySQL
- A. 問題和常見錯誤
- B. 錯誤代碼和消息
- C. 感謝
- D. MySQL變更史
- E. 移植到其他系統
- F. 環境變量
- G. MySQL正則表達式
- H. MySQL中的限制
- I. 特性限制
- J. GNU通用公共許可
- K. MySQL FLOSS許可例外
- 索引