18.2.1 MySQL server has gone away
錯誤
本小節(jié)也涉及有關(guān)Lost connection to server during query
的錯誤。
對MySQL server has gone away
錯誤最常見的原因是服務(wù)器超時了并且關(guān)閉了連接。缺省地,如果沒有事情發(fā)生,服務(wù)器在 8個小時后關(guān)閉連接。你可在啟動mysqld時通過設(shè)置wait_timeout
變量改變時間限制。
你可以通過執(zhí)行mysqladmin version
并且檢驗正常運行的時間來檢查MySQL還沒死掉。
如果你有一個腳本,你只須再發(fā)出查詢讓客護進行一次自動的重新連接。
在這種請下,你通常能獲得下列錯誤代碼(你得到的是OS相關(guān)的):
CR_SERVER_GONE_ERROR
|
客戶不能發(fā)送一個問題給服務(wù)器。 |
CR_SERVER_LOST
|
當(dāng)寫服務(wù)器時,客戶沒有出錯,但是它沒有得到對問題的一個完整的答案(或任何答案)。 |
如果你向服務(wù)器發(fā)送不正確的或太大的查詢,你也可能得到這些錯誤。如果mysqld
得到一個太大或不正常的包,它認為客戶出錯了并關(guān)閉連接。如果你需要較大的查詢(例如,如果你正在處理較大的BLOB
列),你可以使用-O max_allowed_packet=#
選項(缺省1M)啟動mysqld
以增加查詢限制。多余的內(nèi)存按需分配,這樣mysqld
只有在你發(fā)出較大差詢時或mysqld
必須返回較大的結(jié)果行時,才使用更多的內(nèi)存!
18.2.2 Can't connect to [local] MySQL server
錯誤
一個MySQL客戶可以兩種不同的方式連接mysqld
服務(wù)器:Unix套接字,它通過在文件系統(tǒng)中的一個文件(缺省“/tmp/mysqld.sock”)進行連接;或TCP/IP,它通過一個端口號連接。Unix套接字比TCP/IP更快,但是只有用在連接同一臺計算機上的服務(wù)器。如果你不指定主機名或如果你指定特殊的主機名localhost
,使用Unix套接字。
錯誤(2002)Can't connect to ...
通常意味著沒有一個MySQL服務(wù)器運行在系統(tǒng)上或當(dāng)試圖連接mysqld
服務(wù)器時,你正在使用一個錯誤的套接字文件或TCP/IP端口。
由檢查(使用ps
)在你的服務(wù)器上有一個名為mysqld
的進程啟動!如果沒有任何mysqld
過程,你應(yīng)該啟動一個。見4.15.2 啟動MySQL服務(wù)器的問題。
如果一個mysqld
過程正在運行,你可以通過嘗試這些不同的連接來檢查服務(wù)器(當(dāng)然,端口號和套接字路徑名可能在你的安裝中是不同的):
shell> mysqladmin version
shell> mysqladmin variables
shell> mysqladmin -h `hostname` version variables
shell> mysqladmin -h `hostname` --port=3306 version
shell> mysqladmin -h 'ip for your host' version
shell> mysqladmin --socket=/tmp/mysql.sock version
注意hostname
命令使用反引號“`”而非正引號“'”;這些導(dǎo)致hostname
輸出(即,當(dāng)前主機名)被代替進mysqladmin
命令中。
這是可能造成Can't connect to local MySQL server
錯誤的一些原因:
-
mysqld
不在運行。
- 你正在使用MIT-pthreads的一個系統(tǒng)上運行。如果正在運行在一個沒有原生線程的系統(tǒng)上,
mysqld
使用 MIT-pthreads 軟件包。見4.2 由MySQL支持的操作系統(tǒng)。然而,MIT-pthreads不支持Unix套接字,因此當(dāng)與服務(wù)器連接時,在這樣一個系統(tǒng)上,你總是必須明確地指定主機名。試試使用這個命令檢查到服務(wù)器的連接:shell> mysqladmin -h `hostname` version
- 某人刪除了
mysqld
使用的Unix套接字(缺省“/tmp/mysqld.sock”)。你可能有一個cron
任務(wù)刪除了MySQL套接字(例如,一個把舊文件從“/tmp”目錄中刪除的任務(wù))。你總是可以運行mysqladmin version
并且檢查mysqladmin
正在試圖使用的套接字確實存在。在這種情況下,修復(fù)方法是刪除cron
任務(wù)而不刪除“mysqld.sock 或?qū)⑻捉幼址旁谄渌胤健D隳苡眠@個命令在MySQL配置時指定一個不同的套接字地點:shell> ./configure --with-unix-socket-path=/path/to/socket
你也可以使用--socket=/path/to/socket
選項啟動safe_mysqld
和在啟動你的MySQL客戶前設(shè)置環(huán)境變量MYSQL_UNIX_PORT
為套接字路徑名。你可用--socket=/path/to/socket
選項啟動mysqld
服務(wù)器。如果你改變了服務(wù)器的套接字路徑名,你也必須通知MySQL客戶關(guān)于新路徑的情況。你可以通過設(shè)置環(huán)境變量MYSQL_UNIX_PORT
為套接字路徑名或由提供套接字路徑名作為客戶的參數(shù)做到。你可用這個命令測試套接字:
shell> mysqladmin --socket=/path/to/socket version
- 你正在使用 Linux和線程已經(jīng)死了(核心傾倒了)。在這種情況中,你必須殺死其它
mysqld
線程(例如在啟動一個新的MySQL服務(wù)器之前,可以用mysql_zap
腳本)。見18.1 如果MySQL總是崩潰怎么辦。
如果你得到錯誤Can't connect to MySQL server on some_hostname
,你可以嘗試下列步驟找出問題是什么:
- 通過執(zhí)行
telnet your-host-name tcp-ip-port-number
并且按幾次回車來檢查服務(wù)器是否正常運行。如果有一個MySQL運行在這個端口上,你應(yīng)該得到一個包含正在運行的MySQL服務(wù)器的版本號的應(yīng)答。如果你得到類似于telnet: Unable to connect to remote host: Connection refused
的一個錯誤,那么沒有服務(wù)器在使用的端口上運行。
- 嘗試連接本地機器上的
mysqld
守護進程,并用mysqladmin variables
檢查mysqld被配置使用的TCP/IP端口(變量port
)。
- 檢查你的
mysqld
服務(wù)器沒有用--skip-networking
選項啟動。
18.2.3 Host '...' is blocked
錯誤
如果你得到象這樣的一個錯誤:
Host 'hostname' is blocked because of many connection errors.
Unblock with 'mysqladmin flush-hosts'
這意味著,mysqld
已經(jīng)得到了大量(max_connect_errors
)的主機'hostname'
的在中途被中斷了的連接請求。在max_connect_errors
次失敗請求后,mysqld
認定出錯了(象來字一個黑客的攻擊),并且阻止該站點進一步的連接,直到某人執(zhí)行命令mysqladmin flush-hosts
。
缺省地,mysqld
在10個連接錯誤后阻塞一臺主機。你可以通過象這樣啟動服務(wù)器很容易地調(diào)整它:
shell> safe_mysqld -O max_connect_errors=10000 &
注意,對給定的主機,如果得到這條錯誤消息,你應(yīng)該首先檢查該主機的TCP/IP連接有沒有問題。如果你的TCP/IP連接不在運行,增加max_connect_errors
變量的值對你也不會有幫助!
18.2.4 Too many connections
錯誤
如果在你試土連接MySQL時,你得到錯誤Too many connections
,這意味著已經(jīng)有max_connections
個客戶連接了mysqld服務(wù)器。
如果你需要比缺省(100)更多的連接,那么你應(yīng)該重啟mysqld
,用更大的 max_connections 變量值。
注意,mysqld
實際上允許(max_connections+1)個客戶連接。最后一個連接是為一個用Process
權(quán)限的用戶保留的。通過不把這個權(quán)限給一般用戶(他們不應(yīng)該需要它),有這個權(quán)限一個管理員可以登錄并且使用SHOW PROCESSLIST
找出什么可能出錯。見7.21 SHOW
句法(得到表,列的信息)。
18.2.5 Out of memory
錯誤
如果你發(fā)出查詢并且得到類似于下面的錯誤:
mysql: Out of memory at line 42, 'malloc.c'
mysql: needed 8136 byte (8k), memory in use: 12481367 bytes (12189k)
ERROR 2008: MySQL client ran out of memory
注意,錯誤指向了MySQL客戶mysql
。這個錯誤的原因很簡單,客戶沒有足夠的內(nèi)存存儲全部結(jié)果。
為了修正這個問題,首先檢查你的查詢是否正確。它應(yīng)該返回這么多的行,這合理嗎?如果是這樣,你可以使用mysql --quick
,它使用mysql_use_result()
檢索結(jié)果集合。這將較少的負擔(dān)放在了客戶端(只是服務(wù)器更多)。
18.2.6 Packet too large
錯誤
當(dāng)一個MySQL客戶或mysqld
服務(wù)器得到一個比max_allowed_packet
個字節(jié)長的包,它發(fā)出一個Packet too large
錯誤并終止連接。
如果你正在使用mysql
客戶,你可以通過用mysql --set-variable=max_allowed_packet=8M
指定一個更大的緩沖區(qū)來啟動客戶程序。
如果你正在使用不允許你指定最大包大小的其他客戶(例如 DBI
),你需要在你啟動服務(wù)器時設(shè)置包大小。你可以使用mysqld
的命令行選項設(shè)置max_allowed_packet
為一個更大的尺寸。例如,如果你正期望將一個全長的BLOB
存入一張表中,你將需要用--set-variable=max_allowed_packet=24M
選項來啟動服務(wù)器。
18.2.7 The table is full
錯誤
這個錯誤發(fā)生在內(nèi)存臨時表變得比tmp_table_size
字節(jié)大時。為了避免這個問題,你可以使用mysqld
的-O tmp_table_size=#
選項來增加臨時表的大小,或在你發(fā)出有疑問的查詢之前使用SQL選項SQL_BIG_TABLES
。見7.25 SET OPTION
句法。
你也可以使用--big-tables
選項啟動mysqld
。這與為所有查詢使用SQL_BIG_TABLES
完全相同。
18.2.8 Commands out of sync
in client錯誤
如果你在你的客戶代碼中得到Commands out of sync; You can't run this command now
,你正在以錯誤的次序調(diào)用客戶函數(shù)!
這可能發(fā)生,例如,如果你正在使用mysql_use_result()
并且在你已經(jīng)調(diào)用了mysql_free_result()
之前試圖執(zhí)行新查詢。如果你在mysql_use_result()
或mysql_store_result()
之間試圖執(zhí)行返回數(shù)據(jù)的2個查詢,它也可能發(fā)生。
18.2.9 Ignoring user
錯誤
如果你得到下列錯誤:
Found wrong password for user: 'some_user@some_host'; Ignoring user
這意味著在mysqld
啟動時或在它再次裝載權(quán)限表時,它在user
表中找到了一個有一個無效口令的條目。結(jié)果,條目簡單地被權(quán)限系統(tǒng)忽略。
可能導(dǎo)致這個問題的原因和修正:
18.2.10 Table 'xxx' doesn't exist
錯誤
如果你得到錯誤Table 'xxx' doesn't exist
或Can't find file: 'xxx' (errno: 2)
,這意味著在當(dāng)前數(shù)據(jù)庫中沒有名為xxx
的表存在。
注意,因為MySQL使用目錄和文件存儲數(shù)據(jù)庫和表,數(shù)據(jù)庫和表名件是區(qū)分大小寫的!(在Win32上,數(shù)據(jù)庫和表名不是區(qū)分大小寫的,但是在查詢中對所有表的引用必須使用相同的大小寫!)
你可以用SHOW TABLES
檢查你在當(dāng)前數(shù)據(jù)庫中有哪個表。見7.21 SHOW
句法(得到表、列的信息)。
18.3 MySQL怎樣處理一個溢出的磁盤
當(dāng)出現(xiàn)一個磁盤溢出的情況時,MySQL做下列事情:
- 它每分鐘檢查一次看是否有足夠空間寫入當(dāng)前行。如果有足夠的空間,它繼續(xù)好像發(fā)生什么事情。
- 每6分鐘它將有關(guān)磁盤溢出的警告寫入日志文件。
為了緩和這個問題,你可以采取下列行動:
- 繼續(xù),你只需釋放足夠的空閑磁盤空間以便插入所有記錄。
- 放棄線程,你必須發(fā)一個
mysqladmin kill
到線程。在下一次檢查磁盤時,線程將被放棄(在1分鐘內(nèi))。
- 注意,其他線程可能正在等待引起“磁盤溢出”條件的表。如果你有幾個“鎖定的”的線程,殺死正在等待磁盤溢出條件的那個線程將允許其他線程繼續(xù)。
18.4 如何從一個文本文件運行SQL命令
一般地,mysql
客戶被交互性地使用,象這樣:
shell> mysql database
然而,也可以把你的SQL命令放在一個文件中并且告訴mysql
從該文件讀取其輸入。要想這樣做,創(chuàng)造一個文本文件“text_file”,它包含你想要執(zhí)行的命令。然后如下那樣調(diào)用mysql
:
shell> mysql database < text_file
你也能啟動有一個USE db_name
語句的文本文件。在這種情況下,在命令行上指定數(shù)據(jù)庫名是不必要的:
shell> mysql < text_file
見12.1 不同的MySQL程序概述。
18.5 MySQL在哪兒存儲臨時文件
MySQL使用TMPDIR
環(huán)境變量的值作為存儲臨時文件的目錄的路徑名。如果你沒有設(shè)置TMPDIR
,MySQL使用系統(tǒng)缺省值,它通常是“/tmp”或“/usr/tmp”。如果包含你的臨時文件目錄的文件系統(tǒng)太小,你應(yīng)該編輯safe_mysqld
設(shè)定TMPDIR
指向你有足夠空間的一個文件系統(tǒng)!你也可以使用mysqld的--tmpdir
選項目設(shè)置臨時目錄。
MySQL以“隱含文件”創(chuàng)建所有臨時文件。這保證了如果mysqld
被終止,臨時文件也將被刪除。使用隱含文件的缺點是你將看不到一個大的臨時文件填滿了臨時文件目錄所在的文件系統(tǒng)。
當(dāng)排序(ORDER BY
或GROUP BY
)時,MySQL通常使用一個或兩個臨時文件。最大磁盤空間需求是:
(存儲東西的長度 + sizeof (數(shù)據(jù)庫指針))
* 匹配的行數(shù)
* 2
sizeof(數(shù)據(jù)庫指針)
通常是4,但是在未來對確實很大的表可能增加。
對一些SELECT
查詢,MySQL也創(chuàng)建臨時SQL表。這些沒被隱含且有“SQL_*”格式的名字。
ALTER TABLE
和OPTIMIZE TABLE
在原數(shù)據(jù)庫表的同一個目錄中創(chuàng)建一張臨時表。
18.6 怎樣保護“/tmp/mysql.sock ”不被刪除
如果你有這個問題,事實上任何人可以刪除MySQL通訊套接字“/tmp/mysql.sock”,在Unix的大多數(shù)版本上,你能通過為其設(shè)置sticky
(t)位來保護你的“/tmp”文件系統(tǒng)。作為root
登錄并且做下列事情:
shell> chmod +t /tmp
這將保護你的“/tmp”文件系統(tǒng)使得文件僅能由他們的所有者或超級用戶(root
)刪除。
你能執(zhí)行ls -ld /tmp
檢查sticky
位是否被設(shè)置,如果最后一位許可位是t
,該位被設(shè)置了。
18.7 Access denied
錯誤
見6.6 權(quán)限系統(tǒng)如何工作。并且特別要看6.13 引起Access denied
錯誤的原因。
18.8 怎樣作為一個一般用戶運行MySQL
MySQL服務(wù)器mysqld
能被任何用戶啟動并運行。為了將mysqld
改由Unix用戶user_name
來運行,你必須做下列事情:
- 如果它正在運行,停止服務(wù)器(使用
mysqladmin shutdown
)。
- 改變數(shù)據(jù)庫目錄和文件以便
user_name
有權(quán)限讀和寫文件(你可能需要作為Unix的root
用戶才能做到):shell> chown -R user_name /path/to/mysql/datadir
如果在MySQL數(shù)據(jù)目錄中的目錄或文件是符號鏈接,你也將需要順著那些鏈接并改變他們指向的目錄和文件。chown -R
不能跟隨符號鏈接。
- 以
user_name
用戶啟動服務(wù)器,或如果你正在使用MySQL 3.22或以后版本,以Unix root
用戶啟動mysqld
并使用--user=user_name
選項,mysqld
將在接受任何連接之前切換到以Unix user_name
用戶運行。
- 如果在系統(tǒng)被重新啟動時,你使用
mysql.server
腳本啟動mysqld
,你應(yīng)該編輯mysql.server
用su
以用戶user_name
運行mysqld
,或使用--user
選項調(diào)用mysqld
。(不改變safe_mysqld
是必要的。)
現(xiàn)在,你的mysqld
進程應(yīng)該正在作為Unix用戶user_name
運行,并運行完好。盡管有一件事情沒有變化:權(quán)限表的內(nèi)容。缺省 地(就在運行了腳本mysql_install_db
安裝的權(quán)限表后),MySQL用戶root
是唯一有存取mysql
數(shù)據(jù)庫或創(chuàng)建或拋棄數(shù)據(jù)庫權(quán)限的用戶。除非你改變了那些權(quán)限,否則他們?nèi)匀槐3帧.?dāng)你作為一個Unix用戶而不是root
登錄時,這不應(yīng)該阻止你作為MySQLroot
用戶來存取MySQL;只要為客戶程序指定-u root
的選項。
注意通過在命令行上提供-u root
,作為root
存取MySQL,與作為Unix root
用戶或其他Unix用戶運行MySQL沒有關(guān)系。MySQL的存取權(quán)限和用戶名與Unix用戶名字是完全分開的。唯一與Unix用戶名有關(guān)的是,如果當(dāng)你調(diào)用一個客戶程序時,你不提供一個-u
選項,客戶將試圖使用你的Unix登錄名作為你的MySQL用戶名進行連接。
如果你的Unix機器本身不安全,你可能應(yīng)該至少在存取表中為MySQLroot
用戶放上一個口令。否則,在那臺機器上有一個帳號的任何用戶能運行mysql -u root db_name
并且做他喜歡做的任何事情。
18.9 怎樣重新設(shè)置一個忘記的口令
如果你忘記了MySQL的root
用戶的口令,你可以用下列過程恢復(fù)它。
- 通過發(fā)送一個
kill
(不是kill -9
)到mysqld
服務(wù)器來關(guān)閉mysqld服務(wù)器。pid 被保存在一個.pid
文件中,通常在MySQL數(shù)據(jù)庫目錄中:kill `cat /mysql-data-directory/hostname.pid`
你必須是一個UNIX root
用戶或運行服務(wù)器的相同用戶做這個。
- 使用
--skip-grant-tables
選項重啟mysqld
。
- 用
mysql -h hostname mysql
連接mysqld服務(wù)器并且用一條GRANT
命令改變口令。見7.26 GRANT
和REVOKE
句法。你也可以用mysqladmin -h hostname -u user password 'new password'
進行。
- 用
mysqladmin -h hostname flush-privileges
或用SQL命令FLUSH PRIVILEGES
來裝載權(quán)限表。
18.10 文件許可權(quán)限問題
如果你有與文件許可有關(guān)的問題,例如,如果當(dāng)你創(chuàng)建一張表時,mysql
發(fā)出下列錯誤消息:
ERROR: Can't find file: 'path/with/filename.frm' (Errcode: 13)
那么可能是在mysqld
啟動時,環(huán)境變量UMASK
可能設(shè)置不正確。缺省的umask值是0660
。你可以如下啟動safe_mysqld
改變其行為:
shell> UMASK=384 # = 600 in octal
shell> export UMASK
shell> /path/to/safe_mysqld &
18.11 文件沒找到
如果你從MySQL得到ERROR '...' not found (errno: 23)
, Can't open file: ... (errno: 24)
或任何其他有errno 23
或errno 24
的錯誤,它意味著,你沒有為MySQL分配足夠的文件描述符。你能使用perror
實用程序得到錯誤號含義是什么的描述:
shell> perror 23
File table overflow
shell> perror 24
Too many open files
這里的問題是mysqld
正在試圖同時保持打開太多的文件。你也可以告訴mysqld
一次不打開那么多的文件,或增加mysqld
可得到的文件描述符數(shù)量。
為了告訴mysqld
一次保持打開更少的文件,你可以通過使用safe_mysqld
的-O table_cache=32
選項(缺省值是64)使表緩沖更小。減小max_connections
值也將減少打開文件的數(shù)量(缺省值是90)。
要想改變mysqld
可用的文件描述符數(shù)量,修改safe_mysqld
腳本。腳本中有一條注釋了的行ulimit -n 256
。你可以刪除'#'
字符來去掉該行的注釋,并且改變數(shù)字256改變?yōu)?code>mysqld可用的文件描述符的數(shù)量。
ulimit
能增加文件描述符的數(shù)量,但是只能到操作系統(tǒng)強加的限制。如果你需要增加每個進程可用的文件描述符數(shù)量的OS限制,參見你的操作系統(tǒng)文檔。
注意,如果你運行tcsh
外殼,ulimit
將不工作!當(dāng)你請求當(dāng)前限制時,tcsh
也將報告不正確的值!在這種情況下,你應(yīng)該用sh
啟動safe_mysqld
!
18.12 使用DATE
列的問題
一個DATE
值的格式是'YYYY-MM-DD'
。根據(jù)ANSI SQL,不允許其他格式。你應(yīng)該在UPDATE
表達式和SELECT
語句的WHERE子句中使用這個格式。例如:
mysql> SELECT * FROM tbl_name WHERE date >= '1997-05-05';
為了方便,如果日期用在數(shù)字上下文,MySQL自動變換一個日期到一個數(shù)字(并且反過來也如此)。當(dāng)更新時和將一個日期與TIMESTAMP
、DATE
或DATETIME
列比較的一個WHERE
子句中,也是足夠靈活以允許一種“寬松”的字符串格式。(寬松格式意味著任何標點字符用作在部件之間的分割符。例如,'1998-08-15'
和'1998#08#15'
是等價的。)MySQL也能變換不包含分割符的一個字符串(例如 '19980815'
),如果它作為一個日期說得通。
特殊日期'0000-00-00'
可以作為'0000-00-00'
被存儲和檢索。
當(dāng)通過MyODBC使用一個'0000-00-00'
日期時,在MyODBC 2.50.12和以上版本,它將自動被轉(zhuǎn)換為NULL
,因為ODBC不能處理這種日期。
因為MySQL實行了上述的變換,下列語句可以工作:
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,'19970505')=0;
STRCMP()
是字符串函數(shù),因此它將idate
轉(zhuǎn)換為一個字符串并且實施字符串比較。它不將'19970505'
轉(zhuǎn)換為一個日期并實施日期比較。
注意,MySQL不檢查日期是否正確。如果你存儲一個不正確的日期,例如'1998-2-31'
,錯誤的日期將被存儲。如果日期不能被變換到任何合理的值,在DATE
字段中存儲一個0
。這主要是一個速度問題并且我們認為檢查日期是應(yīng)用程序的責(zé)任,而不服務(wù)器。
18.13 時區(qū)問題
如果你有一個問題,SELECT NOW()
以GMT時間返回值而不是你的本地時間,你必須設(shè)定TZ
環(huán)境變量為你的當(dāng)前時區(qū)。這應(yīng)該在服務(wù)器運行的環(huán)境進行,例如在safe_mysqld
或mysql.server
中。
18.14 在搜索中的大小寫敏感性
缺省地,MySQL搜索是大小寫不敏感的(盡管有一些字符集從來不是忽略大小寫的,例如捷克語
)。這意味著,如果你用col_name LIKE 'a%'
搜尋,你將得到所有以A
或a
開始的列值。如果你想要使這個搜索大小寫敏感,使用象INDEX(col_name, "A")=0
檢查一個前綴。或如果列值必須確切是"A"
,使用STRCMP(col_name, "A") = 0
。
簡單的比較操作(>=、>、= 、< 、<=
、排序和聚合)是基于每個字符的“排序值”。有同樣排序值的字符(象E,e和'e)被視為相同的字符!
LIKE
比較在每個字符的大寫值上進行(E==e 但是E<>'e)。
如果你想要一個列總是被當(dāng)作大小寫敏感的方式,聲明它為BINARY
。見7.7 CREATE TABLE
句法。
如果你使用以所謂的big5編碼的中文數(shù)據(jù),你要使所有的字符列是BINARY
,它可行,是因為big5編碼字符的排序順序基于 ASCII代碼的順序。
18.15 NULL
值問題
NULL
值的概念是造成SQL的新手的混淆的普遍原因,他們經(jīng)常認為NULL
是和一個空字符串''
的一樣的東西。不是這樣的!例如,下列語句是完全不同的:
mysql> INSERT INTO my_table (phone) VALUES (NULL);
mysql> INSERT INTO my_table (phone) VALUES ("");
兩個語句把值插入到phone
列,但是第一個插入一個NULL
值而第二個插入一個空字符串。第一個的含義可以認為是“電話號碼不知道”,而第二個則可意味著“她沒有電話”。
在SQL中,NULL
值在于任何其他值甚至NULL
值比較時總是假的(FALSE)。包含NULL
的一個表達式總是產(chǎn)生一個NULL
值,除非在包含在表達式中的運算符和函數(shù)的文檔中指出。在下列例子,所有的列返回NULL
:
mysql> SELECT NULL,1+NULL,CONCAT('Invisible',NULL);
如果你想要尋找值是NULL
的列,你不能使用=NULL
測試。下列語句不返回任何行,因為對任何表達式,expr = NULL
是假的:
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 = "";
在MySQL中,就像很多其他的SQL服務(wù)器一樣,你不能索引可以有NULL
值的列。你必須聲明這樣的列為NOT NULL
,而且,你不能插入NULL
到索引的列中。
當(dāng)用LOAD DATA INFILE
讀取數(shù)據(jù)時,空列用''
更新。如果你想要在一個列中有NULL
值,你應(yīng)該在文本文件中使用\N
。字面上的詞'NULL'
也可以在某些情形下使用。見7.16 LOAD DATA INFILE
句法。
當(dāng)使用ORDER BY
時,首先呈現(xiàn)NULL
值。如果你用DESC
以降序排序,NULL
值最后顯示。當(dāng)使用GROUP BY
時,所有的NULL
值被認為是相等的。
為了有助于NULL
的處理,你能使用IS NULL
和IS NOT NULL
運算符和IFNULL()
函數(shù)。
對某些列類型,NULL
值被特殊地處理。如果你將NULL
插入表的第一個TIMESTAMP
列,則插入當(dāng)前的日期和時間。如果你將NULL
插入一個AUTO_INCREMENT
列,則插入順序中的下一個數(shù)字。
18.16 alias
問題
你可以在GROUP BY
、ORDER BY
或在HAVING
部分中使用別名引用列。別名也可以用來為列取一個更好點的名字:
SELECT SQRT(a*b) as rt FROM table_name GROUP BY rt HAVING rt > 0;
SELECT id,COUNT(*) AS cnt FROM table_name GROUP BY id HAVING cnt > 0;
SELECT id AS "Customer identity" FROM table_name;
注意,你的 ANSI SQL 不允許你在一個WHERE
子句中引用一個別名。這是因為在WHERE
代碼被執(zhí)行時,列值還可能沒有終結(jié)。例如下列查詢是不合法:
SELECT id,COUNT(*) AS cnt FROM table_name WHERE cnt > 0 GROUP BY id;
WHERE
語句被執(zhí)行以確定哪些行應(yīng)該包括GROUP BY
部分中,而HAVING
用來決定應(yīng)該只用結(jié)果集合中的哪些行。
18.17 從關(guān)聯(lián)的表中刪除行
因為MySQL不支持子選擇或在DELETE
語句中使用多個表,你應(yīng)該使用下列方法從2個關(guān)聯(lián)的表中刪除行:
- 在主表中基于某個
WHERE
條件SELECT
行。
- 在主表中基于相同的條件
DELETE
行。
-
DELETE FROM related_table WHERE related_column IN (selected_rows)
如果在related_column
查詢中的字符的全部數(shù)量超過1,048,576(缺省值max_allowed_packet
),你應(yīng)該分成更小的部分并且執(zhí)行多個DELETE
語句。如果related_column
是一個索引,你每次只刪除100-1000個related_column
id將可能使得DELETE
最快。如果related_column
不是一個索引,速度與IN
子句中參數(shù)的數(shù)量無關(guān)。
18.18 解決沒有匹配行的問題
如果你有一個復(fù)雜的查詢,涉及多個表,但沒有返回任何行,你應(yīng)該使用下列過程查找你的詢問有什么不對:
-
EXPLAIN
測試查詢并且檢查你是否能找出顯然是錯誤的一些東西。見7.22 EXPLAIN
句法(得到關(guān)于一個SELECT
的信息)。
- 僅選擇那些在
WHERE
子句中使用的字段。
- 一次從查詢中刪除一個表,直到它返回一些行。如果表很大,對查詢使用
LIMIT 10
是一個好主意。
- 對應(yīng)該已經(jīng)匹配一行的列做一個
SELECT
,針對從詢問中做后被刪除的表。
- 如果你將
FLOAT
或DOUBLE
列與有小數(shù)的數(shù)字進行比較,你不能使用=
!。這個問題在大多數(shù)計算機語言是常見的,因為浮點值不是準確的值。mysql> SELECT * FROM table_name WHERE float_column=3.5;
->
mysql> SELECT * FROM table_name WHERE float_column between 3.45 and 3.55;
在大多數(shù)情況下,將FLOAT
改成一個DOUBLE
將修正它!
- 如果你仍然不能發(fā)現(xiàn)錯誤是什么,創(chuàng)建一個最小的可運行
mysql test < query.sql
的測試來顯示你的問題。你可以用mysqldump --quick database tables > query.sql
創(chuàng)建一個測試文件,在一個編輯器編輯文件,刪除一些插入行(如果有太多這些語句)并且在文件末尾加入你的選擇語句。測試你仍然有問題,可以這樣做:shell> mysqladmin create test2
shell> mysql test2 < query.sql
使用mysqlbug
的郵寄測試文件到mysql@lists.mysql.com。
18.19 與ALTER TABLE
有關(guān)的問題
如果ALTER TABLE
死于這樣一個錯誤:
Error on rename of './database/name.frm' to './database/B-a.frm' (Errcode: 17)
問題可能是MySQL在前一個ALTER TABLE
中已經(jīng)崩潰并且留下了一個名為“A-xxx”或“B-xxx”的老的數(shù)據(jù)庫表。在這種情況下,到MySQL數(shù)據(jù)目錄中并刪除所有名字以A-
或B-
開始的文件。(你可以把他們移到別的地方而不是刪除他們)。
ALTER TABLE
工作方式是:
- 以要求的改變創(chuàng)建一個名為“A-xxx”的新表。
- 從老表把所有行拷貝到“A-xxx”。
- 老表被改名為“B-xxx”。
-
“A-xxx”
被改名為你的老表的名字。
-
“B-xxx”
被刪除。
如果某些改名操作出錯,MySQL試圖還原改變。如果出錯嚴重(當(dāng)然,這不應(yīng)該發(fā)生。),MySQL可能留下了老表為“B-xxx”但是一個簡單改名就應(yīng)該恢復(fù)你的數(shù)據(jù)。
18.20 怎樣改變一張表中列的順序
SQL的要點是中抽象應(yīng)用程序以避免數(shù)據(jù)存儲格式。你應(yīng)該總是以你想要檢索數(shù)據(jù)的意愿指定順序。例如:
SELECT col_name1, col_name2, col_name3 FROM tbl_name;
將以col_name1
、col_name2
、col_name3
的順序返回列,而:
SELECT col_name1, col_name3, col_name2 FROM tbl_name;
將以col_name1
、col_name3
、col_name2
的順序返回列。
在一個應(yīng)用程序中,你應(yīng)該決不基于他們的位置使用SELECT *
檢索列,因為被返回的列的順序永遠不能保證;對你的數(shù)據(jù)庫的一個簡單改變可能導(dǎo)致你的應(yīng)用程序相當(dāng)有戲劇性地失敗。
不管怎樣,如果你想要改變列的順序,你可以這樣做:
- 以正確的列順序創(chuàng)建一張新表。
- 執(zhí)行
INSERT INTO new_table SELECT fields-in-new_table-order FROM old_table
.
- 刪除或改名
old_table
。
-
ALTER TABLE new_table RENAME old_table
。