?
This document uses PHP Chinese website manual Release
目錄
??? 本章包含許多在你處理MySQL代碼時需要了解的你事情。如果你想投入到MySQL的開發(fā)中,或想要接觸到最新的中間版本的代碼,或者就是想了解開發(fā)的進(jìn)度,請參閱2.8.3節(jié),“從開發(fā)源代碼樹安裝”的說明。如果你對MySQL的內(nèi)部插件感興趣,你也可以訂閱我們的內(nèi)部插件郵件列表。這個列表的流量相對低一些。欲知如何訂閱的詳情,請參閱1.7.1.1節(jié),“MySQL郵件列表”。在MySQL AB 的所有開發(fā)人員都在內(nèi)部插件列表里, 此外,我們幫助那些正在處理MySQL代碼的人。請隨意使用這個郵件列表來問代碼有關(guān)的問題,也可用它來發(fā)送你想奉獻(xiàn)給MySQL項(xiàng)目的 補(bǔ)?。?
??? MySQL服務(wù)器創(chuàng)建如下線程:
TCP/IP 連接線程處理所有連接請求,并為每一個連接創(chuàng)建一個新的專用線程來處理認(rèn)證和SQL查詢處理。
Windows NT 平臺上有一個名為管道處理程序(pipe handler)的線程,它和名為管道連接請求(pipe connect requests)的TCP/IP連接線程做同樣的工作。
信號線程處理所有的信號,這個線程通常也處理報警和調(diào)用process_alarm() 函數(shù)來強(qiáng)制使得空閑時間太長的連接超時。
若mysqld是與DUSE_ALARM_THREAD線程一起編譯的,這個專用線程是處理 創(chuàng)建的警報的。這個線程用在一些sigwait()函數(shù)有問題的系統(tǒng)上,或者用在你想在應(yīng)用程序中使用thr_alarm()代碼而不帶專用信號處理線程之時。
若想使用flush_time=val選項(xiàng),會創(chuàng)建一個專用線程以給定的時間間隔刷新所有表格。
每個連接都有它自己的線程。
每個被使用INSERT DELAYED 的不同表格都會有自己的線程。
若使用了master-host, 則會創(chuàng)建一個從屬的復(fù)制線程從主線程讀取并實(shí)施更新。
mysqladmin processlist 僅顯示連接,INSERT DELAYED, 及復(fù)制線程
???? 包含在Unix源碼和二進(jìn)制分發(fā)版中的測試系統(tǒng)可以讓用戶和開發(fā)人員對MySQL代碼施行回歸測試。這些測試可以在Unix上進(jìn)行,目前它們還不能在原生的Windows環(huán)境下進(jìn)行。
????? 當(dāng)前的測試案例套件不能在MySQL中測試所有東西,但是它能發(fā)現(xiàn)SQL處理代碼,OS/library文件中大多數(shù)明顯的缺陷,并且在測試復(fù)件方面也是非常徹底的。我們的終極目標(biāo)是對100%的代碼進(jìn)行測試。我們歡迎大家給我們的測試套件添加內(nèi)容。你可能會特別想貢獻(xiàn)出那些檢查你系統(tǒng)里功能性危機(jī)的測試,因?yàn)檫@將確保未來所有發(fā)行版的MySQL會與你的應(yīng)用程序一起更好地運(yùn)行。
??? 測試系統(tǒng)包括一個測試語言解釋器(mysqltest),一個運(yùn)行所有測試的外殼腳本(mysql-test-run),用專用語言編寫的測試案例,以及它們的預(yù)期結(jié)果。在系統(tǒng)上編譯好之后,在源代碼的root下鍵入make test 或mysql-test/mysql-test-run。如果安裝了一個二進(jìn)制分發(fā)版, cd 到安裝root (如 /usr/local/mysql), 然后鍵入 scripts/mysql-test-run。所有測試應(yīng)該都通過,假使有沒通過的,若是一個MySQL里的缺陷,你可以試著找找是因?yàn)槭裁矗⑶覉蟾孢@個問題。請參閱27.1.2.3節(jié),“在MySQL測試套件里報告缺陷”。
如果你想要運(yùn)行測試套件的機(jī)器上已經(jīng)運(yùn)行了一個 mysqld ,只要它不占用9306 和 9307端口,就不用停掉它。如果占用了其中的一個,以可以編輯mysql-test-run把主端口和(或)從端口號改為其它可用的。.
可使用下面指令運(yùn)行單個測試案例 mysql-test/mysql-test-run test_name.
若一個測試未通過,你可以用--force選項(xiàng)來檢查運(yùn)行著的mysql-test-run看是否是別的測試未通過。
你可以用mysqltest 語言編寫你自己的測試案例。不幸地是,我們還沒有寫完相關(guān)方面完整地文檔。但是,你可以查看我們現(xiàn)有的測試案例,并將它們作為范例。下面幾點(diǎn)將有助于你入手:
測試位于 mysql-test/t*.MYI,檢查所有的MyISAM表,并重啟mysqld。這樣,就能確保從干凈的狀態(tài)運(yùn)行服務(wù)器。請參見第5章:數(shù)據(jù)庫管理。
使用“--log”選項(xiàng)啟動mysqld,并根據(jù)寫入日志的信息確定是否某些特殊的查詢殺死了服務(wù)器。約95%的缺陷與特定的查詢有關(guān)。正常情況下,這是服務(wù)器重啟前日志文件中最夠數(shù)個查詢中的1個。請參見5.11.2節(jié),“通用查詢?nèi)罩尽?。如果能夠用特殊查詢重?fù)殺死MySQL,即使在發(fā)出查詢前檢查了所有表的情況下也同樣,那么你就應(yīng)能確定缺陷,并應(yīng)提交關(guān)于該缺陷的缺陷報告。請參見1.7.1.3節(jié),“如何通報缺陷和問題”。
嘗試提供一個測試范例,我們應(yīng)能利用該范例重復(fù)問題。請參見E.1.6節(jié),“如果出現(xiàn)表崩潰,請生成測試案例”。
請在mysql-test目錄下并根據(jù)MySQL基準(zhǔn)進(jìn)行測試。請參見27.1.2節(jié),“MySQL測試套件”。它們能相當(dāng)良好地測試MySQL。你也可以為基準(zhǔn)測試增加代碼,以模擬你的應(yīng)用程序?;鶞?zhǔn)測試可在源碼分發(fā)版的sql-bench目錄下找到,對于二進(jìn)制分發(fā)版,可在MySQL安裝目錄下的sql-bench目錄下找到。
嘗試使用fork_big.pl腳本(它位于源碼分發(fā)版的測試目錄下)。
如果你將MySQL配置為調(diào)試模式,如果某事出錯,可更為容易地搜集關(guān)于可能錯誤的信息。如果將MySQL配置為調(diào)試模式,可生成1個安全的內(nèi)存分配程序,可使用它發(fā)現(xiàn)某些錯誤。此外,它還提供了很多輸出,這類輸出與出現(xiàn)的問題相關(guān)。在configure上使用“--with-debug”或“--with-debug=full”選項(xiàng)重新配置MySQL,然后再編譯它。請參見E.1節(jié),“調(diào)試MySQL服務(wù)器”。
確保為你的操作系統(tǒng)應(yīng)用了最新的補(bǔ)丁。
對mysqld使用“--skip-external-locking”選項(xiàng)。在某些系統(tǒng)上,lockd鎖定管理器不能正確工作,“--skip-external-locking”選項(xiàng)通知mysqld不使用外部鎖定。(這意味著,你不能在相同的數(shù)據(jù)目錄上運(yùn)行2個mysqld服務(wù)器,如果使用myisamchk,必須謹(jǐn)慎。然而,嘗試將該選項(xiàng)用作測試也是有益的)。
當(dāng)mysqld看上去正在運(yùn)行但并未響應(yīng)時,是否運(yùn)行了mysqladmin -u root processlist?某些時候,即使你認(rèn)為mysqld處于閑置狀態(tài)時,實(shí)際情況并非如此。問題可能是因?yàn)樗羞B接均已使用,或存在某些內(nèi)部鎖定問題。即使在該情況下,mysqladmin -u root processlist通常能夠進(jìn)行連接,并能提供關(guān)于當(dāng)前連接數(shù)以及其狀態(tài)的有用信息。
在運(yùn)行其他查詢的同時,在單獨(dú)的窗口中運(yùn)行命令mysqladmin -i 5 status或mysqladmin -i 5 -r status,以生成統(tǒng)計信息。
嘗試采用下述方法:
從gdb(或另一個調(diào)試器)啟動mysqld。請參見E.1.3節(jié),“在gdb環(huán)境下調(diào)試mysqld”。
運(yùn)行測試腳本。
在3個較低層面上輸出backtrace(向后跟蹤)和局部變量。在gdb中,當(dāng)mysqld在gdb內(nèi)崩潰時,可使用下述命令完成該任務(wù):
backtrace
info local
up
info local
up
info local
使用gdb,你還能檢查與info線程共存的線程,并切換至特定的線程N,其中,N是線程ID。
嘗試用Perl腳本模擬你的應(yīng)用程序,強(qiáng)制MySQL崩潰或行為異常。
發(fā)送正常的缺陷報告。請參見1.7.1.3節(jié),“如何通報缺陷和問題”。應(yīng)比通常的報告更詳細(xì)。由于MySQL是為很多人提供服務(wù)的,它可能因僅存在于你的計算機(jī)上的某事崩潰(例如,與你的特定系統(tǒng)庫有關(guān)的錯誤)。
如果你遇到與包含動態(tài)長度行的表有關(guān)的問題,而且你僅使用VARCHAR列(而不是BLOB或TEXT列),可嘗試用ALTER TABLE將所有VARCHAR列更改為CHAR列。這樣,就會強(qiáng)制MySQL使用固定大小的行。固定大小的行占用的空間略多,但對損壞的容忍度更高。
目前的動態(tài)行代碼在MySQL AB已使用多年,很少遇到問題,但從本質(zhì)上看,動態(tài)長度行更傾向于出現(xiàn)錯誤,因此,不妨嘗試采用該策略以查看它是否有幫助,這不失為一個好主意。
診斷問題時不要將你的服務(wù)器硬件排除在外。有缺陷的硬件能夠?qū)е聰?shù)據(jù)損壞。對硬件進(jìn)行故障診斷與排除操作時,尤其應(yīng)注意RAM和硬盤驅(qū)動器。
在本節(jié)中,介紹了MySQL響應(yīng)磁盤滿錯誤的方式(如“設(shè)備上無剩余空間”),以及響應(yīng)超配額錯誤的方式(如“寫入失敗”或“達(dá)到了用戶屏蔽限制”)。
本節(jié)介紹的內(nèi)容與寫入MyISAM表有關(guān)。它也適用于寫入二進(jìn)制日志文件和二進(jìn)制索引文件,但對“row”和“record”的應(yīng)用應(yīng)被視為“event”。
出現(xiàn)磁盤滿狀況時,MySQL將:
每分鐘檢查一次,查看是否有足夠空間寫入當(dāng)前行。如果有足夠空間,將繼續(xù),就像什么也未發(fā)生一樣。
每10分鐘將1個條目寫入日志文件,提醒磁盤滿狀況。
為了減輕問題,可采取下述措施:
要想繼續(xù),僅需有足夠的磁盤空間以插入所有記錄。
要想放棄線程,必須使用mysqladmin kill。下次檢查磁盤時將放棄線程(1分鐘)。
其他線程可能會正在等待導(dǎo)致磁盤滿狀況的表。如果有數(shù)個“已鎖定”的線程,殺死正在磁盤滿狀況下等待的某一線程,以便允許其他線程繼續(xù)。
對前述行為的例外是,當(dāng)你使用REPAIR TABLE或OPTIMIZE TABLE時,或當(dāng)索引是在LOAD DATA INFILE或ALTER TABLE語句后、在批操作中創(chuàng)建的。所有這些語句能創(chuàng)建大的臨時文件,如果保留這些文件,會導(dǎo)致系統(tǒng)其他部分出現(xiàn)大問題。如果在MySQL執(zhí)行這類操作的同時磁盤已滿,它將刪除大的臨時文件,并將表標(biāo)注為崩潰。但對于ALTER TABLE例外,舊表保持不變。
MySQL使用環(huán)境變量TMPDIR的值作為保存臨時文件的目錄的路徑名。如果未設(shè)置TMPDIR,MySQL將使用系統(tǒng)的默認(rèn)值,通常為/tmp、/var/tmp或/usr/tmp。如果包含臨時文件目錄的文件系統(tǒng)過小,可對mysqld使用“—tmpdir”選項(xiàng),在具有足夠空間的文件系統(tǒng)內(nèi)指定1個目錄。
在MySQL 5.1中,“—tmpdir”選項(xiàng)可被設(shè)置為數(shù)個路徑的列表,以循環(huán)方式使用。在Unix平臺上,路徑用冒號字符“:”隔開,在Windows、NetWare和OS/2平臺上,路徑用分號字符“;”隔開。注意,為了有效分布負(fù)載,這些路徑應(yīng)位于不同的物理磁盤上,而不是位于相同磁盤的不同分區(qū)中。
如果MySQL服務(wù)器正作為復(fù)制從服務(wù)器使用,不應(yīng)將“--tmpdir”設(shè)置為指向基于內(nèi)存的文件系統(tǒng)的目錄,或當(dāng)服務(wù)器主機(jī)重啟時將清空的目錄。對于復(fù)制從服務(wù)器,需要在機(jī)器重啟時仍保留一些臨時文件,以便能夠復(fù)制臨時表或執(zhí)行LOAD DATA INFILE操作。如果在服務(wù)器重啟時丟失了臨時文件目錄下的文件,復(fù)制將失敗。
MySQL會以隱含方式創(chuàng)建所有的臨時文件。這樣,就能確保中止mysqld時會刪除所有臨時文件。使用隱含文件的缺點(diǎn)在于,在臨時文件目錄所在的位置中,看不到占用了文件系統(tǒng)的大臨時文件。
進(jìn)行排序時(ORDER BY或GROUP BY),MySQL通常會使用1個或多個臨時文件。所需的最大磁盤空間由下述表達(dá)式?jīng)Q定:
(length of what is sorted + sizeof(row pointer))
* number of matched rows
* 2
“row pointer”(行指針)的大小通常是4字節(jié),但在以后,對于大的表,該值可能會增加。
對于某些SELECT查詢,MySQL還會創(chuàng)建臨時SQL表。它們不是隱含表,并具有SQL_*形式的名稱。
ALTER TABLE會在與原始表目錄相同的目錄下創(chuàng)建臨時表。
對于服務(wù)器用來與本地客戶端進(jìn)行通信的Unix套接字文件,其默認(rèn)位置是/tmp/mysql.sock。這有可能導(dǎo)致問題,原因在于,在某些版本的Unix上,任何人都能刪除/tmp目錄下的文件。
在大多數(shù)Unix版本中,可對/tmp目錄進(jìn)行保護(hù),使得文件只能被其所有這或超級用戶(根用戶)刪除。為此,以根用戶身份登錄,并使用下述命令在/tmp目錄上設(shè)置粘著位:
shell> chmod +t /tmp
通過執(zhí)行ls -ld /tmp,可檢查是否設(shè)置了粘著位。如果最后一個許可字符是“t”,表明設(shè)置了粘著位。
另一種方法是改變服務(wù)器創(chuàng)建Unix套接字文件的位置。如果進(jìn)行了這類操作,還應(yīng)讓客戶端程序知道文件的位置。能夠以多種不同方式指定文件位置:
在全局或局部選項(xiàng)文件中指定路徑。例如,將下述行置于文件/etc/my.cnf中:
[mysqld]
socket=/path/to/socket
?
[client]
socket=/path/to/socket
請參見4.3.2節(jié),“使用選項(xiàng)文件”。
在運(yùn)行客戶端程序時,在命令行上為mysqld_safe指定“--socket”選項(xiàng)。
將MYSQL_UNIX_PORT環(huán)境變量設(shè)置為Unix套接字文件的路徑。
重新從源碼編譯MySQL,以使用不同的默認(rèn)Unix套接字文件位置。運(yùn)行configure時,用“--with-unix-socket-path”選項(xiàng)定義文件路徑。請參見2.8.2節(jié),“典型配置選項(xiàng)”。
用下述命令連接服務(wù)器,能夠測試新的套接字位置是否工作:
shell> mysqladmin --socket=/path/to/socket version
如果遇到與SELECT NOW()有關(guān)的問題,它返回GMT值而不是當(dāng)?shù)貢r間,就應(yīng)通知服務(wù)器你的當(dāng)前失去。如果UNIX_TIMESTAMP()返回錯誤值,上述方式同樣適用。應(yīng)為服務(wù)器所運(yùn)行的環(huán)境進(jìn)行這類設(shè)置,例如,在mysqld_safe或mysql.server中。請參見附錄F:環(huán)境變量。
也可以對mysqld_safe使用“--timezone=timezone_name”選項(xiàng),為服務(wù)器設(shè)置失去。也可以在啟動mysqld之前,通過設(shè)置TZ環(huán)境變量完成該設(shè)置。
“--timezone”或TZ的允許值與系統(tǒng)有關(guān)。關(guān)于可接受的值,請參見操作系統(tǒng)文檔。
在默認(rèn)情況下,MySQL搜索不區(qū)分大小寫(但某些字符集始終區(qū)分大小寫,如czech)。這意味著,如果你使用col_name LIKE 'a%'進(jìn)行搜索,你將獲得以A或a開始的所有列。如果打算使搜索區(qū)分大小寫,請確保操作數(shù)之一具有區(qū)分大小寫的或二進(jìn)制校對。例如,如果你正在比較均適用latin1字符集的列和字符串,可使用COLLATE操作符,使1個操作數(shù)具有latin1_general_cs或latin1_bin校對特性。例如:
col_name COLLATE latin1_general_cs LIKE 'a%'
col_name LIKE 'a%' COLLATE latin1_general_cs
col_name COLLATE latin1_bin LIKE 'a%'
col_name LIKE 'a%' COLLATE latin1_bin
如果希望總是以區(qū)分大小寫的方式處理列,可使用區(qū)分大小寫的或二進(jìn)制校對聲明它。請參見13.1.5節(jié),“CREATE TABLE語法”。
簡單的比較操作(>=, >, =, <, <=, 排序和分組)基于每個字符的“排序值”。具有相同排序值的字符(如‘E’, ‘e’,和‘??’)將被當(dāng)作相同的寫字符。
DATE值的格式是'YYYY-MM-DD'。按照標(biāo)準(zhǔn)的SQL,不允許其他格式。在UPDATE表達(dá)式以及SELECT語句的WHERE子句中應(yīng)使用該格式。例如:
mysql> SELECT * FROM tbl_name WHERE date >= '2003-05-05';
為了方便,如果日期是在數(shù)值環(huán)境下使用的,MySQL會自動將日期轉(zhuǎn)換為數(shù)值(反之亦然)。它還具有相當(dāng)?shù)闹悄?,在更新時或在與TIMESTAMP、DATE或DATETIME列比較日期的WHERE子句中,允許“寬松的”字符串形式(“寬松形式”表示,任何標(biāo)點(diǎn)字符均能用作各部分之間的分隔符。例如,'2004-08-15'和'2004#08#15'是等同的)。MySQL還能轉(zhuǎn)換不含任何分隔符的字符串(如'20040815'),前體是它必須是有意義的日期。
使用<、<=、=、>=、>、或BETWEEN操作符將DATE、TIME、DATETIME或TIMESTAMP與常量字符串進(jìn)行比較時,MySQL通常會將字符串轉(zhuǎn)換為內(nèi)部長整數(shù),以便進(jìn)行快速比較(以及略為“寬松”的字符串檢查)。但是,該轉(zhuǎn)換具有下述例外:
比較兩列時
將DATE、TIME、DATETIME或TIMESTAMP列與表達(dá)式進(jìn)行比較時
使用其他比較方法時,如IN或STRCMP()。
對于這些例外情形,會將對象轉(zhuǎn)換為字符串并執(zhí)行字符串比較,采用該方式進(jìn)行比較。
為了保持安全,假定按字符串比較字符串,如果你打算比較臨時值和字符串,將使用恰當(dāng)?shù)淖址瘮?shù)。
對于特殊日期'0000-00-00',能夠以'0000-00-00'形式保存和檢索。在MyODBC中使用'0000-00-00'日期時,對于MyODBC 2.50.12或更高版本,該日期將被自動轉(zhuǎn)換為NULL,這是因?yàn)?span>ODBC不能處理這類日期。
由于MySQL能夠執(zhí)行前面所介紹的轉(zhuǎn)換,下述語句均能正常工作:
mysql> INSERT INTO tbl_name (idate) VALUES (19970505);
mysql> INSERT INTO tbl_name (idate) VALUES ('19970505');
mysql> INSERT INTO tbl_name (idate) VALUES ('97-05-05');
mysql> INSERT INTO tbl_name (idate) VALUES ('1997.05.05');
mysql> INSERT INTO tbl_name (idate) VALUES ('1997 05 05');
mysql> INSERT INTO tbl_name (idate) VALUES ('0000-00-00');
?
mysql> SELECT idate FROM tbl_name WHERE idate >= '1997-05-05';
mysql> SELECT idate FROM tbl_name WHERE idate >= 19970505;
mysql> SELECT MOD(idate,100) FROM tbl_name WHERE idate >= 19970505;
mysql> SELECT idate FROM tbl_name WHERE idate >= '19970505';
但是,下述語句不能正常工作:
mysql> SELECT idate FROM tbl_name WHERE STRCMP(idate,'20030505')=0;
STRCMP()是一種字符串函數(shù),它能將idate轉(zhuǎn)換為'YYYY-MM-DD'格式的字符串,并執(zhí)行字符串比較。它不能將'20030505'轉(zhuǎn)換為日期'2003-05-05'并進(jìn)行日期比較。
如果你正在使用ALLOW_INVALID_DATES SQL模式,MySQL允許以僅執(zhí)行給定的有限檢查方式保存日期:MySQL僅保證天位于1~31的范圍內(nèi),月位于1~12的范圍內(nèi)。
這樣就使得MySQL很適合于Web應(yīng)用程序,其中,你能獲得三個不同字段中的年、月、日值,也能準(zhǔn)確保存用戶插入的值(無日期驗(yàn)證)。
如果未使用NO_ZERO_IN_DATE SQL模式,“天”和“月”部分可能為0。如果你打算將生日保存在DATE列而且僅知道部分日期,它十分方便。
如果未使用NO_ZERO_DATE SQL模式,MySQL也允許你將'0000-00-00'保存為“偽日期”。在某些情況下,它比使用NULL值更方便。
如果無法將日期轉(zhuǎn)換為任何合理值,“0”將保存在DATE列中,并被檢索為'0000-00-00'。這是兼顧速度和便利性的事宜。我們認(rèn)為,數(shù)據(jù)庫服務(wù)器的職責(zé)是檢索與你保存的日期相同的日期(即使在任何情況下,數(shù)據(jù)在邏輯上不正確也同樣)。我們認(rèn)為,對日期的檢查應(yīng)由應(yīng)用程序而不是服務(wù)器負(fù)責(zé)。
如果你希望MySQL檢查所有日期并僅接受合法日期(除非由IGNORE覆蓋),應(yīng)將sql_mode設(shè)置為"NO_ZERO_IN_DATE,NO_ZERO_DATE"。
對于SQL的新手,NULL值的概念常常會造成混淆,他們常認(rèn)為NULL是與空字符串''相同的事。情況并非如此。例如,下述語句是完全不同的:
mysql> INSERT INTO my_table (phone) VALUES (NULL);
mysql> INSERT INTO my_table (phone) VALUES ('');
這兩條語句均會將值插入phone(電話)列,但第1條語句插入的是NULL值,第2條語句插入的是空字符串。第1種情況的含義可被解釋為“電話號碼未知”,而第2種情況的含義可被解釋為“該人員沒有電話,因此沒有電話號碼”。
為了進(jìn)行NULL處理,可使用IS NULL和IS NOT NULL操作符以及IFNULL()函數(shù)。
在SQL中,NULL值與任何其它值的比較(即使是NULL)永遠(yuǎn)不會為“真”。包含NULL的表達(dá)式總是會導(dǎo)出NULL值,除非在關(guān)于操作符的文檔中以及表達(dá)式的函數(shù)中作了其他規(guī)定。下述示例中的所有列均返回NULL:
mysql> SELECT NULL, 1+NULL, CONCAT('Invisible',NULL);
如果打算搜索列值為NULL的列,不能使用expr = NULL測試。下述語句不返回任何行,這是因?yàn)?,對于任何表達(dá)式,expr = NULL永遠(yuǎn)不為“真”:
mysql> SELECT * FROM my_table WHERE phone = NULL;
要想查找NULL值,必須使用IS NULL測試。在下面的語句中,介紹了查找NULL電話號碼和空電話號碼的方式:
mysql> SELECT * FROM my_table WHERE phone IS NULL;
mysql> SELECT * FROM my_table WHERE phone = '';
更多信息和示例,請參見3.3.4.6節(jié),“使用NULL值”。
如果你正在使用MyISAM、InnoDB、BDB、或MEMORY存儲引擎,能夠在可能具有NULL值的列上增加1條索引。如不然,必須聲明索引列為NOT NULL,而且不能將NULL插入到列中。
用LOAD DATA INFILE讀取數(shù)據(jù)時,對于空的或丟失的列,將用''更新它們。如果希望在列中具有NULL值,應(yīng)在數(shù)據(jù)文件中使用\N。在某些情況下,也可以使用文字性單詞“NULL”。請參見13.2.5 “LOAD DATA INFILE語法” 。
使用DISTINCT、GROUP BY或ORDER BY時,所有NULL值將被視為等同的。
使用ORDER BY時,首先將顯示NULL值,如果指定了DESC按降序排列,NULL值將最后顯示。
對于聚合(累計)函數(shù),如COUNT()、MIN()和SUM(),將忽略NULL值。對此的例外是COUNT(*),它將計數(shù)行而不是單獨(dú)的列值。例如,下述語句產(chǎn)生兩個計數(shù)。首先計數(shù)表中的行數(shù),其次計數(shù)age列中的非NULL值數(shù)目:
mysql> SELECT COUNT(*), COUNT(age) FROM person;
對于某些列類型,MySQL將對NULL值進(jìn)行特殊處理。如果將NULL插入TIMESTAMP列,將插入當(dāng)前日期和時間。如果將NULL插入具有AUTO_INCREMENT屬性的整數(shù)列,將插入序列中的下一個編號。
SELECT SQRT(a*b) AS root FROM tbl_name GROUP BY root HAVING root > 0;
SELECT id, COUNT(*) AS cnt FROM tbl_name GROUP BY id HAVING cnt > 0;
SELECT id AS 'Customer identity' FROM tbl_name;
標(biāo)準(zhǔn)SQL不允許在WHERE子句中已用列別名。這是因?yàn)椋瑘?zhí)行WHERE代碼時,可能尚未確定列值。例如,下述查詢是非法的:
SELECT id, COUNT(*) AS cnt FROM tbl_name WHERE cnt > 0 GROUP BY id;
執(zhí)行WHERE語句以確定哪些行應(yīng)被包含在GROUP BY部分中,而HAVING用于確定應(yīng)使用結(jié)果集中的哪些行。
執(zhí)行ROLLBACK(回滾)時,如果收到下述消息,表示事務(wù)中使用的1個或多個表不支持事務(wù):
警告:某些更改的非事務(wù)性表不能被回滾。
這些非事務(wù)性表不受ROLLBACK語句的影響。
如果在事務(wù)中意外地混合了事務(wù)性表和非事務(wù)性表,導(dǎo)致該消息的最可能原因是,你認(rèn)為本應(yīng)是事務(wù)性的表實(shí)際上不是。如你試圖使用mysqld服務(wù)器不支持的事務(wù)性存儲引擎(或用啟動選項(xiàng)禁止了它)創(chuàng)建表,就可能出現(xiàn)該情況。如果mysqld不支持存儲引擎,它將以MyISAM表創(chuàng)建表,這是非事務(wù)性表。
可使用下述語句之一檢查表的標(biāo)類型:
SHOW TABLE STATUS LIKE 'tbl_name';
SHOW CREATE TABLE tbl_name;
請參見13.5.4.18節(jié),“SHOW TABLE STATUS語法以及13.5.4.5節(jié),“SHOW CREATE TABLE語法”。
使用下述語句,可檢查mysqld服務(wù)器支持的存儲引擎:
SHOW ENGINES;
也可以使用下述語句,檢查與你感興趣的存儲引擎有關(guān)的變量值:
SHOW VARIABLES LIKE 'have_%';
例如,要想確定InnoDB存儲引擎是否可用,可檢查have_innodb變量的值。
請參見13.5.4.8節(jié),“SHOW ENGINES語法”和13.5.4.21節(jié),“SHOW VARIABLES語法”。
如果有使用了很多表的復(fù)雜查詢,但未返回任何行,應(yīng)采用下述步驟找出什么出錯:
用EXPLAIN測試查詢,以檢查是否發(fā)現(xiàn)某事顯然出錯。請參見7.2.1節(jié),“EXPLAIN語法(獲取關(guān)于SELECT的信息)”。
僅選擇在WHERE子句中使用的列。
從查詢中1次刪除1個表,直至返回了某些行為止。如果表很大,較好的主意是在查詢中使用LIMIT 10。
對于具有與上次從查詢中刪除的表匹配的行的列,發(fā)出SELECT查詢。
如果將FLOAT或DOUBLE列與具有數(shù)值類型的數(shù)值進(jìn)行比較,不能使用等式(=)比較。在大多數(shù)計算機(jī)語言中,該問題很常見,這是因?yàn)椋⒎撬械母↑c(diǎn)值均能以準(zhǔn)確的精度保存。在某些情況下,將FLOAT更改為DOUBLE可更正該問題。請參見A.5.8節(jié),“與浮點(diǎn)比較有關(guān)的問題”。
如果仍不能找出問題之所在,請創(chuàng)建能與顯示問題的“mysql test < query.sql”一起運(yùn)行的最小測試。通過使用mysqldump --quick db_name tbl_name_1 ... tbl_name_n > query.sql轉(zhuǎn)儲表,可創(chuàng)建測試文件。在編輯器中打開文件,刪除某些插入的行(如果有超出演示問題所需的行),并在文件末尾添加SELECT語句。
通過執(zhí)行下述命令,驗(yàn)證測試文件能演示問題:
shell> mysqladmin create test2
shell> mysql test2 < query.sql
使用mysqlbug將測試文件張貼到喲娜通用MySQL郵件列表。請參見1.7.1.1節(jié),“The MySQL郵件列表”。
浮點(diǎn)數(shù)有時會導(dǎo)致混淆,這是因?yàn)樗鼈儫o法以準(zhǔn)確值保存在計算機(jī)體系結(jié)構(gòu)中。你在屏幕上所看到的值通常不是數(shù)值的準(zhǔn)確值。對于FLOAT和DOUBLE列類型,情況就是如此。DECIMAL列能保存具有準(zhǔn)確精度的值,這是因?yàn)樗鼈兪怯勺址硎镜摹?/span>
在下面的示例中,介紹了使用DOUBLE時的問題:
mysql> CREATE TABLE t1 (i INT, d1 DOUBLE, d2 DOUBLE);
mysql> INSERT INTO t1 VALUES (1, 101.40, 21.40), (1, -80.00, 0.00),
??? -> (2, 0.00, 0.00), (2, -13.20, 0.00), (2, 59.60, 46.40),
??? -> (2, 30.40, 30.40), (3, 37.00, 7.40), (3, -29.60, 0.00),
??? -> (4, 60.00, 15.40), (4, -10.60, 0.00), (4, -34.00, 0.00),
??? -> (5, 33.00, 0.00), (5, -25.80, 0.00), (5, 0.00, 7.20),
??? -> (6, 0.00, 0.00), (6, -51.40, 0.00);
?
mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b
??? -> FROM t1 GROUP BY i HAVING a <> b;
?
+------+-------+------+
| i??? | a???? | b??? |
+------+-------+------+
| ???1 |? 21.4 | 21.4 |
|??? 2 |? 76.8 | 76.8 |
|??? 3 |?? 7.4 |? 7.4 |
|??? 4 |? 15.4 | 15.4 |
|??? 5 |?? 7.2 |? 7.2 |
|??? 6 | -51.4 |??? 0 |
+------+-------+------+
結(jié)果是正確的。盡管前5個記錄看上去不應(yīng)能進(jìn)行比較測試(a和b的值看上去沒有什么不同),但它們能進(jìn)行比較,這是因?yàn)轱@示的數(shù)值間的差異在十分位左右,具體情況取決于計算機(jī)的體系結(jié)構(gòu)。
如果列d1和d2定義為DECIMAL而不是DOUBLE,SELECT查詢的結(jié)果僅包含1行,即上面顯示的最后1行。
MySQL采用了基于開銷的優(yōu)化器,以確定處理查詢的最解方式。在很多情況下,MySQL能夠計算最佳的可能查詢計劃,但在某些情況下,MySQL沒有關(guān)于數(shù)據(jù)的足夠信息,不得不就數(shù)據(jù)進(jìn)行“有教養(yǎng)”的估測。
當(dāng)MySQL未能做“正確的”事時,可使用下述工具來幫助MySQL:
使用EXPLAIN語句獲取關(guān)于MySQL如何處理查詢的信息。要想使用它,可在SELECT語句前添加關(guān)鍵字EXPLAIN:
mysql> EXPLAIN SELECT * FROM t1, t2 WHERE t1.i = t2.i;
關(guān)于EXPLAIN的詳細(xì)討論,請參見7.2.1節(jié),“EXPLAIN語法(獲取關(guān)于SELECT的信息)”。
使用ANALYZE TABLE tbl_name,為已掃描的表更新鍵分配。請參見13.5.2.1節(jié),“ANALYZE TABLE語法”。
為已掃描的表使用FORCE INDEX,通知MySQL:與使用給定的索引相比,表掃描開銷昂貴。請參見13.2.7節(jié),“SELECT語法”。
SELECT * FROM t1, t2 FORCE INDEX (index_for_column)
WHERE t1.col_name=t2.col_name;
USE INDEX和IGNORE INDEX也有一定的幫助。
關(guān)于全局和表級別的STRAIGHT_JOIN。請參見13.2.7節(jié),“SELECT語法”。
你可以調(diào)節(jié)全局或線程類系統(tǒng)變量。例如,用“--max-seeks-for-key=1000”選項(xiàng)啟動mysqld,或使用“SET max_seeks_for_key=1000”來通知優(yōu)化器:假定任何表掃描均不會導(dǎo)致1000個以上的鍵搜索。請參見5.3.3節(jié),“服務(wù)器系統(tǒng)變量”。
ALTER TABLE將表更改為當(dāng)前字符集。如果在執(zhí)行ALTER TABLE操作期間遇到重復(fù)鍵錯誤,原因在于新的字符集將2個鍵映射到了相同值,或是表已損壞。在后一種情況下,應(yīng)在表上運(yùn)行REPAIR TABLE。
如果ALTER TABLE失敗并給出下述錯誤,問題可能是因?yàn)樵?span>ALTER TABLE操作的早期階段出現(xiàn)MySQL崩潰,沒有名為A-xxx或B-xxx的舊表:
Error on rename of './database/name.frm'
to './database/B-xxx.frm' (Errcode: 17)
在該情況下,進(jìn)入MySQL數(shù)據(jù)目錄,并刪除其名稱為以A-或B-開始的所有文件(或許你希望將它們移動到其他地方而不是刪除它們)。
ALTER TABLE的工作方式如下:
如果在重命名操作中出錯,MySQL將嘗試撤銷更改。如果錯誤很嚴(yán)重(盡管這不應(yīng)出現(xiàn)),MySQL會將舊表保留為B-xxx。簡單地在系統(tǒng)級別上重命名表文件,應(yīng)能使數(shù)據(jù)復(fù)原。
如果在事務(wù)性表上使用ALTER TABLE,或正在使用Windows或OS/2操作系統(tǒng),如果已在表上執(zhí)行了LOCK TABLE操作,ALTER TABLE將對表執(zhí)行解鎖操作。這是因?yàn)?span>InnoDB和這類操作系統(tǒng)不能撤銷正在使用的表。
首先,請考慮是否的確需要更改表中的列順序。SQL的核心要點(diǎn)是從數(shù)據(jù)存儲格式獲取應(yīng)用??倯?yīng)指定檢索數(shù)據(jù)的順序。在下面的第1條語句中,以col_name1、col_name2、col_name3順序返回列;在第2條語句中,以col_name1、col_name3、col_name2順序返回列:
mysql> SELECT col_name1, col_name2, col_name3 FROM tbl_name;
mysql> SELECT col_name1, col_name3, col_name2 FROM tbl_name;
如果決定更改表列的順序,可執(zhí)行下述操作:
mysql> INSERT INTO new_table
??? -> SELECT columns-in-new-order FROM old_table;
mysql> ALTER TABLE new_table RENAME old_table;
SELECT *十分適合于測試查詢。但是,在應(yīng)用程序中,永遠(yuǎn)不要依賴SELECT *的使用,不要依賴根據(jù)其位置檢索列。如果添加、移動或刪除了列,所返回的列的順序和位置不會保持相同。對表結(jié)構(gòu)的簡單更改也會導(dǎo)致應(yīng)用程序失敗。
下面介紹了對使用TEMPORARY表的限制:
mysql> SELECT * FROM temp_table, temp_table AS t2;
錯誤1137:不能再次打開表:'temp_table'
mysql> ALTER TABLE orig_name RENAME new_name;
在本節(jié)中,列出了當(dāng)前MySQL版本中的已知事宜。
關(guān)于平臺相關(guān)事宜的更多信息,請參見2.12節(jié),“具體操作系統(tǒng)相關(guān)的注意事項(xiàng)”和附錄E:移植到其他系統(tǒng)中的安裝和移植說明。
下面列出了已知問題,更正它們具有較高的優(yōu)先級:
001207 22:07:56? bdb:? log_flush: LSN past current end-of-log
如果以特定的方式設(shè)計查詢,使得數(shù)據(jù)更改是非決定性(通常不推薦,即使在復(fù)制之外也同樣),主服務(wù)器和從服務(wù)器上的數(shù)據(jù)將變得不同。
例如:
- 將0或NULL值插入AUTO_INCREMENT列中的CREATE ... SELECT或INSERT ... SELECT語句。
- DELETE,如果從具有ON DELETE CASCADE屬性的外鍵的表中刪除行。
- REPLACE ... SELECT、INSERT IGNORE ... SELECT,如果在插入的數(shù)據(jù)中具有重復(fù)鍵。
當(dāng)且僅當(dāng)前述查詢沒有保證決定行順序的ORDER BY子句時。
例如,對于不具有ORDER BY的INSERT ... SELECT,SELECT可能會以不同的順序返回行(它會導(dǎo)致具有不同等級的行,從而導(dǎo)致AUTO_INCREMENT列中的不同數(shù)值),具體情況取決于優(yōu)化器在主服務(wù)器和從服務(wù)器上所作的選擇。
在主服務(wù)器和從服務(wù)器上,查詢將進(jìn)行不同的優(yōu)化,僅當(dāng):
- 使用不同的存儲引擎在主服務(wù)器上而不是從服務(wù)器上保存表。(能夠在主服務(wù)器和從服務(wù)器上使用不同的存儲引擎。例如,如果從服務(wù)器具有較少的可用磁盤空間,可以在主服務(wù)器上使用InnoDB,但在 從服務(wù)器桑使用MyISAM)。
- 在主服務(wù)器和從服務(wù)器上,MySQL緩沖區(qū)大小是不同的(key_buffer_size等)。
- 在主服務(wù)器和從服務(wù)器上運(yùn)行不同的MySQL版本,版本間的優(yōu)化器代碼也不同。
該問題也會影響使用mysqlbinlog|mysql的數(shù)據(jù)庫恢復(fù)。
避免該問題的最簡單方法是,為前述的非決定性查詢增加ORDER BY子句,以確??偸且韵嗤捻樞虮4婊蚋男?。
在將來的MySQL版本中,需要時,我們將自動增加ORDER BY子句。
下面列出了已知的事宜,這些事宜將在恰當(dāng)?shù)臅r候更正:
mysql> UPDATE tbl_name SET KEY=KEY+1,KEY=KEY+1;
mysql> SELECT * FROM temp_table, temp_table AS t2;
錯誤1137:不能再次打開表:'temp_table'
例如:
SELECT DISTINCT mp3id FROM band_downloads
?????? WHERE userid = 9 ORDER BY id DESC;
以及
SELECT DISTINCT band_downloads.mp3id
?????? FROM band_downloads,band_mp3
?????? WHERE band_downloads.userid = 9
?????? AND band_mp3.id = band_downloads.mp3id
?????? ORDER BY band_downloads.id DESC;
在第2種情況下,使用MySQL服務(wù)器3.23.x,可在結(jié)果集中獲得2個等同行(這是因?yàn)?,隱藏ID列中的值可能不同)。
注意,在結(jié)果集中,僅對不含ORDER BY列的查詢才會出現(xiàn)該情況。
這是MySQL參考手冊的翻譯版本,關(guān)于MySQL參考手冊,請訪問dev.mysql.com。原始參考手冊為英文版,與英文版參考手冊相比,本翻譯版可能不是最新的。