Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2501282
  • 博文数量: 709
  • 博客积分: 12251
  • 博客等级: 上将
  • 技术积分: 7905
  • 用 户 组: 普通用户
  • 注册时间: 2005-07-17 00:00
个人简介

实现有价值的IT服务

文章存档

2012年(7)

2011年(147)

2009年(3)

2008年(5)

2007年(74)

2006年(431)

2005年(42)

分类: Mysql/postgreSQL

2006-06-29 14:40:58

問題和常見錯誤

所有MySQL在發布它們之前,在許多平台上被測試。這並不意味著在MySQL中沒有任何錯誤,但是如果有錯誤,它們是很少的並且很能難發現。如果你有一個問題並且如果你嘗試找出究竟是什麼摧毀了你的系統,它將總是有幫助的,因為你將有一個更好機會使得它被快速修複。

首先你應該試著找出問題mysqld守護進程是否死掉或你的問題是否與你的客戶有關。你可以用mysqladmin version檢查你的mysqld服務器正常執行了多長時間,如果mysqld死了,你可以在文件“mysql-data-directory/'hostname'.err”中找到其原因。

因為很難知道一些東西為什麼崩潰,首先試著檢查為其它人工作的東西是否使你崩潰。請嘗試下列事情:

  • mysqladmin shutdown停止mysqld守護進程,在所有表上運行myisamchk --silent --force */*.MYI並重啟mysqld守護經進程。這將保証你從一個幹淨的狀態運行。見。
  • 使用mysqld --log並且試著從日志信息確定是否是某個特定的查詢殺死了服務器。全部錯誤的95%與特定的查詢有關!通常它是在日志文件中就在MySQL重啟之前的最後查詢之一。你也許可以使用下列過程驗証它:
    • 停止MySQL守護進程(用mysqladmin shutdown)。
    • MySQL數據庫目錄下做文件的一個備份。
    • myisamchk -s */*.MYI檢查表以驗証所有表是正確的。如果有任何表被損壞,用myisamchk -r path-to-table.MYI修複它。
    • MySQL數據目錄刪除(或移走)任何舊的日志文件。
    • safe_mysql --log啟動服務器。
    • 如果mysqld現在死掉,你可以通過恢複備份並執行mysql < mysql-log-file來測試問題是否是一個特定的查詢引起的。當然你可以通過用safe_mysqld --data=path-to-backup-directory啟動另一個MySQL服務器,在某個其他目錄而不是標准MySQL數據庫目錄下做後面的測試。
  • 你試用了基准測試嗎?他們應該很好地測試MySQL。你也可以增加代碼模擬你的應用程序!基准程序可在源代碼分發的“bench”目錄下找到,或對二進制分發,在你的MySQL安裝目錄下“sql-bench”目錄。
  • 試一下fork_test.plfork2_test.pl
  • 對任何錯誤檢查文件“mysql-data-directory/'hostname'.err”
  • 如果你配置MySQL以便調試,如果出錯,收集可能的錯誤信息將更容易。使用--with-debug選項的configure重新配置MySQL然後重新編譯。見。
  • 為調試而配置MySQL使它包含一個安全的內存分配器以便能發現一些錯誤。它也提供關于正在發生什麼的大量輸出。
  • 你為你的操作系統使用了最新的補丁嗎?
  • 使用mysql--skip-locking選項.在一些系統上,lockd鎖管理器不能正確工作;--skip-locking選項告訴mysqld不使用外部鎖。(這意味著你不能在同一個數據上運行兩個mysqld服務器而且如果你使用myisamchk,你一定要小心,但是它對為了測試試用選項可能有益。)
  • mysqld好象正在運行但沒有反應時,你嘗試過mysqladmin -u root processlist嗎?有時mysqld不是毫無反應,盡管你可能這樣認為。問題可能是所有在用的連接,或可能用內部鎖定問題。mysqladmin processlist甚至在這些情況下將通常可以進行一個連接,並且能提供有關當前連接數量及其狀態的有用信息。
  • 在你運行其他查詢時,在一個單獨窗口中運行命令mysqladmin -i 5 status,產生統計。
  • 嘗試下列步驟:
    1. 通過gdb(或其他調試器)啟動mysqld
    2. 運行你的測試腳本。
    3. mysqld核心傾倒(core dump),做back(或你調試器中的回溯-backtrace命令)。
  • 嘗試用一個Perl腳本模擬你的應用程序以強制MySQL崩潰或表現不正確。
  • 或發一份正式的錯誤報告。見,但是要不平常更詳細。因為MySQL為很多人的工作,導致崩潰的東西可能只存在于你的計算機上(例如,與你的特定系統庫有關的一個錯誤)。
  • 如果你的問題有具有動態長度行的表有關,並且你不使用BLOB/TEXT列(但是僅VARCHAR列),你可以用ALTER TABLE試著將全部VARCHAR改為CHAR,這將強制MySQL使用固定尺寸的行。固定尺寸的行占據很小的額外空間,但是更能容忍崩潰!當前動態行的代碼在 TCX 至少使用3 年的時間,沒有任何問題,但是從本質上講,動態長度的行對錯誤更敏感,因此如果上述對你有幫助,嘗試一下可能是一個好主意!

本小節也涉及有關Lost connection to server during query的錯誤。

MySQL server has gone away錯誤最常見的原因是服務器超時了並且關閉了連接。缺省地,如果沒有事情發生,服務器在 8個小時後關閉連接。你可在啟動mysqld時通過設置wait_timeout變量改變時間限制。

你可以通過執行mysqladmin version並且檢驗正常運行的時間來檢查MySQL還沒死掉。

如果你有一個腳本,你只須再發出查詢讓客護進行一次自動的重新連接。

在這種請下,你通常能獲得下列錯誤代碼(你得到的是OS相關的):

CR_SERVER_GONE_ERROR 客戶不能發送一個問題給服務器。
CR_SERVER_LOST 當寫服務器時,客戶沒有出錯,但是它沒有得到對問題的一個完整的答案(或任何答案)。

如果你向服務器發送不正確的或太大的查詢,你也可能得到這些錯誤。如果mysqld得到一個太大或不正常的包,它認為客戶出錯了並關閉連接。如果你需要較大的查詢(例如,如果你正在處理較大的BLOB列),你可以使用-O max_allowed_packet=#選項(缺省1M)啟動mysqld以增加查詢限制。多余的內存按需分配,這樣mysqld只有在你發出較大差詢時或mysqld必須返回較大的結果行時,才使用更多的內存!

一個MySQL客戶可以兩種不同的方式連接mysqld服務器:Unix套接字,它通過在文件系統中的一個文件(缺省“/tmp/mysqld.sock”)進行連接;或TCP/IP,它通過一個端口號連接。Unix套接字比TCP/IP更快,但是只有用在連接同一台計算機上的服務器。如果你不指定主機名或如果你指定特殊的主機名localhost,使用Unix套接字。

錯誤(2002)Can't connect to ...通常意味著沒有一個MySQL服務器運行在系統上或當試圖連接mysqld服務器時,你正在使用一個錯誤的套接字文件或TCP/IP端口。

由檢查(使用ps)在你的服務器上有一個名為mysqld的進程啟動!如果沒有任何mysqld過程,你應該啟動一個。見。

如果一個mysqld過程正在運行,你可以通過嘗試這些不同的連接來檢查服務器(當然,端口號和套接字路徑名可能在你的安裝中是不同的):

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命令使用反引號“`”而非正引號“'”;這些導致hostname輸出(即,當前主機名)被代替進mysqladmin命令中。

這是可能造成Can't connect to local MySQL server錯誤的一些原因:

  • mysqld不在運行。
  • 你正在使用MIT-pthreads的一個系統上運行。如果正在運行在一個沒有原生線程的系統上,mysqld使用 MIT-pthreads 軟件包。見。然而,MIT-pthreads不支持Unix套接字,因此當與服務器連接時,在這樣一個系統上,你總是必須明確地指定主機名。試試使用這個命令檢查到服務器的連接:
    shell> mysqladmin -h `hostname` version
    
  • 某人刪除了mysqld使用的Unix套接字(缺省“/tmp/mysqld.sock”)。你可能有一個cron任務刪除了MySQL套接字(例如,一個把舊文件從“/tmp”目錄中刪除的任務)。你總是可以運行mysqladmin version並且檢查mysqladmin正在試圖使用的套接字確實存在。在這種情況下,修複方法是刪除cron任務而不刪除“mysqld.sock 或將套接字放在其他地方。你能用這個命令在MySQL配置時指定一個不同的套接字地點:
    shell> ./configure --with-unix-socket-path=/path/to/socket
    

    你也可以使用--socket=/path/to/socket選項啟動safe_mysqld和在啟動你的MySQL客戶前設置環境變量MYSQL_UNIX_PORT為套接字路徑名。你可用--socket=/path/to/socket選項啟動mysqld服務器。如果你改變了服務器的套接字路徑名,你也必須通知MySQL客戶關于新路徑的情況。你可以通過設置環境變量MYSQL_UNIX_PORT為套接字路徑名或由提供套接字路徑名作為客戶的參數做到。你可用這個命令測試套接字:

    shell> mysqladmin --socket=/path/to/socket version
    
  • 你正在使用 Linux和線程已經死了(核心傾倒了)。在這種情況中,你必須殺死其它mysqld線程(例如在啟動一個新的MySQL服務器之前,可以用mysql_zap腳本)。見。

如果你得到錯誤Can't connect to MySQL server on some_hostname,你可以嘗試下列步驟找出問題是什麼:

  • 通過執行telnet your-host-name tcp-ip-port-number並且按幾次回車來檢查服務器是否正常運行。如果有一個MySQL運行在這個端口上,你應該得到一個包含正在運行的MySQL服務器的版本號的應答。如果你得到類似于telnet: Unable to connect to remote host: Connection refused的一個錯誤,那麼沒有服務器在使用的端口上運行。
  • 嘗試連接本地機器上的mysqld守護進程,並用mysqladmin variables檢查mysqld被配置使用的TCP/IP端口(變量port)。
  • 檢查你的mysqld服務器沒有用--skip-networking選項啟動。

如果你得到象這樣的一個錯誤:

Host 'hostname' is blocked because of many connection errors.
Unblock with 'mysqladmin flush-hosts'

這意味著,mysqld已經得到了大量(max_connect_errors)的主機'hostname'的在中途被中斷了的連接請求。在max_connect_errors次失敗請求後,mysqld認定出錯了(象來字一個黑客的攻擊),並且阻止該站點進一步的連接,直到某人執行命令mysqladmin flush-hosts

缺省地,mysqld在10個連接錯誤後阻塞一台主機。你可以通過象這樣啟動服務器很容易地調整它:

shell> safe_mysqld -O max_connect_errors=10000 &

注意,對給定的主機,如果得到這條錯誤消息,你應該首先檢查該主機的TCP/IP連接有沒有問題。如果你的TCP/IP連接不在運行,增加max_connect_errors變量的值對你也不會有幫助!

如果在你試土連接MySQL時,你得到錯誤Too many connections,這意味著已經有max_connections個客戶連接了mysqld服務器。

如果你需要比缺省(100)更多的連接,那麼你應該重啟mysqld,用更大的 max_connections 變量值。

注意,mysqld實際上允許(max_connections+1)個客戶連接。最後一個連接是為一個用Process權限的用戶保留的。通過不把這個權限給一般用戶(他們不應該需要它),有這個權限一個管理員可以登錄並且使用SHOW PROCESSLIST找出什麼可能出錯。見。

如果你發出查詢並且得到類似于下面的錯誤:

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。這個錯誤的原因很簡單,客戶沒有足夠的內存存儲全部結果。

為了修正這個問題,首先檢查你的查詢是否正確。它應該返回這麼多的行,這合理嗎?如果是這樣,你可以使用mysql --quick,它使用mysql_use_result()檢索結果集合。這將較少的負擔放在了客戶端(只是服務器更多)。

當一個MySQL客戶或mysqld服務器得到一個比max_allowed_packet個字節長的包,它發出一個Packet too large錯誤並終止連接。

如果你正在使用mysql客戶,你可以通過用mysql --set-variable=max_allowed_packet=8M指定一個更大的緩衝區來啟動客戶程序。

如果你正在使用不允許你指定最大包大小的其他客戶(例如 DBI),你需要在你啟動服務器時設置包大小。你可以使用mysqld的命令行選項設置max_allowed_packet為一個更大的尺寸。例如,如果你正期望將一個全長的BLOB存入一張表中,你將需要用--set-variable=max_allowed_packet=24M選項來啟動服務器。

 

這個錯誤發生在內存臨時表變得比tmp_table_size字節大時。為了避免這個問題,你可以使用mysqld-O tmp_table_size=#選項來增加臨時表的大小,或在你發出有疑問的查詢之前使用SQL選項SQL_BIG_TABLES。見。

你也可以使用--big-tables選項啟動mysqld。這與為所有查詢使用SQL_BIG_TABLES完全相同。

如果你在你的客戶代碼中得到Commands out of sync; You can't run this command now,你正在以錯誤的次序調用客戶函數!

這可能發生,例如,如果你正在使用mysql_use_result()並且在你已經調用了mysql_free_result()之前試圖執行新查詢。如果你在mysql_use_result()mysql_store_result()之間試圖執行返回數據的2個查詢,它也可能發生。

如果你得到下列錯誤:

Found wrong password for user: 'some_user@some_host'; Ignoring user

這意味著在mysqld啟動時或在它再次裝載權限表時,它在user表中找到了一個有一個無效口令的條目。結果,條目簡單地被權限系統忽略。

可能導致這個問題的原因和修正:

  • 你可能正在運行一個有一個老的user表的新版本mysqld。你可以通過執行mysqlshow mysql user看看口令字段是否少于 16個字符來檢查它。如果是這樣,你可以通過運行scripts/add_long_password腳本改正這種情況。
  • 用戶有一個老式的口令(8個字符長)並且你沒使用--old-protocol選項啟動mysqld。用一個新口令更新在user表中的用戶或用--old-protocol重啟mysqld
  • 你沒有使用PASSWORD()函數在在user表中指定了一個口令。使用mysql以一個新口令更新在user表中的用戶。確保使用PASSWORD()函數:
    mysql> update user set password=PASSWORD('your password')
               where user='XXX';
    

如果你得到錯誤Table 'xxx' doesn't existCan't find file: 'xxx' (errno: 2),這意味著在當前數據庫中沒有名為xxx的表存在。

注意,因為MySQL使用目錄和文件存儲數據庫和表,數據庫和表名件是區分大小寫的!(在Win32上,數據庫和表名不是區分大小寫的,但是在查詢中對所有表的引用必須使用相同的大小寫!)

你可以用SHOW TABLES檢查你在當前數據庫中有哪個表。見。

當出現一個磁盤溢出的情況時,MySQL做下列事情:

  • 它每分鐘檢查一次看是否有足夠空間寫入當前行。如果有足夠的空間,它繼續好像發生什麼事情。
  • 每6分鐘它將有關磁盤溢出的警告寫入日志文件。

為了緩和這個問題,你可以採取下列行動:

  • 繼續,你只需釋放足夠的空閒磁盤空間以便插入所有記錄。
  • 放棄線程,你必須發一個mysqladmin kill到線程。在下一次檢查磁盤時,線程將被放棄(在1分鐘內)。
  • 注意,其他線程可能正在等待引起“磁盤溢出”條件的表。如果你有幾個“鎖定的”的線程,殺死正在等待磁盤溢出條件的那個線程將允許其他線程繼續。

一般地,mysql客戶被交互性地使用,象這樣:

shell> mysql database

然而,也可以把你的SQL命令放在一個文件中並且告訴mysql從該文件讀取其輸入。要想這樣做,創造一個文本文件“text_file”,它包含你想要執行的命令。然後如下那樣調用mysql

shell> mysql database < text_file

你也能啟動有一個USE db_name語句的文本文件。在這種情況下,在命令行上指定數據庫名是不必要的:

shell> mysql < text_file
見。 

MySQL使用TMPDIR環境變量的值作為存儲臨時文件的目錄的路徑名。如果你沒有設置TMPDIRMySQL使用系統缺省值,它通常是“/tmp”“/usr/tmp”。如果包含你的臨時文件目錄的文件系統太小,你應該編輯safe_mysqld設定TMPDIR指向你有足夠空間的一個文件系統!你也可以使用mysqld的--tmpdir選項目設置臨時目錄。

MySQL以“隱含文件”創建所有臨時文件。這保証了如果mysqld被終止,臨時文件也將被刪除。使用隱含文件的缺點是你將看不到一個大的臨時文件填滿了臨時文件目錄所在的文件系統。

當排序(ORDER BYGROUP BY)時,MySQL通常使用一個或兩個臨時文件。最大磁盤空間需求是:

(存儲東西的長度 + sizeof (數據庫指針))
* 匹配的行數
* 2

sizeof(數據庫指針)通常是4,但是在未來對確實很大的表可能增加。

對一些SELECT查詢,MySQL也創建臨時SQL表。這些沒被隱含且有“SQL_*”格式的名字。

ALTER TABLEOPTIMIZE TABLE在原數據庫表的同一個目錄中創建一張臨時表。

如果你有這個問題,事實上任何人可以刪除MySQL通訊套接字“/tmp/mysql.sock”,在Unix的大多數版本上,你能通過為其設置sticky(t)位來保護你的“/tmp”文件系統。作為root登錄並且做下列事情:

shell> chmod +t /tmp

這將保護你的“/tmp”文件系統使得文件僅能由他們的所有者或超級用戶(root)刪除。

你能執行ls -ld /tmp檢查sticky位是否被設置,如果最後一位許可位是t,該位被設置了。

見。並且特別要看。

MySQL服務器mysqld能被任何用戶啟動並運行。為了將mysqld改由Unix用戶user_name來運行,你必須做下列事情:

  1. 如果它正在運行,停止服務器(使用mysqladmin shutdown)。
  2. 改變數據庫目錄和文件以便user_name有權限讀和寫文件(你可能需要作為Unix的root用戶才能做到):
    shell> chown -R user_name /path/to/mysql/datadir
    

    如果在MySQL數據目錄中的目錄或文件是符號鏈接,你也將需要順著那些鏈接並改變他們指向的目錄和文件。chown -R不能跟隨符號鏈接。

  3. user_name用戶啟動服務器,或如果你正在使用MySQL 3.22或以後版本,以Unix root用戶啟動mysqld並使用--user=user_name選項,mysqld將在接受任何連接之前切換到以Unix user_name用戶運行。
  4. 如果在系統被重新啟動時,你使用mysql.server腳本啟動mysqld,你應該編輯mysql.serversu以用戶user_name運行mysqld,或使用--user選項調用mysqld。(不改變safe_mysqld是必要的。)

現在,你的mysqld進程應該正在作為Unix用戶user_name運行,並運行完好。盡管有一件事情沒有變化:權限表的內容。缺省 地(就在運行了腳本mysql_install_db安裝的權限表後),MySQL用戶root是唯一有存取mysql數據庫或創建或拋棄數據庫權限的用戶。除非你改變了那些權限,否則他們仍然保持。當你作為一個Unix用戶而不是root登錄時,這不應該阻止你作為MySQL root用戶來存取MySQL;只要為客戶程序指定-u root的選項。

注意通過在命令行上提供-u root,作為root存取MySQL,與作為Unix root用戶或其他Unix用戶運行MySQL沒有關系MySQL的存取權限和用戶名與Unix用戶名字是完全分開的。唯一與Unix用戶名有關的是,如果當你調用一個客戶程序時,你不提供一個-u選項,客戶將試圖使用你的Unix登錄名作為你的MySQL用戶名進行連接。

如果你的Unix機器本身不安全,你可能應該至少在存取表中為MySQL root用戶放上一個口令。否則,在那台機器上有一個帳號的任何用戶能運行mysql -u root db_name並且做他喜歡做的任何事情。

如果你忘記了MySQLroot用戶的口令,你可以用下列過程恢複它。

  1. 通過發送一個kill(不是kill -9)到mysqld服務器來關閉mysqld服務器。pid 被保存在一個.pid文件中,通常在MySQL數據庫目錄中:
    kill `cat /mysql-data-directory/hostname.pid`
    

    你必須是一個UNIX root用戶或運行服務器的相同用戶做這個。

  2. 使用--skip-grant-tables選項重啟mysqld
  3. mysql -h hostname mysql連接mysqld服務器並且用一條GRANT命令改變口令。見。你也可以用mysqladmin -h hostname -u user password 'new password' 進行。
  4. mysqladmin -h hostname flush-privileges或用SQL命令FLUSH PRIVILEGES來裝載權限表。

如果你有與文件許可有關的問題,例如,如果當你創建一張表時,mysql發出下列錯誤消息:

ERROR: Can't find file: 'path/with/filename.frm' (Errcode: 13) 

那麼可能是在mysqld啟動時,環境變量UMASK可能設置不正確。缺省的umask值是0660。你可以如下啟動safe_mysqld改變其行為:

shell> UMASK=384  # = 600 in octal
shell> export UMASK
shell> /path/to/safe_mysqld &

如果你從MySQL得到ERROR '...' not found (errno: 23), Can't open file: ... (errno: 24)或任何其他有errno 23errno 24的錯誤,它意味著,你沒有為MySQL分配足夠的文件描述符。你能使用perror實用程序得到錯誤號含義是什麼的描述:

shell> perror 23
File table overflow
shell> perror 24
Too many open files

這里的問題是mysqld正在試圖同時保持打開太多的文件。你也可以告訴mysqld一次不打開那麼多的文件,或增加mysqld可得到的文件描述符數量。

為了告訴mysqld一次保持打開更少的文件,你可以通過使用safe_mysqld-O table_cache=32選項(缺省值是64)使表緩衝更小。減小max_connections值也將減少打開文件的數量(缺省值是90)。

要想改變mysqld可用的文件描述符數量,修改safe_mysqld腳本。腳本中有一條注釋了的行ulimit -n 256。你可以刪除'#'字符來去掉該行的注釋,並且改變數字256改變為mysqld可用的文件描述符的數量。

ulimit能增加文件描述符的數量,但是只能到操作系統強加的限制。如果你需要增加每個進程可用的文件描述符數量的OS限制,參見你的操作系統文檔。

注意,如果你運行tcsh外殼,ulimit將不工作!當你請求當前限制時,tcsh也將報告不正確的值!在這種情況下,你應該用sh啟動safe_mysqld

一個DATE值的格式是'YYYY-MM-DD'。根據ANSI SQL,不允許其他格式。你應該在UPDATE表達式和SELECT語句的WHERE子句中使用這個格式。例如:

mysql> SELECT * FROM tbl_name WHERE date >= '1997-05-05';

為了方便,如果日期用在數字上下文,MySQL自動變換一個日期到一個數字(並且反過來也如此)。當更新時和將一個日期與TIMESTAMPDATEDATETIME列比較的一個WHERE子句中,也是足夠靈活以允許一種“寬松”的字符串格式。(寬松格式意味著任何標點字符用作在部件之間的分割符。例如,'1998-08-15''1998#08#15'是等價的。)MySQL也能變換不包含分割符的一個字符串(例如 '19980815'),如果它作為一個日期說得通。

特殊日期'0000-00-00'可以作為'0000-00-00'被存儲和檢索當通過MyODBC使用一個'0000-00-00'日期時,在MyODBC 2.50.12和以上版本,它將自動被轉換為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()是字符串函數,因此它將idate轉換為一個字符串並且實施字符串比較。它不將'19970505'轉換為一個日期並實施日期比較。

注意,MySQL不檢查日期是否正確。如果你存儲一個不正確的日期,例如'1998-2-31',錯誤的日期將被存儲。如果日期不能被變換到任何合理的值,在DATE字段中存儲一個0。這主要是一個速度問題並且我們認為檢查日期是應用程序的責任,而不服務器。

如果你有一個問題,SELECT NOW()以GMT時間返回值而不是你的本地時間,你必須設定TZ環境變量為你的當前時區。這應該在服務器運行的環境進行,例如在safe_mysqldmysql.server中。

缺省地,MySQL搜索是大小寫不敏感的(盡管有一些字符集從來不是忽略大小寫的,例如捷克語)。這意味著,如果你用col_name LIKE 'a%'搜尋,你將得到所有以Aa開始的列值。如果你想要使這個搜索大小寫敏感,使用象INDEX(col_name, "A")=0檢查一個前綴。或如果列值必須確切是"A",使用STRCMP(col_name, "A") = 0

簡單的比較操作(>=、>、= 、< 、<=、排序和聚合)是基于每個字符的“排序值”。有同樣排序值的字符(象E,e和'e)被視為相同的字符!

LIKE比較在每個字符的大寫值上進行(E==e 但是E<>'e)。

如果你想要一個列總是被當作大小寫敏感的方式,聲明它為BINARY。見。

如果你使用以所謂的big5編碼的中文數據,你要使所有的字符列是BINARY,它可行,是因為big5編碼字符的排序順序基于 ASCII代碼的順序。

NULL值的概念是造成SQL的新手的混淆的普遍原因,他們經常認為NULL是和一個空字符串''的一樣的東西。不是這樣的!例如,下列語句是完全不同的:

mysql> INSERT INTO my_table (phone) VALUES (NULL);
mysql> INSERT INTO my_table (phone) VALUES ("");

兩個語句把值插入到phone列,但是第一個插入一個NULL值而第二個插入一個空字符串。第一個的含義可以認為是“電話號碼不知道”,而第二個則可意味著“她沒有電話”。

在SQL中,NULL值在于任何其他值甚至NULL值比較時總是假的(FALSE)。包含NULL的一個表達式總是產生一個NULL值,除非在包含在表達式中的運算符和函數的文檔中指出。在下列例子,所有的列返回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服務器一樣,你不能索引可以有NULL值的列。你必須聲明這樣的列為NOT NULL,而且,你不能插入NULL到索引的列中。

當用LOAD DATA INFILE讀取數據時,空列用''更新。如果你想要在一個列中有NULL值,你應該在文本文件中使用\N。字面上的詞'NULL'也可以在某些情形下使用。見。

當使用ORDER BY時,首先呈現NULL值。如果你用DESC以降序排序,NULL值最後顯示。當使用GROUP BY時,所有的NULL值被認為是相等的。

為了有助于NULL的處理,你能使用IS NULLIS NOT NULL運算符和IFNULL()函數。

對某些列類型,NULL值被特殊地處理。如果你將NULL插入表的第一個TIMESTAMP列,則插入當前的日期和時間。如果你將NULL插入一個AUTO_INCREMENT列,則插入順序中的下一個數字。

 

你可以在GROUP BYORDER 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代碼被執行時,列值還可能沒有終結。例如下列查詢是不合法

SELECT id,COUNT(*) AS cnt FROM table_name WHERE cnt > 0 GROUP BY id;

WHERE語句被執行以確定哪些行應該包括GROUP BY部分中,而HAVING用來決定應該只用結果集合中的哪些行。

因為MySQL不支持子選擇或在DELETE語句中使用多個表,你應該使用下列方法從2個關聯的表中刪除行:

  1. 在主表中基于某個WHERE條件SELECT行。
  2. 在主表中基于相同的條件DELETE行。
  3. DELETE FROM related_table WHERE related_column IN (selected_rows)

如果在related_column查詢中的字符的全部數量超過1,048,576(缺省值max_allowed_packet),你應該分成更小的部分並且執行多個DELETE語句。如果related_column是一個索引,你每次只刪除100-1000個related_column id將可能使得DELETE最快。如果related_column不是一個索引,速度與IN子句中參數的數量無關。

如果你有一個複雜的查詢,涉及多個表,但沒有返回任何行,你應該使用下列過程查找你的詢問有什麼不對:

  1. EXPLAIN測試查詢並且檢查你是否能找出顯然是錯誤的一些東西。見。
  2. 僅選擇那些在WHERE子句中使用的字段。
  3. 一次從查詢中刪除一個表,直到它返回一些行。如果表很大,對查詢使用LIMIT 10是一個好主意。
  4. 對應該已經匹配一行的列做一個SELECT,針對從詢問中做後被刪除的表。
  5. 如果你將FLOATDOUBLE列與有小數的數字進行比較,你不能使用=!。這個問題在大多數計算機語言是常見的,因為浮點值不是准確的值。
    mysql> SELECT * FROM table_name WHERE float_column=3.5;
       ->
    mysql> SELECT * FROM table_name WHERE float_column between 3.45 and 3.55;
    

    在大多數情況下,將FLOAT改成一個DOUBLE將修正它!

  6. 如果你仍然不能發現錯誤是什麼,創建一個最小的可運行mysql test < query.sql的測試來顯示你的問題。你可以用mysqldump --quick database tables > query.sql創建一個測試文件,在一個編輯器編輯文件,刪除一些插入行(如果有太多這些語句)並且在文件末尾加入你的選擇語句。測試你仍然有問題,可以這樣做:
    shell> mysqladmin create test2
    shell> mysql test2 < query.sql
    

    使用mysqlbug的郵寄測試文件到。

如果ALTER TABLE死于這樣一個錯誤:

Error on rename of './database/name.frm' to './database/B-a.frm' (Errcode: 17)

問題可能是MySQL在前一個ALTER TABLE中已經崩潰並且留下了一個名為“A-xxx”“B-xxx”的老的數據庫表。在這種情況下,到MySQL數據目錄中並刪除所有名字以A-B-開始的文件。(你可以把他們移到別的地方而不是刪除他們)。

ALTER TABLE工作方式是:

  • 以要求的改變創建一個名為“A-xxx”的新表。
  • 從老表把所有行拷貝到“A-xxx”
  • 老表被改名為“B-xxx”
  • “A-xxx”被改名為你的老表的名字。
  • “B-xxx”被刪除。

如果某些改名操作出錯,MySQL試圖還原改變。如果出錯嚴重(當然,這不應該發生。),MySQL可能留下了老表為“B-xxx”但是一個簡單改名就應該恢複你的數據。

SQL的要點是中抽象應用程序以避免數據存儲格式。你應該總是以你想要檢索數據的意願指定順序。例如:

SELECT col_name1, col_name2, col_name3 FROM tbl_name;

將以col_name1col_name2col_name3的順序返回列,而:

SELECT col_name1, col_name3, col_name2 FROM tbl_name; 

將以col_name1col_name3col_name2的順序返回列。

在一個應用程序中,你應該決不基于他們的位置使用SELECT * 檢索列,因為被返回的列的順序永遠不能保証;對你的數據庫的一個簡單改變可能導致你的應用程序相當有戲劇性地失敗。

不管怎樣,如果你想要改變列的順序,你可以這樣做:

  1. 以正確的列順序創建一張新表。
  2. 執行INSERT INTO new_table SELECT fields-in-new_table-order FROM old_table.
  3. 刪除或改名old_table
  4. ALTER TABLE new_table RENAME old_table
阅读(1663) | 评论(0) | 转发(0) |
0

上一篇:維護MySQL

下一篇:mySql 第三人软件

给主人留下些什么吧!~~