### 第3章:教程
**目錄**
[ 3.1. 連接與斷開服務器](#)[ 3.2. 輸入查詢](#)[ 3.3. 創建并使用數據庫](#)[ 3.3.1. 創建并選擇數據庫](#)[ 3.3.2. 創建表](#)[ 3.3.3. 將數據裝入表中](#)[ 3.3.4. 從表檢索信息](#)[ 3.4. 獲得數據庫和表的信息](#)[ 3.5. 在批處理模式**下使用my**sql](#)[ 3.6. 常用查詢的例子](#)[ 3.6.1. 列的最大值](#)[ 3.6.2. 擁有某個列的最大值的行](#)[ 3.6.3. 列的最大值:按組](#)[ 3.6.4. 擁有某個字段的組間最大值的行](#)[ 3.6.5. 使用用戶變量](#)[ 3.6.6. 使用外鍵](#)[ 3.6.7. 根據兩個鍵搜索](#)[ 3.6.8. 根據天計算訪問量](#)[ 3.6.9. 使用AUTO_INCREMENT](#)[ 3.7. 孿生項目的查詢](#)[ 3.7.1. 查找所有未分發的孿生項](#)[ 3.7.2. 顯示孿生對狀態的表](#)[ 3.8. 與Apache一起使用MySQL](#)
本章通過演示如何使用**mysql**客戶程序創造和使用一個簡單的數據庫,提供一個MySQL的入門教程。**mysql**(有時稱為“終端監視器”或只是“監視”)是一個交互式程序,允許你連接一個MySQL服務器,運行查詢并察看結果。**mysql**可以用于批模式:你預先把查詢放在一個文件中,然后告訴**mysql**執行文件的內容。本章將介紹使用**mysql**的兩個方法。
要想查看由**mysql**提供的選擇項目表,可以用--help選項來調用:
~~~
shell> mysql --help
~~~
本章假定**mysql**已經被安裝在你的機器上,并且有一個MySQL服務器可以連接。否則,請聯絡MySQ**L**管理員。(如果*你*是管理員,則需要查閱本手冊的其它章節,例如[第5章:](#)[*數據庫管理*](# "Chapter?5.?Database Administration")。)
本章描述建立和使用一個數據庫的全過程。如果你僅僅對訪問一個已經存在的數據庫感興趣,可以跳過描述怎樣創建數據庫及它所包含的表的章節。
由于本章是一個教程,省略了許多細節。關于這里所涉及的主題的詳細信息,請查閱本手冊的相關章節。
### 3.1.?連接與斷開服務器
為了連接服務器,當調用**mysql**時,通常需要提供一個MySQL用戶名并且很可能需要一個 密碼。如果服務器運行在登錄服務器之外的其它機器上,還需要指定主機名。聯系管理員以找出進行連接所使用的參數 (即,連接的主機、用戶名和使用的密碼)。知道正確的參數后,可以按照以下方式進行連接:
~~~
shell> mysql -h host -u user -p
~~~
~~~
Enter password: ********
~~~
host和user分別代表MySQL服務器運行的主機名和MySQL賬戶用戶名。設置時替換為正確的值。******** 代表你的密碼;當**mysql**顯示Enter password:提示時輸入它。
如果有效,你應該看見mysql>提示符后的一些介紹信息:
~~~
shell> mysql -h host -u user -p
~~~
~~~
Enter password: ********
~~~
~~~
Welcome to the MySQL monitor.? Commands end with ; or \g.
~~~
~~~
Your MySQL connection id is 25338 to server version: 5.1.2-alpha-standard
~~~
~~~
?
~~~
~~~
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
~~~
~~~
?
~~~
~~~
mysql>
~~~
mysql> 提示符告訴你mysql準備為你輸入命令。
一些**MySQL**安裝允許用戶以匿名(未命名)用戶連接到本地主機上運行的服務器。如果你的機器是這種情況,你應該能不帶任何選項地調用**mysql**與該服務器連接:
~~~
shell> mysql
~~~
成功地連接后,可以在mysql>提示下輸入QUIT(或\q)隨時退出:
~~~
mysql> QUIT
~~~
~~~
Bye
~~~
在Unix中,也可以按control-D鍵斷開服務器。
在下列章節的大多數例子都假設你連接到了服務器。由mysql>提示指明。
### 3.2.?輸入查詢
確保你連接上了服務器,如在先前的章節討論的。連接上服務器并布代表選擇了任何數據庫,但這樣就可以了。知道關于如何查詢的基本知識,比馬上跳至創建表、給他們裝載數據并且從他們檢索數據更重要。本節描述輸入命令的基本原則,使用幾個查詢,你能嘗試了解**mysql**是如何工作的。
這是一個簡單的命令,要求服務器告訴它的版本號和當前日期。在mysql>提示輸入如下命令并按回車鍵:
~~~
mysql> SELECT VERSION(), CURRENT_DATE;
~~~
~~~
+-----------------+--------------+
~~~
~~~
| VERSION()?????? | CURRENT_DATE |
~~~
~~~
+-----------------+--------------+
~~~
~~~
| 5.1.2-alpha-log | 2005-10-11?? |
~~~
~~~
+-----------------+--------------+
~~~
~~~
1 row in set (0.01 sec)
~~~
~~~
mysql>
~~~
這詢問說明**mysql**的幾個方面:
·???????? 一個命令通常由SQL語句組成,隨后跟著一個分號。(有一些例外不需要分號。早先提到的QUIT是一個例子。后面我們將看到其它的例子。)
·???????? 當發出一個命令時,**mysql**將它發送給服務器并顯示執行結果,然后顯示另一個mysql>顯示它準備好接受其它命令。
·???????? **mysql**用表格(行和列)方式顯示查詢輸出。第一行包含列的標簽,隨后的行是查詢結果。通常,列標簽是你取自數據庫表的列的名字。如果你正在檢索一個表達式而非表列的值(如剛才的例子),**mysql**用表達式本身標記列。
·???????? **mysql**顯示返回了多少行,以及查詢花了多長時間,它給你提供服務器性能的一個大致概念。因為他們表示時鐘時間(不是 CPU 或機器時間),并且因為他們受到諸如服務器負載和網絡延時的影響,因此這些值是不精確的。(為了簡潔,在本章其它例子中不再顯示“集合中的行”。)
能夠以大小寫輸入關鍵詞。下列查詢是等價的:
~~~
mysql> SELECT VERSION(), CURRENT_DATE;
~~~
~~~
mysql> select version(), current_date;
~~~
~~~
mysql> SeLeCt vErSiOn(), current_DATE;
~~~
這是另外一個查詢,它說明你能將**mysql**用作一個簡單的計算器:
~~~
mysql> SELECT SIN(PI()/4), (4+1)*5;
~~~
~~~
+------------------+---------+
~~~
~~~
| SIN(PI()/4)????? | (4+1)*5 |
~~~
~~~
+------------------+---------+
~~~
~~~
| 0.70710678118655 |????? 25 |
~~~
~~~
+------------------+---------+
~~~
~~~
1 row in set (0.02 sec)
~~~
至此顯示的命令是相當短的單行語句。你可以在一行上輸入多條語句,只需要以一個分號間隔開各語句:
~~~
mysql> SELECT VERSION(); SELECT NOW();
~~~
~~~
+-----------------+
~~~
~~~
| VERSION()?????? |
~~~
~~~
+-----------------+
~~~
~~~
| 5.1.2-alpha-log |
~~~
~~~
+-----------------+
~~~
~~~
1 row in set (0.00 sec)
~~~
~~~
?
~~~
~~~
+---------------------+
~~~
~~~
| NOW()?????????????? |
~~~
~~~
+---------------------+
~~~
~~~
| 2005-10-11 15:15:00 |
~~~
~~~
+---------------------+
~~~
~~~
1 row in set (0.00 sec)
~~~
不必全在一個行內給出一個命令,較長命令可以輸入到多個行中。**mysql**通過尋找終止分號而不是輸入行的結束來決定語句在哪兒結束。(換句話說,**mysql**接受自由格式的輸入:它收集輸入行但直到看見分號才執行。)
這里是一個簡單的多行語句的例子:
~~~
mysql> SELECT
~~~
~~~
??? -> USER()
~~~
~~~
??? -> ,
~~~
~~~
??? -> CURRENT_DATE;
~~~
~~~
+---------------+--------------+
~~~
~~~
| USER()??????? | CURRENT_DATE |
~~~
~~~
+---------------+--------------+
~~~
~~~
| jon@localhost | 2005-10-11?? |
~~~
~~~
+---------------+--------------+
~~~
在這個例子中,在輸入多行查詢的第一行后,要注意提示符如何從mysql>變為->,這正是**mysql**如何指出它沒見到完整的語句并且正在等待剩余的部分。提示符是你的朋友,因為它提供有價值的反饋,如果使用該反饋,將總是知道**mysql**正在等待什么。
如果你決定不想執行正在輸入過程中的一個命令,輸入\c取消它:
~~~
mysql> SELECT
~~~
~~~
??? -> USER()
~~~
~~~
??? -> \c
~~~
~~~
mysql>
~~~
這里也要注意提示符,在你輸入\c以后,它切換回到mysql>,提供反饋以表明**mysql**準備接受一個新命令。
下表顯示出可以看見的各個提示符并簡述它們所表示的**mysql**的狀態:
<table border="1" cellpadding="0" id="table1"><tr><td> <p><strong><span>提示符</span></strong></p></td> <td width="490"> <p><strong><span>含義</span></strong></p></td> </tr><tr><td> <p> <span>mysql></span></p></td> <td width="490"> <p>準備好接受新命令。</p></td> </tr><tr><td> <p> <span>-></span></p></td> <td width="490"> <p>等待多行命令的下一行。</p></td> </tr><tr><td> <p> <span>'></span></p></td> <td width="490"> <p>等待下一行,等待以單引號<span>(</span>“<span>'</span>”<span>)</span>開始的字符串的結束。</p></td> </tr><tr><td> <p> <span>"></span></p></td> <td width="490"> <p>等待下一行,等待以雙引號<span>(</span>“<span>"</span>”<span>)</span>開始的字符串的結束。</p></td> </tr><tr><td> <p> <span>`></span></p></td> <td width="490"> <p>等待下一行,等待以反斜點<span>(</span>‘<span>`</span>’<span>)</span>開始的識別符的結束。</p></td> </tr><tr><td> <p> <span>/*></span></p></td> <td width="490"> <p>等待下一行,等待以<span>/*</span>開始的注釋的結束。</p></td> </tr></table>
當你打算在一個單行上發出一個命令時,通常會“偶然”出現多行語句,但是沒有終止分號。在這種情況中,**mysql**等待進一步輸入:
~~~
mysql> SELECT USER()
~~~
~~~
??? ->
~~~
如果出現這種情況(你認為輸完了語句,但是只有一個->提示符響應),很可能**mysql**正在等待分號。如果你沒有注意到提示符的提示,在意識到你需要做什么之前,你可能會呆坐一會兒。輸入一個分號完成語句,**mysql**將執行:
~~~
mysql> SELECT USER()
~~~
~~~
??? -> ;
~~~
~~~
+---------------+
~~~
~~~
| USER()??????? |
~~~
~~~
+---------------+
~~~
~~~
| jon@localhost |
~~~
~~~
+---------------+
~~~
在字符串收集期間將出現 '> 和 "> 提示符(提示MySQL正等待字符串的結束)。在MySQL中,可以寫由‘'’或‘"’字符括起來的字符串 (例如,'hello'或"goodbye"),并且**mysql**允許輸入跨越多行的字符串。當看到一個 '> 或 "> 提示符時,這意味著已經輸入了包含以‘'’或‘"’括號字符開始的字符串的一行,但是還沒有輸入終止字符串的匹配引號。這顯示你粗心地省掉了一個引號字符。例如:
~~~
mysql> SELECT * FROM my_table WHERE name = 'Smith AND age < 30;
~~~
~~~
??? '>
~~~
如果你輸入SELECT語句,然后按**Enter(****回車)**鍵并等待結果,什么都沒有出現。不要驚訝,“為什么該查詢這么長呢?”,注意">提示符提供的線索。它告訴你**mysql**期望見到一個未終止字符串的余下部分。(你看見語句中的錯誤嗎?字符串"Smith丟掉了第二個引號。)
走到這一步,你該做什么?最簡單的是取消命令。然而,在這種情況下,你不能只是輸入\c,因為**mysql**作為它正在收集的字符串的一部分來解釋它!相反,應輸入關閉的引號字符(這樣**mysql**知道你完成了字符串),然后輸入\c:
~~~
mysql> SELECT * FROM my_table WHERE name = 'Smith AND age < 30;
~~~
~~~
??? '> '\c
~~~
~~~
mysql>
~~~
提示符回到mysql>,顯示**mysql**準備好接受一個新命令了。
`>提示符類似于 '>和">提示符,但表示你已經開始但沒有結束以`> 開始的識別符。
知道'>和">提示符的含義很重要,因為如果你錯誤地輸入一個未終止的字符串,任何后面輸入的行將要被**mysql**忽略--包括包含QUIT的行!這可能令人相當困惑,特別是如果取消當前命令前還不知道你需要提供終止引號。
### 3.3.?創建并使用數據庫
[ 3.3.1. 創建并選擇數據庫](#)[ 3.3.2. 創建表](#)[ 3.3.3. 將數據裝入表中](#)[ 3.3.4. 從表檢索信息](#)
知道怎樣輸入命令,便可以訪問數據庫了。
假定在你的家(你的“動物園”)中有很多寵物,并且你想跟蹤關于它們各種類型的信息。你可以通過創建表來保存你的數據并根據所需要的信息裝載他們,然后你可以從表中檢索數據來回答關于動物不同種類的問題。本節顯示如何做到所有這些事情:
·???????? 創建數據庫
·???????? 創建數據庫表
·???????? 裝載數據到數據庫表
·???????? 以各種方法從表中檢索數據
·???????? 使用多個表
動物園數據庫很簡單(特意的),但是不難把它想象成可能用到類似數據庫的真實世界情況。例如,農夫可以使用這樣的一個數據庫來追蹤家畜,或者獸醫可以用它跟蹤病畜記錄。從MySQL網址上可以獲得后面章節中將用到的含有部分查詢和樣例數據的動物園分發。有**tar壓縮格式** ([http://www.mysql.com/Downloads/Contrib/Examples/menagerie.tar.gz](http://www.mysql.com/Downloads/Contrib/Examples/menagerie.tar.gz))和Zip壓縮格式 ([http://www.mysql.com/Downloads/Contrib/Examples/menagerie.zip](http://www.mysql.com/Downloads/Contrib/Examples/menagerie.zip))。
使用SHOW語句找出服務器上當前存在什么數據庫:
~~~
mysql> SHOW DATABASES;
~~~
~~~
+----------+
~~~
~~~
| Database |
~~~
~~~
+----------+
~~~
~~~
| mysql??? |
~~~
~~~
| test???? |
~~~
~~~
| tmp????? |
~~~
~~~
+----------+
~~~
可能你的機器上的數據庫列表是不同的,但是很可能有mysql和test數據庫。mysql是必需的,因為它描述用戶訪問權限,test數據庫經常作為用戶試身手的工作區。
請注意如果沒有SHOW DATABASES權限,則不能看見所有數據庫。參見[13.5.1.3節,“GRANT和REVOKE語法”](# "13.5.1.3.?GRANT and REVOKE Syntax")。
如果test數據庫存在,嘗試訪問它:
~~~
mysql> USE test
~~~
~~~
Database changed
~~~
注意,USE,類似QUIT,不需要一個分號。(如果你喜歡,你可以用一個分號終止這樣的語句;這無礙)USE語句在使用上也有另外一個特殊的地方:它必須在一個單行上給出。
你可列在后面的例子中使用test數據庫(如果你能訪問它),但是你在該數據庫創建的任何東西可以被訪問它的其它人刪除,因此,你應該詢問MySQL管理員許可你使用自己的一個數據庫。假定你想要調用你的menagerie,管理員需要執行這樣一條命令:
~~~
mysql> GRANT ALL ON menagerie.* TO 'your_mysql_name'@'your_client_host';
~~~
這里your_mysql_name是分配給你的**MySQL**用戶名,your_client_host是所連接的服務器所在的主機。
### 3.3.1.?創建并選擇數據庫
如果管理員在設置權限時為你創建了數據庫,你可以開始使用它。否則,你需要自己創建數據庫:
~~~
mysql> CREATE DATABASE menagerie;
~~~
在Unix下,數據庫名稱是區分大小寫的(不像SQL關鍵字),因此你必須總是以menagerie訪問數據庫,而不能用Menagerie、MENAGERIE或其它一些變量。對表名也是這樣的。(在Windows下,該限制不適用,盡管你必須在一個給定的查詢中使用同樣的大小寫來引用數據庫和表。但是,由于多種原因,作為最好的慣例,一定要使用與數據庫創建時的同樣的大小寫。)
創建數據庫并不表示選定并使用它,你必須明確地操作。為了使menagerie成為當前的數據庫,使用這個命令:
~~~
mysql> USE menagerie
~~~
~~~
Database changed
~~~
數據庫只需要創建一次,但是必須在每次啟動**mysql**會話時在使用前先選擇它。你可以根據上面的例子執行一個USE語句來實現。還可以在調用**mysql**時,通過命令行選擇數據庫,只需要在提供連接參數之后指定數據庫名稱。例如:
~~~
shell> mysql -h host -u user -p menagerie
~~~
~~~
Enter password: ********
~~~
注意,剛才顯示的命令行中的menagerie**不**是你的 密碼。如果你想要在命令行上在-p選項后提供 密碼,則不能插入空格(例如,如-pmypassword,不是-p mypassword)。但是,不建議在命令行輸入密碼,因為這樣會暴露 密碼,能被在機器上登錄的其它用戶窺探到。
### 3.3.2.?創建表
創建數據庫是很容易的部分,但是在這時它是空的,正如SHOW TABLES將告訴你的:
~~~
mysql> SHOW TABLES;
~~~
~~~
Empty set (0.00 sec)
~~~
較難的部分是決定你的數據庫結構應該是什么:你需要什么數據庫表,各數據庫表中有什么樣的列。
你將需要一個包含你每個寵物的記錄的表。它可稱為pet表,并且它應該包含,最少,每個動物的名字。因為名字本身不是很有趣,表應該包含另外的信息。例如,如果在你豢養寵物的家庭有超過一個人,你可能想要列出每個動物的主人。你可能也想要記錄例如種類和性別的一些基本的描述信息。
年齡呢?那可能有趣,但是存儲到一個數據庫中不是一件好事情。年齡隨著時間流逝而變化,這意味著你將要不斷地更新你的記錄。相反, 存儲一個固定值例如生日比較好,那么,無論何時你需要年齡,可以以當前日期和出生日期之間的差來計算它。**MySQL**提供了日期運算函數,因此這并不困難。存儲出生日期而非年齡還有其它優點:
·???????? 你可以使用數據庫完成這樣的任務,例如生成即將到來的寵物生日的提示。(如果你認為這類查詢有點蠢,注意,這與從商務數據庫來識別出不久要發給生日祝賀的客戶是同一個問題,因為計算機幫助私人聯絡。)
·???????? 你可以相對于日期而不止是當前日期來計算年齡。例如,如果你在數據庫存儲死亡日期,你能很容易地計算出一只寵物死時有多大。
你可能想到pet表中其它有用的其它類型信息,但是到目前為止這些已經足夠了:名字、主人、種類,性別、出生和死亡日期。
使用一個CREATE TABLE語句指定你的數據庫表的布局:
~~~
mysql> CREATE TABLE pet (name VARCHAR(20), owner VARCHAR(20),
~~~
~~~
??? -> species VARCHAR(20), sex CHAR(1), birth DATE, death DATE);
~~~
VARCHAR適合于name、owner和species列,因為列值是變長的。這些列的長度不必都相同,而且不必是20。你可以挑選從1到65535的任何長度,從中選擇一個最合理的值。(如果選擇得不合適,后來證明你需要一個更長的字段,**MySQL**提供一個ALTER TABLE語句。)
可以用多種類型的值來表示動物記錄中的性別,例如,"m"和"f",或"male"和"female"。使用單字符"m"和"f"是最簡單的方法。
很顯然,birth和death列應選用DATE數據類。
創建了數據庫表后,SHOW TABLES應該產生一些輸出:
~~~
mysql> SHOW TABLES;
~~~
~~~
+---------------------+
~~~
~~~
| Tables in menagerie |
~~~
~~~
+---------------------+
~~~
~~~
| pet???????????????? |
~~~
~~~
+---------------------+
~~~
為了驗證你的表是按你期望的方式創建,使用一個DESCRIBE語句:
~~~
mysql> DESCRIBE pet;
~~~
~~~
+---------+-------------+------+-----+---------+-------+
~~~
~~~
| Field?? | Type??????? | Null | Key | Default | Extra |
~~~
~~~
+---------+-------------+------+-----+---------+-------+
~~~
~~~
| name??? | varchar(20) | YES? |???? | NULL??? |?????? |
~~~
~~~
| owner?? | varchar(20) | YES? |???? | NULL??? |?????? |
~~~
~~~
| species | varchar(20) | YES? |???? | NULL??? |?????? |
~~~
~~~
| sex???? | char(1)???? | YES? |???? | NULL??? |?????? |
~~~
~~~
| birth?? | date??????? | YES? |? ???| NULL??? |?????? |
~~~
~~~
| death?? | date??????? | YES? |???? | NULL??? |?????? |
~~~
~~~
+---------+-------------+------+-----+---------+-------+
~~~
你可以隨時使用DESCRIBE,例如,如果你忘記表中的列的名稱或類型時。
### 3.3.3.?將數據裝入表中
創建表后,需要填入內容。通過LOAD DATA和INSERT語句可以完成該任務。
假定你的寵物紀錄描述如下。(假定在**MySQL**中期望的日期格式是YYYY-MM-DD;這可能與你習慣的不同。)
<table border="1" cellpadding="0" id="table2"><tr><td> <p><strong> <span>name</span></strong></p></td> <td> <p><strong> <span>owner</span></strong></p></td> <td> <p><strong> <span>species</span></strong></p></td> <td> <p><strong> <span>sex</span></strong></p></td> <td> <p><strong> <span>birth</span></strong></p></td> <td> <p><strong> <span>death</span></strong></p></td> </tr><tr><td> <p><span>Fluffy</span></p></td> <td> <p><span>Harold</span></p></td> <td> <p><span>cat</span></p></td> <td> <p><span>f</span></p></td> <td> <p><span>1993-02-04</span></p></td> <td> <p><span>?</span></p></td> </tr><tr><td> <p><span>Claws</span></p></td> <td> <p><span>Gwen</span></p></td> <td> <p><span>cat</span></p></td> <td> <p><span>m</span></p></td> <td> <p><span>1994-03-17</span></p></td> <td> <p><span>?</span></p></td> </tr><tr><td> <p><span>Buffy</span></p></td> <td> <p><span>Harold</span></p></td> <td> <p><span>dog</span></p></td> <td> <p><span>f</span></p></td> <td> <p><span>1989-05-13</span></p></td> <td> <p><span>?</span></p></td> </tr><tr><td> <p><span>Fang</span></p></td> <td> <p><span>Benny</span></p></td> <td> <p><span>dog</span></p></td> <td> <p><span>m</span></p></td> <td> <p><span>1990-08-27</span></p></td> <td> <p><span>?</span></p></td> </tr><tr><td> <p><span>Bowser</span></p></td> <td> <p><span>Diane</span></p></td> <td> <p><span>dog</span></p></td> <td> <p><span>m</span></p></td> <td> <p><span>1979-08-31</span></p></td> <td> <p><span>1995-07-29</span></p></td> </tr><tr><td> <p><span>Chirpy</span></p></td> <td> <p><span>Gwen</span></p></td> <td> <p><span>bird</span></p></td> <td> <p><span>f</span></p></td> <td> <p><span>1998-09-11</span></p></td> <td> <p><span>?</span></p></td> </tr><tr><td> <p><span>Whistler</span></p></td> <td> <p><span>Gwen</span></p></td> <td> <p><span>bird</span></p></td> <td> <p><span>?</span></p></td> <td> <p><span>1997-12-09</span></p></td> <td> <p><span>?</span></p></td> </tr><tr><td> <p><span>Slim</span></p></td> <td> <p><span>Benny</span></p></td> <td> <p><span>snake</span></p></td> <td> <p><span>m</span></p></td> <td> <p><span>1996-04-29</span></p></td> <td> <p><span>?</span></p></td> </tr></table>
因為你是從一個空表開始的,填充它的一個簡易方法是創建一個文本文件,每個動物各一行,然后用一個語句將文件的內容裝載到表中。
你可以創建一個文本文件“pet.txt”,每行包含一個記錄,用定位符(tab)把值分開,并且以CREATE TABLE語句中列出的列次序給出。對于丟失的值(例如未知的性別,或仍然活著的動物的死亡日期),你可以使用NULL值。為了在你的文本文件中表示這些內容,使用\N(反斜線,字母N)。例如,Whistler鳥的記錄應為(這里值之間的空白是一個定位符):
<table border="1" cellpadding="0" id="table3"><tr><td> <p><strong> <span>name</span></strong></p></td> <td> <p><strong> <span>owner</span></strong></p></td> <td> <p><strong> <span>species</span></strong></p></td> <td> <p><strong> <span>sex</span></strong></p></td> <td> <p><strong> <span>birth</span></strong></p></td> <td> <p><strong> <span>death</span></strong></p></td> </tr><tr><td> <p> <span>Whistler</span></p></td> <td> <p> <span>Gwen</span></p></td> <td> <p> <span>bird</span></p></td> <td> <p> <span>\N</span></p></td> <td> <p> <span>1997-12-09</span></p></td> <td> <p> <span>\N</span></p></td> </tr></table>
要想將文本文件“pet.txt”裝載到pet表中,使用這個命令:
~~~
mysql> LOAD DATA LOCAL INFILE '/path/pet.txt' INTO TABLE pet;
~~~
請注意如果用Windows中的編輯器(使用\r\n做為行的結束符)創建文件,應使用:
~~~
mysql> LOAD DATA LOCAL INFILE '/path/pet.txt' INTO TABLE pet
~~~
~~~
??? -> LINES TERMINATED BY '\r\n';
~~~
(在運行OS X的Apple機上,應使用行結束符'\r'。)
如果你愿意,你能明確地在LOAD DATA語句中指出列值的分隔符和行尾標記,但是默認標記是定位符和換行符。這對讀入文件“pet.txt”的語句已經足夠。
如果該語句失敗,可能是你安裝的MySQL不與使用默認值的本地文件兼容。關于如何更改請參見[5.6.4節,“LOAD DATA LOCAL安全問題``”](# "5.6.4.?Security Issues with LOAD DATA LOCAL")。
如果想要一次增加一個新記錄,可以使用INSERT語句。最簡單的形式是,提供每一列的值,其順序與CREATE TABLE語句中列的順序相同。假定Diane把一只新倉鼠命名為Puffball,你可以使用下面的INSERT語句添加一條新記錄:
~~~
mysql> INSERT INTO pet
~~~
~~~
??? -> VALUES ('Puffball','Diane','hamster','f','1999-03-30',NULL);
~~~
注意,這里字符串和日期值均為引號擴起來的字符串。另外,可以直接用INSERT語句插入NULL代表不存在的值。不能使用LOAD DATA中所示的的\N。
從這個例子,你應該能看到涉及很多的鍵入用多個INSERT語句而非單個LOAD DATA語句裝載你的初始記錄。
### 3.3.4.?從表檢索信息
[ 3.3.4.1. 選擇所有數據](#)[ 3.3.4.2. 選擇特殊行](#)[ 3.3.4.3. 選擇特殊列](#)[ 3.3.4.4. 分類行](#)[ 3.3.4.5. 日期計算](#)[ 3.3.4.6. NULL值操作``](#)[ 3.3.4.7. 模式匹配](#)[ 3.3.4.8. 計數行](#)[3.3.4.9. 使用1個以上的表
?](#)
SELECT語句用來從數據表中檢索信息。語句的一般格式是:
~~~
SELECT what_to_select
~~~
~~~
FROM which_table
~~~
~~~
WHERE conditions_to_satisfy;
~~~
what_to_select指出你想要看到的內容,可以是列的一個表,或*表示“所有的列”。which_table指出你想要從其檢索數據的表。WHERE子句是可選項,如果選擇該項,conditions_to_satisfy指定行必須滿足的檢索條件。
#### 3.3.4.1.?選擇所有數據
SELECT最簡單的形式是從一個表中檢索所有記錄:
~~~
mysql> SELECT * FROM pet;
~~~
~~~
+----------+--------+---------+------+------------+------------+
~~~
~~~
| name???? | owner? | species | sex? | birth????? | death????? |
~~~
~~~
+----------+--------+---------+------+------------+------------+
~~~
~~~
| Fluffy?? | Harold | cat???? | f??? | 1993-02-04 | NULL?????? |
~~~
~~~
| Claws??? | Gwen?? | cat???? | m??? | 1994-03-17 | NULL?????? |
~~~
~~~
| Buffy??? | Harold | dog???? | f??? | 1989-05-13 | NULL???? ??|
~~~
~~~
| Fang???? | Benny? | dog???? | m??? | 1990-08-27 | NULL?????? |
~~~
~~~
| Bowser?? | Diane? | dog???? | m??? | 1979-08-31 | 1995-07-29 |
~~~
~~~
| Chirpy?? | Gwen?? | bird??? | f??? | 1998-09-11 | NULL?????? |
~~~
~~~
| Whistler | Gwen?? | bird??? | NULL | 1997-12-09 | NULL ??????|
~~~
~~~
| Slim???? | Benny? | snake?? | m??? | 1996-04-29 | NULL?????? |
~~~
~~~
| Puffball | Diane? | hamster | f??? | 1999-03-30 | NULL?????? |
~~~
~~~
+----------+--------+---------+------+------------+------------+
~~~
如果你想要瀏覽整個表,可以使用這種形式的SELECT,例如,剛剛裝載了初始數據集以后。也有可能你想到Bowser的生日看起來不很對。查閱你原來的家譜,你發現正確的出生年是1989,而不是1979。
至少有兩種修正方法:
·???????? 編輯文件“pet.txt”改正錯誤,然后使用DELETE和LOAD DATA清空并重新裝載表:
~~~
·??????????????? mysql> DELETE FROM pet;
~~~
~~~
·??????????????? mysql> LOAD DATA LOCAL INFILE 'pet.txt' INTO TABLE pet;
~~~
然而, 如果這樣操做,必須重新輸入Puffball記錄。
·???????? 用一個UPDATE語句僅修正錯誤記錄:
~~~
·??????????????? mysql> UPDATE pet SET birth = '1989-08-31' WHERE name = 'Bowser';
~~~
UPDATE只更改有問題的記錄,不需要重新裝載數據庫表。
#### 3.3.4.2.?選擇特殊行
如上所示,檢索整個表是容易的。只需要從SELECT語句中刪掉WHERE子句。但是一般你不想看到整個表,特別地當表變得很大時。相反,你通常對回答一個具體的問題更感興趣,在這種情況下在你想要的信息上進行一些限制。讓我們看一些他們回答的有關你寵物的問題的選擇查詢。
可以從表中只選擇特定的行。例如,如果你想要驗證你對Bowser的生日所做的更改,按下述方法選擇Bowser的記錄:
~~~
mysql> SELECT * FROM pet WHERE name = 'Bowser';
~~~
~~~
+--------+-------+---------+------+------------+------------+
~~~
~~~
| name?? | owner | species | sex? | birth????? | death????? |
~~~
~~~
+--------+-------+---------+------+------------+------------+
~~~
~~~
| Bowser | Diane | dog???? | m??? | 1989-08-31 | 1995-07-29 |
~~~
~~~
+--------+-------+---------+------+------------+------------+
~~~
輸出證實正確的年份記錄為1989,而不是1979。
字符串比較時通常對大小些不敏感,因此你可以將名字指定為"bowser"、"BOWSER"等,查詢結果相同。
你可以在任何列上指定條件,不只僅僅是name。例如,如果你想要知道哪個動物在1998以后出生的,測試birth列:
~~~
mysql> SELECT * FROM pet WHERE birth > '1998-1-1';
~~~
~~~
+----------+-------+---------+------+------------+-------+
~~~
~~~
| name???? | owner | species | sex? | birth????? | death |
~~~
~~~
+----------+-------+---------+------+------------+-------+
~~~
~~~
| Chirpy? ?| Gwen? | bird??? | f??? | 1998-09-11 | NULL? |
~~~
~~~
| Puffball | Diane | hamster | f??? | 1999-03-30 | NULL? |
~~~
~~~
+----------+-------+---------+------+------------+-------+
~~~
可以組合條件,例如,找出雌性的狗:
~~~
mysql> SELECT * FROM pet WHERE species = 'dog' AND sex = 'f';
~~~
~~~
+-------+--------+---------+------+------------+-------+
~~~
~~~
| name? | owner? | species | sex? | birth????? | death |
~~~
~~~
+-------+--------+---------+------+------------+-------+
~~~
~~~
| Buffy | Harold | dog???? | f??? | 1989-05-13 | NULL? |
~~~
~~~
+-------+--------+---------+------+------------+-------+
~~~
上面的查詢使用AND邏輯操作符,也有一個OR操作符:
~~~
mysql> SELECT * FROM pet WHERE species = 'snake' OR species = 'bird';
~~~
~~~
+----------+-------+---------+------+------------+-------+
~~~
~~~
| name???? | owner | species | sex? | birth????? | death |
~~~
~~~
+----------+-------+---------+------+------------+-------+
~~~
~~~
| Chirpy?? | Gwen? | bird??? | f??? | 1998-09-11 | NULL? |
~~~
~~~
| Whistler | Gwen? | bird??? | NULL | 1997-12-09 | NULL? |
~~~
~~~
| Slim???? | Benny | snake?? | m??? | 1996-04-29 | NULL? |
~~~
~~~
+----------+-------+---------+------+------------+-------+
~~~
AND和OR可以混用,但AND比OR具有更高的優先級。如果你使用兩個操作符,使用圓括號指明如何對條件進行分組是一個好主意:
~~~
mysql> SELECT * FROM pet WHERE (species = 'cat' AND sex = 'm')
~~~
~~~
??? -> OR (species = 'dog' AND sex = 'f');
~~~
~~~
+-------+--------+---------+------+------------+-------+
~~~
~~~
| name? | owner? | species | sex? | birth????? | death |
~~~
~~~
+-------+--------+---------+------+------------+-------+
~~~
~~~
| Claws | Gwen?? | cat???? | m??? | 1994-03-17 | NULL? |
~~~
~~~
| Buffy | Harold | dog???? | f??? | 1989-05-13 | NULL? |
~~~
~~~
+-------+--------+---------+------+------------+-------+
~~~
#### 3.3.4.3.?選擇特殊列
如果你不想看到表中的所有行,就命名你感興趣的列,用逗號分開。例如,如果你想要知道你的動物什么時候出生的,選擇name和birth列:
~~~
mysql> SELECT name, birth FROM pet;
~~~
~~~
+----------+------------+
~~~
~~~
| name???? | birth????? |
~~~
~~~
+----------+------------+
~~~
~~~
| Fluffy?? | 1993-02-04 |
~~~
~~~
| Claws??? | 1994-03-17 |
~~~
~~~
| Buffy??? | 1989-05-13 |
~~~
~~~
| Fang???? | 1990-08-27 |
~~~
~~~
| Bowser?? | 1989-08-31 |
~~~
~~~
| Chirpy?? | 1998-09-11 |
~~~
~~~
| Whistler | 1997-12-09 |
~~~
~~~
| Slim???? | 1996-04-29 |
~~~
~~~
| Puffball | 1999-03-30 |
~~~
~~~
+----------+------------+
~~~
找出誰擁有寵物,使用這個查詢:
~~~
mysql> SELECT owner FROM pet;
~~~
~~~
+--------+
~~~
~~~
| owner? |
~~~
~~~
+--------+
~~~
~~~
| Harold |
~~~
~~~
| Gwen?? |
~~~
~~~
| Harold |
~~~
~~~
| Benny? |
~~~
~~~
| Diane? |
~~~
~~~
| Gwen?? |
~~~
~~~
| Gwen?? |
~~~
~~~
| Benny? |
~~~
~~~
| Diane? |
~~~
~~~
+--------+
~~~
請注意該查詢只是簡單地檢索每個記錄的owner列,并且他們中的一些出現多次。為了使輸出減到最少,增加關鍵字DISTINCT檢索出每個唯一的輸出記錄:
~~~
mysql> SELECT DISTINCT owner FROM pet;
~~~
~~~
+--------+
~~~
~~~
| owner? |
~~~
~~~
+--------+
~~~
~~~
| Benny? |
~~~
~~~
| Diane? |
~~~
~~~
| Gwen?? |
~~~
~~~
| Harold |
~~~
~~~
+--------+
~~~
可以使用一個WHERE子句結合行選擇與列選擇。例如,要想查詢狗和貓的出生日期,使用這個查詢:
~~~
mysql> SELECT name, species, birth FROM pet
~~~
~~~
??? -> WHERE species = 'dog' OR species = 'cat';
~~~
~~~
+--------+---------+------------+
~~~
~~~
| name?? | species | birth????? |
~~~
~~~
+--------+---------+------------+
~~~
~~~
| Fluffy | cat???? | 1993-02-04 |
~~~
~~~
| Claws? | cat???? | 1994-03-17 |
~~~
~~~
| Buffy? | dog???? | 1989-05-13 |
~~~
~~~
| Fang?? | dog???? | 1990-08-27 |
~~~
~~~
| Bowser | dog???? | 1989-08-31 |
~~~
~~~
+--------+---------+------------+
~~~
#### 3.3.4.4.?分類行
你可能已經注意到前面的例子中結果行沒有以特定的順序顯示。然而,當行按某種方式排序時,檢查查詢輸出通常更容易。為了排序結果,使用ORDER BY子句。
這里是動物生日,按日期排序:
~~~
mysql> SELECT name, birth FROM pet ORDER BY birth;
~~~
~~~
+----------+------------+
~~~
~~~
| name???? | birth????? |
~~~
~~~
+----------+------------+
~~~
~~~
| Buffy??? | 1989-05-13 |
~~~
~~~
| Bowser?? | 1989-08-31 |
~~~
~~~
| Fang???? | 1990-08-27 |
~~~
~~~
| Fluffy?? | 1993-02-04 |
~~~
~~~
| Claws??? | 1994-03-17 |
~~~
~~~
| Slim???? | 1996-04-29 |
~~~
~~~
| Whistler | 1997-12-09 |
~~~
~~~
| Chirpy?? | 1998-09-11 |
~~~
~~~
| Puffball | 1999-03-30 |
~~~
~~~
+----------+------------+
~~~
在字符類型列上,與所有其他比較操作類似,分類功能正常情況下是以區分大小寫的方式執行的。這意味著,對于等同但大小寫不同的列,并未定義其順序。對于某一列,可以使用BINARY強制執行區分大小寫的分類功能,如:ORDER BY BINARY *col_name*.
默認排序是升序,最小的值在第一。要想以降序排序,在你正在排序的列名上增加DESC(降序 )關鍵字:
~~~
mysql> SELECT name, birth FROM pet ORDER BY birth DESC;
~~~
~~~
+----------+------------+
~~~
~~~
| name???? | birth????? |
~~~
~~~
+----------+------------+
~~~
~~~
| Puffball | 1999-03-30 |
~~~
~~~
| Chirpy?? | 1998-09-11 |
~~~
~~~
| Whistler | 1997-12-09 |
~~~
~~~
| Slim???? | 1996-04-29 |
~~~
~~~
| Claws??? | 1994-03-17 |
~~~
~~~
| Fluffy?? | 1993-02-04 |
~~~
~~~
| Fang???? | 1990-08-27 |
~~~
~~~
| Bowser?? | 1989-08-31 |
~~~
~~~
| Buffy??? | 1989-05-13 |
~~~
~~~
+----------+------------+
~~~
可以對多個列進行排序,并且可以按不同的方向對不同的列進行排序。例如,按升序對動物的種類進行排序,然后按降序根據生日對各動物種類進行排序(最年輕的動物在最前面),使用下列查詢:
~~~
mysql> SELECT name, species, birth FROM pet
~~~
~~~
??? -> ORDER BY species, birth DESC;
~~~
~~~
+----------+---------+------------+
~~~
~~~
| name???? | species | birth????? |
~~~
~~~
+----------+---------+------------+
~~~
~~~
| Chirpy?? | bird??? | 1998-09-11 |
~~~
~~~
| Whistler | bird??? | 1997-12-09 |
~~~
~~~
| Claws??? | cat???? | 1994-03-17 |
~~~
~~~
| Fluffy?? | cat???? | 1993-02-04 |
~~~
~~~
| Fang???? | dog???? | 1990-08-27 |
~~~
~~~
| Bowser?? | dog???? | 1989-08-31 |
~~~
~~~
| Buffy??? | dog???? | 1989-05-13 |
~~~
~~~
| Puffball | hamster | 1999-03-30 |
~~~
~~~
| Slim???? | snake?? | 1996-04-29 |
~~~
~~~
+----------+---------+------------+
~~~
注意DESC關鍵字僅適用于在它前面的列名(birth);不影響species列的排序順序。
#### 3.3.4.5.?日期計算
**MySQL**提供了幾個函數,可以用來計算日期,例如,計算年齡或提取日期部分。
要想確定每個寵物有多大,可以計算當前日期的年和出生日期之間的差。如果當前日期的日歷年比出生日期早,則減去一年。以下查詢顯示了每個寵物的出生日期、當前日期和年齡數值的年數字。
~~~
mysql> SELECT name, birth, CURDATE(),
~~~
~~~
??? -> (YEAR(CURDATE())-YEAR(birth))
~~~
~~~
??? -> - (RIGHT(CURDATE(),5)<RIGHT(birth,5))
~~~
~~~
??? -> AS age
~~~
~~~
??? -> FROM pet;
~~~
~~~
+----------+------------+------------+------+
~~~
~~~
| name???? | birth????? | CURDATE()? | age? |
~~~
~~~
+----------+------------+------------+------+
~~~
~~~
| Fluffy?? | 1993-02-04 | 2003-08-19 |?? 10 |
~~~
~~~
| Claws??? | 1994-03-17 | 2003-08-19 |??? 9 |
~~~
~~~
| Buffy??? | 1989-05-13 | 2003-08-19 |?? 14 |
~~~
~~~
| Fang???? | 1990-08-27 | 2003-08-19 |?? 12 |
~~~
~~~
| Bowser?? | 1989-08-31 | 2003-08-19 |?? 13 |
~~~
~~~
| Chirpy?? | 1998-09-11 | 2003-08-19 |??? 4 |
~~~
~~~
| Whistler | 1997-12-09 | 2003-08-19 |??? 5 |
~~~
~~~
| Slim???? | 1996-04-29 | 2003-08-19 |??? 7 |
~~~
~~~
| Puffball | 1999-03-30 | 2003-08-19 |??? 4 |
~~~
~~~
+----------+------------+------------+------+
~~~
此處,YEAR()提取日期的年部分,RIGHT()提取日期的MM-DD (日歷年)部分的最右面5個字符。比較MM-DD值的表達式部分的值一般為1或0,如果CURDATE()的年比birth的年早,則年份應減去1。整個表達式有些難懂,使用*alias* (age)來使輸出的列標記更有意義。
盡管查詢可行,如果以某個順序排列行,則能更容易地瀏覽結果。添加ORDER BY name子句按照名字對輸出進行排序則能夠實現。
~~~
mysql> SELECT name, birth, CURDATE(),
~~~
~~~
??? -> (YEAR(CURDATE())-YEAR(birth))
~~~
~~~
??? -> - (RIGHT(CURDATE(),5)<RIGHT(birth,5))
~~~
~~~
???-> AS age
~~~
~~~
??? -> FROM pet ORDER BY name;
~~~
~~~
+----------+------------+------------+------+
~~~
~~~
| name???? | birth????? | CURDATE()? | age? |
~~~
~~~
+----------+------------+------------+------+
~~~
~~~
| Bowser?? | 1989-08-31 | 2003-08-19 |?? 13 |
~~~
~~~
| Buffy??? | 1989-05-13 | 2003-08-19 |?? 14 |
~~~
~~~
| Chirpy?? | 1998-09-11 | 2003-08-19 |??? 4 |
~~~
~~~
| Claws??? | 1994-03-17 | 2003-08-19 |??? 9 |
~~~
~~~
| Fang???? | 1990-08-27 | 2003-08-19 |?? 12 |
~~~
~~~
| Fluffy?? | 1993-02-04 | 2003-08-19 |?? 10 |
~~~
~~~
| Puffball | 1999-03-30 | 2003-08-19 |??? 4 |
~~~
~~~
| Slim? ???| 1996-04-29 | 2003-08-19 |??? 7 |
~~~
~~~
| Whistler | 1997-12-09 | 2003-08-19 |??? 5 |
~~~
~~~
+----------+------------+------------+------+
~~~
為了按age而非name排序輸出,只要再使用一個ORDER BY子句:
~~~
mysql> SELECT name, birth, CURDATE(),
~~~
~~~
??? -> (YEAR(CURDATE())-YEAR(birth))
~~~
~~~
??? -> - (RIGHT(CURDATE(),5)<RIGHT(birth,5))
~~~
~~~
??? -> AS age
~~~
~~~
??? -> FROM pet ORDER BY age;
~~~
~~~
+----------+------------+------------+------+
~~~
~~~
| name???? | birth????? | CURDATE()? | age? |
~~~
~~~
+----------+------------+------------+------+
~~~
~~~
| Chirpy?? | 1998-09-11 | 2003-08-19 |??? 4 |
~~~
~~~
| Puffball | 1999-03-30 | 2003-08-19 |??? 4 |
~~~
~~~
| Whistler | 1997-12-09 | 2003-08-19 |??? 5 |
~~~
~~~
| Slim???? | 1996-04-29 | 2003-08-19 |??? 7 |
~~~
~~~
| Claws??? | 1994-03-17 | 2003-08-19 |??? 9 |
~~~
~~~
| Fluffy?? | 1993-02-04 | 2003-08-19 |?? 10 |
~~~
~~~
| Fang???? | 1990-08-27 | 2003-08-19 |?? 12 |
~~~
~~~
| Bowser?? | 1989-08-31 | 2003-08-19 |?? 13 |
~~~
~~~
| Buffy??? | 1989-05-13 | 2003-08-19 |?? 14 |
~~~
~~~
+----------+------------+------------+------+
~~~
可以使用一個類似的查詢來確定已經死亡動物的死亡年齡。你通過檢查death值是否是NULL來確定是哪些動物,然后,對于那些非NULL值的動物,需要計算出death和birth值之間的差:
~~~
mysql> SELECT name, birth, death,
~~~
~~~
??? -> (YEAR(death)-YEAR(birth)) - (RIGHT(death,5)<RIGHT(birth,5))
~~~
~~~
??? -> AS age
~~~
~~~
??? -> FROM pet WHERE death IS NOT NULL ORDER BY age;
~~~
~~~
+--------+------------+------------+------+
~~~
~~~
| name?? | birth????? | death????? | age? |
~~~
~~~
+--------+------------+------------+------+
~~~
~~~
| Bowser | 1989-08-31 | 1995-07-29 |??? 5 |
~~~
~~~
+--------+------------+------------+------+
~~~
查詢使用death IS NOT NULL而非death != NULL,因為NULL是特殊的值,不能使用普通比較符來比較,以后會給出解釋。參見[3.3.4.6節,“NULL值操作``”](# "3.3.4.6.?Working with NULL Values")。
如果你想要知道哪個動物下個月過生日,怎么辦?對于這類計算,年和天是無關的,你只需要提取birth列的月份部分。**MySQL**提供幾個日期部分的提取函數,例如YEAR( )、MONTH( )和DAYOFMONTH( )。在這里MONTH()是適合的函數。為了看它怎樣工作,運行一個簡單的查詢,顯示birth和MONTH(birth)的值:
~~~
mysql> SELECT name, birth, MONTH(birth) FROM pet;
~~~
~~~
+----------+------------+--------------+
~~~
~~~
| name???? | birth????? | MONTH(birth) |
~~~
~~~
+----------+------------+--------------+
~~~
~~~
| Fluffy?? | 1993-02-04 |??????????? 2 |
~~~
~~~
| Claws??? | 1994-03-17 |??????????? 3 |
~~~
~~~
| Buffy ???| 1989-05-13 |??????????? 5 |
~~~
~~~
| Fang???? | 1990-08-27 |??????????? 8 |
~~~
~~~
| Bowser?? | 1989-08-31 |??????????? 8 |
~~~
~~~
| Chirpy?? | 1998-09-11 |??????????? 9 |
~~~
~~~
| Whistler | 1997-12-09 |?????????? 12 |
~~~
~~~
| Slim???? | 1996-04-29 |??????????? 4 |
~~~
~~~
| Puffball | 1999-03-30 |??????????? 3 |
~~~
~~~
+----------+------------+--------------+
~~~
找出下個月生日的動物也是容易的。假定當前月是4月,那么月值是4,你可以找在5月出生的動物 (5月),方法是:
~~~
mysql> SELECT name, birth FROM pet WHERE MONTH(birth) = 5;
~~~
~~~
+-------+------------+
~~~
~~~
| name? | birth????? |
~~~
~~~
+-------+------------+
~~~
~~~
| Buffy | 1989-05-13 |
~~~
~~~
+-------+------------+
~~~
如果當前月份是12月,就有點復雜了。你不能只把1加到月份數(12)上并尋找在13月出生的動物,因為沒有這樣的月份。相反,你應尋找在1月出生的動物(1月) 。
你甚至可以編寫查詢,不管當前月份是什么它都能工作。采用這種方法不必在查詢中使用一個特定的月份,DATE_ADD( )允許在一個給定的日期上加上時間間隔。如果在NOW( )值上加上一個月,然后用MONTH()提取月份,結果產生生日所在月份:
~~~
mysql> SELECT name, birth FROM pet
~~~
~~~
??? -> WHERE MONTH(birth) = MONTH(DATE_ADD(CURDATE(),INTERVAL 1 MONTH));
~~~
完成該任務的另一個方法是加1以得出當前月份的下一個月(在使用取模函數(MOD)后,如果月份當前值是12,則“回滾”到值0):
~~~
mysql> SELECT name, birth FROM pet
~~~
~~~
??? -> WHERE MONTH(birth) = MOD(MONTH(CURDATE()), 12) + 1;
~~~
注意,MONTH返回在1和12之間的一個數字,且MOD(something,12)返回在0和11之間的一個數字,因此必須在MOD( )以后加1,否則我們將從11月( 11 )跳到1月(1)。
#### 3.3.4.6.?NULL值操作``
NULL值可能令人感到奇怪直到你習慣它。概念上,NULL意味著“沒有值”或“未知值”,且它被看作與眾不同的值。為了測試NULL,你不能使用算術比較 操作符例如=、<或!=。為了說明它,試試下列查詢:
~~~
mysql> SELECT 1 = NULL, 1 <> NULL, 1 < NULL, 1 > NULL;
~~~
~~~
+----------+-----------+----------+----------+
~~~
~~~
| 1 = NULL | 1 <> NULL | 1 < NULL | 1 > NULL |
~~~
~~~
+----------+-----------+----------+----------+
~~~
~~~
|???? NULL |????? NULL |???? NULL |???? NULL |
~~~
~~~
+----------+-----------+----------+----------+
~~~
很顯然你不能通過這些比較得到有意義的結果。相反使用IS NULL和IS NOT NULL操作符:
~~~
mysql> SELECT 1 IS NULL, 1 IS NOT NULL;
~~~
~~~
+-----------+---------------+
~~~
~~~
| 1 IS NULL | 1 IS NOT NULL |
~~~
~~~
+-----------+---------------+
~~~
~~~
|???????? 0 |???????????? 1 |
~~~
~~~
+-----------+---------------+
~~~
請注意在**MySQL**中,0或 NULL意味著假而其它值意味著真。布爾運算的默認真值是1。
對NULL的特殊處理即是在前面的章節中,為了決定哪個動物不再是活著的,使用death IS NOT NULL而不使用death != NULL的原因。
在GROUP BY中,兩個NULL值視為相同。
執行ORDER BY時,如果運行 ORDER BY ... ASC,則NULL值出現在最前面,若運行ORDER BY ... DESC,則NULL值出現在最后面。
NULL操作的常見錯誤是不能在定義為NOT NULL的列內插入0或空字符串,但事實并非如此。在NULL表示"沒有數值"的地方有數值。使用IS [NOT] NULL則可以很容易地進行測試,如下所示:
~~~
mysql> SELECT 0 IS NULL, 0 IS NOT NULL, '' IS NULL, '' IS NOT NULL;
~~~
~~~
+-----------+---------------+------------+----------------+
~~~
~~~
| 0 IS NULL | 0 IS NOT NULL | '' IS NULL | '' IS NOT NULL |
~~~
~~~
+-----------+---------------+------------+----------------+
~~~
~~~
|???????? 0 |???????????? 1 |????????? 0 |????????????? 1 |
~~~
~~~
+-----------+---------------+------------+----------------+
~~~
因此完全可以在定義為NOT NULL的列內插入0或空字符串,實際是NOT NULL。參見[A.5.3節,“與NULL值有關的問題``”](# "A.5.3.?Problems with NULL Values")。
#### 3.3.4.7.?模式匹配
** MySQL**提供標準的SQL模式匹配,以及一種基于象Unix實用程序如**vi****、****grep**和**sed**的擴展正則表達式模式匹配的格式。
SQL模式匹配允許你使用“_”匹配任何單個字符,而“%”匹配任意數目字符(包括零字符)。在 MySQL中,SQL的模式默認是忽略大小寫的。下面給出一些例子。注意使用SQL模式時,不能使用=或!=;而應使用LIKE或NOT LIKE比較操作符。
要想找出以“b”開頭的名字:
~~~
mysql> SELECT * FROM pet WHERE name LIKE 'b%';
~~~
~~~
+--------+--------+---------+------+------------+------------+
~~~
~~~
| name?? | owner? | species | sex? | birth????? | death????? |
~~~
~~~
+--------+--------+---------+------+------------+------------+
~~~
~~~
| Buffy? | Harold | dog???? | f??? | 1989-05-13 | NULL?????? |
~~~
~~~
| Bowser | Diane? | dog???? | m??? | 1989-08-31 | 1995-07-29 |
~~~
~~~
+--------+--------+---------+------+------------+------------+
~~~
要想找出以“fy”結尾的名字:
~~~
mysql> SELECT * FROM pet WHERE name LIKE '%fy';
~~~
~~~
+--------+--------+---------+------+------------+-------+
~~~
~~~
| name?? | owner? | species | sex? | birth????? | death |
~~~
~~~
+--------+--------+---------+------+------------+-------+
~~~
~~~
| Fluffy | Harold | cat???? | f??? | 1993-02-04 | NULL? |
~~~
~~~
| Buffy? | Harold | dog???? | f??? | 1989-05-13 | NULL? |
~~~
~~~
+--------+--------+---------+------+------------+-------+
~~~
要想找出包含“w”的名字:
~~~
mysql> SELECT * FROM pet WHERE name LIKE '%w%';
~~~
~~~
+----------+-------+---------+------+------------+------------+
~~~
~~~
| name???? | owner | species | sex? | birth????? | death????? |
~~~
~~~
+----------+-------+---------+------+------------+------------+
~~~
~~~
| Claws??? | Gwen? | cat???? | m??? | 1994-03-17 | NULL?????? |
~~~
~~~
| Bowser?? | Diane | dog???? | m??? | 1989-08-31 | 1995-07-29 |
~~~
~~~
| Whistler | Gwen? | bird??? | NULL | 1997-12-09 | NULL?????? |
~~~
~~~
+----------+-------+---------+------+------------+------------+
~~~
要想找出正好包含5個字符的名字,使用“_”模式字符:
~~~
mysql> SELECT * FROM pet WHERE name LIKE '_____';
~~~
~~~
+-------+--------+---------+------+------------+-------+
~~~
~~~
| name? | owner? | species | sex? | birth????? | death |
~~~
~~~
+-------+--------+---------+------+------------+-------+
~~~
~~~
| Claws | Gwen?? | cat???? | m??? | 1994-03-17 | NULL? |
~~~
~~~
| Buffy | Harold | dog???? | f??? | 1989-05-13 | NULL? |
~~~
~~~
+-------+--------+---------+------+------------+-------+
~~~
由**MySQL**提供的模式匹配的其它類型是使用擴展正則表達式。當你對這類模式進行匹配測試時,使用REGEXP和NOT REGEXP操作符(或RLIKE和NOT RLIKE,它們是同義詞)。
擴展正則表達式的一些字符是:
·???????? ‘.’匹配任何單個的字符。
·???????? 字符類“[...]”匹配在方括號內的任何字符。例如,“[abc]”匹配“a”、“b”或“c”。為了命名字符的范圍,使用一個“-”。“[a-z]”匹配任何字母,而“[0-9]”匹配任何數字。
·???????? “ * ”匹配零個或多個在它前面的字符。例如,“x*”匹配任何數量的“x”字符,“[0-9]*”匹配任何數量的數字,而“.*”匹配任何數量的任何字符。
- 如果REGEXP模式與被測試值的任何地方匹配,模式就匹配(這不同于LIKE模式匹配,只有與整個值匹配,模式才匹配)。
- 為了定位一個模式以便它必須匹配被測試值的開始或結尾,在模式開始處使用“^”或在模式的結尾用“$”。
為了說明擴展正則表達式如何工作,下面使用REGEXP重寫上面所示的LIKE查詢:
為了找出以“b”開頭的名字,使用“^”匹配名字的開始:
~~~
mysql> SELECT * FROM pet WHERE name REGEXP '^b';
~~~
~~~
+--------+--------+---------+------+------------+------------+
~~~
~~~
| name?? | owner? | species | sex? | birth????? | death????? |
~~~
~~~
+--------+--------+---------+------+------------+------------+
~~~
~~~
| Buffy? | Harold | dog???? | f??? | 1989-05-13 | NULL?????? |
~~~
~~~
| Bowser | Diane? | dog???? | m??? | 1989-08-31 | 1995-07-29 |
~~~
~~~
+--------+--------+---------+------+------------+------------+
~~~
如果你想強制使REGEXP比較區分大小寫,使用BINARY關鍵字使其中一個字符串變為二進制字符串。該查詢只匹配名稱首字母的小寫‘b’。
~~~
mysql> SELECT * FROM pet WHERE name REGEXP BINARY '^b';
~~~
為了找出以“fy”結尾的名字,使用“$”匹配名字的結尾:
~~~
mysql> SELECT * FROM pet WHERE name REGEXP 'fy$';
~~~
~~~
+--------+--------+---------+------+------------+-------+
~~~
~~~
| name?? | owner? | species | sex? | birth????? | death |
~~~
~~~
+--------+--------+---------+------+------------+-------+
~~~
~~~
| Fluffy | Harold | cat???? | f??? | 1993-02-04 | NULL? |
~~~
~~~
| Buffy? | Harold | dog???? | f??? | 1989-05-13 | NULL? |
~~~
~~~
+--------+--------+---------+------+------------+-------+
~~~
為了找出包含一個“w”的名字,使用以下查詢:
~~~
mysql> SELECT * FROM pet WHERE name REGEXP 'w';
~~~
~~~
+----------+-------+---------+------+------------+------------+
~~~
~~~
| name???? | owner | species | sex? | birth????? | death????? |
~~~
~~~
+----------+-------+---------+------+------------+------------+
~~~
~~~
| Claws??? | Gwen? | cat???? | m??? | 1994-03-17 | NULL?????? |
~~~
~~~
| Bowser?? | Diane | dog???? | m??? | 1989-08-31 | 1995-07-29 |
~~~
~~~
| Whistler | Gwen? | bird??? | NULL | 1997-12-09 | NULL?????? |
~~~
~~~
+----------+-------+---------+------+------------+------------+
~~~
既然如果一個正則表達式出現在值的任何地方,其模式匹配了,就不必在先前的查詢中在模式的兩側放置一個通配符以使得它匹配整個值,就像你使用了一個SQL模式那樣。
為了找出包含正好5個字符的名字,使用“^”和“$”匹配名字的開始和結尾,和5個“.”實例在兩者之間:
~~~
mysql> SELECT * FROM pet WHERE name REGEXP '^.....$';
~~~
~~~
+-------+--------+---------+------+------------+-------+
~~~
~~~
| name? | owner? | species | sex? | birth????? | death |
~~~
~~~
+-------+--------+---------+------+------------+-------+
~~~
~~~
| Claws | Gwen?? | cat???? | m??? | 1994-03-17 | NULL? |
~~~
~~~
| Buffy | Harold | dog???? | f??? | 1989-05-13 | NULL? |
~~~
~~~
+-------+--------+---------+------+------------+-------+
~~~
你也可以使用“{n}”“重復n次”操作符重寫前面的查詢:
~~~
mysql> SELECT * FROM pet WHERE name REGEXP '^.{5}$';
~~~
~~~
+-------+--------+---------+------+------------+-------+
~~~
~~~
| name? | owner? | species | sex? | birth????? | death |
~~~
~~~
+-------+--------+---------+------+------------+-------+
~~~
~~~
| Claws | Gwen?? | cat???? | m??? | 1994-03-17 | NULL? |
~~~
~~~
| Buffy | Harold | dog???? | f??? | 1989-05-13 | NULL? |
~~~
~~~
+-------+--------+---------+------+------------+-------+
~~~
[附錄G:](#)[*MySQL正則表達式*](# "Appendix?G.?MySQL Regular Expressions")提供了關于正則表達式的句法的詳細信息。
#### 3.3.4.8.?計數行
數據庫經常用于回答這個問題,“某個類型的數據在表中出現的頻度?”例如,你可能想要知道你有多少寵物,或每位主人有多少寵物,或你可能想要對你的動物進行各種類型的普查。
計算你擁有動物的總數目與“在pet表中有多少行?”是同樣的問題,因為每個寵物有一個記錄。COUNT(*)函數計算行數,所以計算動物數目的查詢應為:
~~~
mysql> SELECT COUNT(*) FROM pet;
~~~
~~~
+----------+
~~~
~~~
| COUNT(*) |
~~~
~~~
+----------+
~~~
~~~
|??????? 9 |
~~~
~~~
+----------+
~~~
在前面,你檢索了擁有寵物的人的名字。如果你想要知道每個主人有多少寵物,你可以使用COUNT( )函數:
~~~
mysql> SELECT owner, COUNT(*) FROM pet GROUP BY owner;
~~~
~~~
+--------+----------+
~~~
~~~
| owner? | COUNT(*) |
~~~
~~~
+--------+----------+
~~~
~~~
| Benny? |??????? 2 |
~~~
~~~
| Diane? |??????? 2 |
~~~
~~~
| Gwen?? |??????? 3 |
~~~
~~~
| Harold |??????? 2 |
~~~
~~~
+--------+----------+
~~~
注意,使用GROUP BY對每個owner的所有記錄分組,沒有它,你會得到錯誤消息:
~~~
mysql> SELECT owner, COUNT(*) FROM pet;
~~~
~~~
ERROR 1140 (42000): Mixing of GROUP columns (MIN(),MAX(),COUNT(),...)
~~~
~~~
with no GROUP columns is illegal if there is no GROUP BY clause
~~~
COUNT( )和GROUP BY以各種方式分類你的數據。下列例子顯示出進行動物普查操作的不同方式。
每種動物的數量:
~~~
mysql> SELECT species, COUNT(*) FROM pet GROUP BY species;
~~~
~~~
+---------+----------+
~~~
~~~
| species | COUNT(*) |
~~~
~~~
+---------+----------+
~~~
~~~
| bird??? |??????? 2 |
~~~
~~~
| cat???? |??????? 2 |
~~~
~~~
| dog???? |??????? 3 |
~~~
~~~
| hamster |??????? 1 |
~~~
~~~
| snake?? |??????? 1 |
~~~
~~~
+---------+----------+
~~~
每種性別的動物數量:
~~~
mysql> SELECT sex, COUNT(*) FROM pet GROUP BY sex;
~~~
~~~
+------+----------+
~~~
~~~
| sex? | COUNT(*) |
~~~
~~~
+------+----------+
~~~
~~~
| NULL |??????? 1 |
~~~
~~~
| f??? |??????? 4 |
~~~
~~~
| m??? |??????? 4 |
~~~
~~~
+------+----------+
~~~
(在這個輸出中,NULL表示“未知性別”。)
按種類和性別組合的動物數量:
~~~
mysql> SELECT species, sex, COUNT(*) FROM pet GROUP BY species, sex;
~~~
~~~
+---------+------+----------+
~~~
~~~
| species | sex? | COUNT(*) |
~~~
~~~
+---------+------+----------+
~~~
~~~
| bird??? | NULL |??????? 1 |
~~~
~~~
| bird??? | f??? |??????? 1 |
~~~
~~~
| cat???? | f??? |??????? 1 |
~~~
~~~
| cat???? | m??? |??????? 1 |
~~~
~~~
| dog???? | f??? |??????? 1 |
~~~
~~~
| dog???? | m??? |??????? 2 |
~~~
~~~
| hamster | f??? |??????? 1 |
~~~
~~~
| snake?? | m??? |????? ??1 |
~~~
~~~
+---------+------+----------+
~~~
若使用COUNT( ),你不必檢索整個表。例如, 前面的查詢,當只對狗和貓進行時,應為:
~~~
mysql> SELECT species, sex, COUNT(*) FROM pet
~~~
~~~
??? -> WHERE species = 'dog' OR species = 'cat'
~~~
~~~
??? -> GROUP BY species, sex;
~~~
~~~
+---------+------+----------+
~~~
~~~
| species | sex? | COUNT(*) |
~~~
~~~
+---------+------+----------+
~~~
~~~
| cat???? | f??? |??????? 1 |
~~~
~~~
| cat???? | m??? |??????? 1 |
~~~
~~~
| dog???? | f??? |??????? 1 |
~~~
~~~
| dog???? | m??? |??????? 2 |
~~~
~~~
+---------+------+----------+
~~~
或,如果你僅需要知道已知性別的按性別的動物數目:
~~~
mysql> SELECT species, sex, COUNT(*) FROM pet
~~~
~~~
??? -> WHERE sex IS NOT NULL
~~~
~~~
??? -> GROUP BY species, sex;
~~~
~~~
+---------+------+----------+
~~~
~~~
| species | sex? | COUNT(*) |
~~~
~~~
+---------+------+----------+
~~~
~~~
| bird??? | f??? |??????? 1 |
~~~
~~~
| cat???? | f??? |??????? 1 |
~~~
~~~
| cat???? | m??? |??????? 1 |
~~~
~~~
| dog??? ?| f??? |??????? 1 |
~~~
~~~
| dog???? | m??? |??????? 2 |
~~~
~~~
| hamster | f??? |??????? 1 |
~~~
~~~
| snake?? | m??? |??????? 1 |
~~~
~~~
+---------+------+----------+
~~~
#### 3.3.4.9.?使用1個以上的表
?
pet表追蹤你有哪個寵物。如果你想要記錄其它相關信息,例如在他們一生中看獸醫或何時后代出生,你需要另外的表。這張表應該像什么呢?需要:
·???????? 它需要包含寵物名字以便你知道每個事件屬于哪個動物。
·???????? 需要一個日期以便你知道事件是什么時候發生的。
·???????? 需要一個描述事件的字段。
·???????? 如果你想要對事件進行分類,則需要一個事件類型字段。
綜合上述因素,event表的CREATE TABLE語句應為:
~~~
mysql> CREATE TABLE event (name VARCHAR(20), date DATE,
~~~
~~~
??? -> type VARCHAR(15), remark VARCHAR(255));
~~~
對于pet表,最容易的方法是創建包含信息的用定位符分隔的文本文件來裝載初始記錄:
<table border="1" cellpadding="0" id="table4"><tr><td> <p><strong> <span>name</span></strong></p></td> <td> <p><strong> <span>date</span></strong></p></td> <td> <p><strong> <span>type</span></strong></p></td> <td> <p><strong> <span>remark</span></strong></p></td> </tr><tr><td> <p><span>Fluffy</span></p></td> <td> <p><span>1995-05-15</span></p></td> <td> <p><span>litter</span></p></td> <td> <p><span>4 kittens, 3 female, 1 male</span></p></td> </tr><tr><td> <p><span>Buffy</span></p></td> <td> <p><span>1993-06-23</span></p></td> <td> <p><span>litter</span></p></td> <td> <p><span>5 puppies, 2 female, 3 male</span></p></td> </tr><tr><td> <p><span>Buffy</span></p></td> <td> <p><span>1994-06-19</span></p></td> <td> <p><span>litter</span></p></td> <td> <p><span>3 puppies, 3 female</span></p></td> </tr><tr><td> <p><span>Chirpy</span></p></td> <td> <p><span>1999-03-21</span></p></td> <td> <p><span>vet</span></p></td> <td> <p><span>needed beak straightened</span></p></td> </tr><tr><td> <p><span>Slim</span></p></td> <td> <p><span>1997-08-03</span></p></td> <td> <p><span>vet</span></p></td> <td> <p><span>broken rib</span></p></td> </tr><tr><td> <p><span>Bowser</span></p></td> <td> <p><span>1991-10-12</span></p></td> <td> <p><span>kennel</span></p></td> <td> <p><span>?</span></p></td> </tr><tr><td> <p><span>Fang</span></p></td> <td> <p><span>1991-10-12</span></p></td> <td> <p><span>kennel</span></p></td> <td> <p><span>?</span></p></td> </tr><tr><td> <p><span>Fang</span></p></td> <td> <p><span>1998-08-28</span></p></td> <td> <p><span>birthday</span></p></td> <td> <p><span>Gave him a new chew toy</span></p></td> </tr><tr><td> <p><span>Claws</span></p></td> <td> <p><span>1998-03-17</span></p></td> <td> <p><span>birthday</span></p></td> <td> <p><span>Gave him a new flea collar</span></p></td> </tr><tr><td> <p><span>Whistler</span></p></td> <td> <p><span>1998-12-09</span></p></td> <td> <p><span>birthday</span></p></td> <td> <p><span>First birthday</span></p></td> </tr></table>
采用如下方式裝載記錄:
~~~
mysql> LOAD DATA LOCAL INFILE 'event.txt' INTO TABLE event;
~~~
根據你從已經運行在pet表上的查詢中學到的,你應該能執行對event表中記錄的檢索;原理是一樣的。但是什么時候event表本身不能回答你可能問的問題呢?
當他們有了一窩小動物時,假定你想要找出每只寵物的年齡。我們前面看到了如何通過兩個日期計算年齡。event表中有母親的生產日期,但是為了計算母親的年齡,你需要她的出生日期,存儲在pet表中。說明查詢需要兩個表:
~~~
mysql> SELECT pet.name,
~~~
~~~
??? -> (YEAR(date)-YEAR(birth)) - (RIGHT(date,5)<RIGHT(birth,5)) AS age,
~~~
~~~
??? -> remark
~~~
~~~
??? -> FROM pet, event
~~~
~~~
??? -> WHERE pet.name = event.name AND event.type = 'litter';
~~~
~~~
+--------+------+-----------------------------+
~~~
~~~
| name?? | age? | remark????????????????????? |
~~~
~~~
+--------+------+-----------------------------+
~~~
~~~
| Fluffy |??? 2 | 4 kittens, 3 female, 1 male |
~~~
~~~
| Buffy? |??? 4 | 5 puppies, 2 female, 3 male |
~~~
~~~
| Buffy? |??? 5 | 3 puppies, 3 female???????? |
~~~
~~~
+--------+------+-----------------------------+
~~~
關于該查詢要注意的幾件事情:
- FROM子句列出兩個表,因為查詢需要從兩個表提取信息。
- 當從多個表組合(聯結)信息時,你需要指定一個表中的記錄怎樣能匹配其它表的記錄。這很簡單,因為它們都有一個name列。查詢使用WHERE子句基于name值來匹配2個表中的記錄。
- 因為name列出現在兩個表中,當引用列時,你一定要指定哪個表。把表名附在列名前即可以實現。
你不必有2個不同的表來進行聯結。如果你想要將一個表的記錄與同一個表的其它記錄進行比較,可以將一個表聯結到自身。例如,為了在你的寵物之中繁殖配偶,你可以用pet聯結自身來進行相似種類的雄雌配對:
?
~~~
mysql> SELECT p1.name, p1.sex, p2.name, p2.sex, p1.species
~~~
~~~
??? -> FROM pet AS p1, pet AS p2
~~~
~~~
??? -> WHERE p1.species = p2.species AND p1.sex = 'f' AND p2.sex = 'm';
~~~
~~~
+--------+------+--------+------+---------+
~~~
~~~
| name?? | sex? | name?? | sex? | species |
~~~
~~~
+--------+------+--------+------+---------+
~~~
~~~
| Fluffy | f??? | Claws? | m??? | cat???? |
~~~
~~~
| Buffy? | f??? | Fang?? | m??? | dog???? |
~~~
~~~
| Buffy? | f??? | Bowser | m??? | dog???? |
~~~
~~~
+--------+------+--------+------+---------+
~~~
在這個查詢中,我們為表名指定別名以便能引用列并且使得每一個列引用與哪個表實例相關聯更直觀。
### 3.4.?獲得數據庫和表的信息
如果你忘記數據庫或表的名字,或給定的表的結構是什么(例如,它的列叫什么),怎么辦?**MySQL**通過提供數據庫及其支持的表的信息的幾個語句解決這個問題。
你已經見到了SHOW DATABASES,它列出由服務器管理的數據庫。為了找出當前選擇了哪個數據庫,使用DATABASE( )函數:
~~~
mysql> SELECT DATABASE();
~~~
~~~
+------------+
~~~
~~~
| DATABASE() |
~~~
~~~
+------------+
~~~
~~~
| menagerie? |
~~~
~~~
+------------+
~~~
如果你還沒選擇任何數據庫,結果是NULL。
為了找出當前的數據庫包含什么表(例如,當你不能確定一個表的名字),使用這個命令:
~~~
mysql> SHOW TABLES;
~~~
~~~
+---------------------+
~~~
~~~
| Tables in menagerie |
~~~
~~~
+---------------------+
~~~
~~~
| event?????????????? |
~~~
~~~
| pet???????????????? |
~~~
~~~
+---------------------+
~~~
如果你想要知道一個表的結構,可以使用DESCRIBE命令;它顯示表中每個列的信息:
~~~
mysql> DESCRIBE pet;
~~~
~~~
+---------+-------------+------+-----+---------+-------+
~~~
~~~
| Field?? | Type??????? | Null | Key | Default | Extra |
~~~
~~~
+---------+-------------+------+-----+---------+-------+
~~~
~~~
| name??? | varchar(20) | YES? |???? | NULL??? |?????? |
~~~
~~~
| owner?? | varchar(20) | YES? |???? | NULL??? |?????? |
~~~
~~~
| species | varchar(20) | YES? |???? | NULL??? |??? ???|
~~~
~~~
| sex???? | char(1)???? | YES? |???? | NULL??? |?????? |
~~~
~~~
| birth?? | date??????? | YES? |???? | NULL??? |?????? |
~~~
~~~
| death?? | date??????? | YES? |???? | NULL??? |?????? |
~~~
~~~
+---------+-------------+------+-----+---------+-------+
~~~
Field顯示列名字,Type是列的數據類型,Null表示列是否能包含NULL值,Key顯示列是否被索引而Default指定列的默認值。
如果表有索引,SHOW INDEX FROM tbl_name生成有關索引的信息。
### 3.5.?在批處理模式**下使用my**sql
在前面的章節中,你交互式地使用**mysql**輸入查詢并且查看結果。你也可以以批模式運行**mysql**。為了做到這些,把你想要運行的命令放在一個文件中,然后告訴**mysql**從文件讀取它的輸入:
~~~
shell> mysql < batch-file
~~~
如果在Windows下運行**mysql**,并且文件中有一些可以造成問題的特殊字符,可以這樣操作:
~~~
C:\> mysql -e "source batch-file"
~~~
如果你需要在命令行上指定連接參數,命令應為:
~~~
shell> mysql -h host -u user -p < batch-file
~~~
~~~
Enter password: ********
~~~
當這樣操作**mysql**時,則創建一個腳本文件,然后執行腳本。
如果你想在語句出現錯誤的時候仍想繼續執行腳本,則應使用--force命令行選項。
為什么要使用一個腳本?有很多原因:
- 如果你需要重復運行查詢(比如說,每天或每周),可以把它編成一個腳本,則每次執行時不必重新鍵入。
- 可以通過拷貝并編輯腳本文件從類似的現有的查詢生成一個新查詢。
- 當你正在開發查詢時,批模式也是很有用的,特別對多行命令或多語句命令序列。如果你犯了一個錯誤,你不必重新輸入所有內容,只需要編輯腳本來改正錯誤,然后告訴**mysql**再次執行腳本。
- 如果你有一個產生多個輸出的查詢,你可以通過一個分頁器而不是盯著它翻屏到屏幕的頂端來運行輸出:
~~~
·??????????????? shell> mysql < batch-file | more
~~~
- 你可以捕捉文件中的輸出以便進行進一步的處理:
~~~
·??????????????? shell> mysql < batch-file > mysql.out
~~~
- 你可以將腳本分發給另外的人,以便他們也能運行命令。
- 某些情況不允許交互地使用,例如, 當你從一個**cron**任務中運行查詢時。在這種情況下,你必須使用批模式。
當你以批模式運行mysql時,比起你交互地使用它時,其默認輸出格式是不同的(更簡明些)。例如,當交互式運行SELECT DISTINCT species FROM pet時,輸出應為:
?
~~~
+---------+
~~~
~~~
| species |
~~~
~~~
+---------+
~~~
~~~
| bird??? |
~~~
~~~
| cat???? |
~~~
~~~
| dog???? |
~~~
~~~
| hamster |
~~~
~~~
| snake?? |
~~~
~~~
+---------+
~~~
但是當以批模式運行時,輸出應為:
~~~
species
~~~
~~~
bird
~~~
~~~
cat
~~~
~~~
dog
~~~
~~~
hamster
~~~
~~~
snake
~~~
如果你想要在批模式中得到交互輸出格式,使用mysql -t。為了回顯以輸出被執行的命令,使用mysql -vvv。
你還可以使用源代碼或 \.命令從**mysql**提示符運行腳本:
~~~
mysql> source filename;
~~~
~~~
mysql> \. filename
~~~
### 3.6.?常用查詢的例子
[ 3.6.1. 列的最大值](#)[ 3.6.2. 擁有某個列的最大值的行](#)[ 3.6.3. 列的最大值:按組](#)[ 3.6.4. 擁有某個字段的組間最大值的行](#)[ 3.6.5. 使用用戶變量](#)[ 3.6.6. 使用外鍵](#)[ 3.6.7. 根據兩個鍵搜索](#)[ 3.6.8. 根據天計算訪問量](#)[ 3.6.9. 使用AUTO_INCREMENT](#)
下面是一些學習如何用**MySQL**解決一些常見問題的例子。
在一些例子中,使用數據庫表“shop”來儲存某個商人(經銷商)的每件物品(物品號)的價格。假定每個商人對每項物品有一個固定價格,那么(物品,商人)即為該記錄的主關鍵字。
啟動命令行工具**mysql**并選擇數據庫:
~~~
shell> mysql your-database-name
~~~
(在大多數MySQL中,你可以使用test數據庫)。
你可以使用以下語句創建示例表:
~~~
mysql> CREATE TABLE shop (
~~~
~~~
??? -> article INT(4) UNSIGNED ZEROFILL DEFAULT '0000' NOT NULL,
~~~
~~~
??? -> dealer? CHAR(20)???????????????? DEFAULT ''???? NOT NULL,
~~~
~~~
??? -> price?? DOUBLE(16,2)???????????? DEFAULT '0.00' NOT NULL,
~~~
~~~
??? -> PRIMARY KEY(article, dealer));
~~~
~~~
mysql> INSERT INTO shop VALUES
~~~
~~~
??? -> (1,'A',3.45),(1,'B',3.99),(2,'A',10.99),(3,'B',1.45),
~~~
~~~
??? -> (3,'C',1.69),(3,'D',1.25),(4,'D',19.95);
~~~
執行語句后,表應包含以下內容:
~~~
mysql> SELECT * FROM shop;
~~~
~~~
+---------+--------+-------+
~~~
~~~
| article | dealer | price |
~~~
~~~
+---------+--------+-------+
~~~
~~~
|??? 0001 | A????? |? 3.45 |
~~~
~~~
|??? 0001 | B????? |? 3.99 |
~~~
~~~
|??? 0002 | A????? | 10.99 |
~~~
~~~
| ???0003 | B????? |? 1.45 |
~~~
~~~
|??? 0003 | C????? |? 1.69 |
~~~
~~~
|??? 0003 | D????? |? 1.25 |
~~~
~~~
|??? 0004 | D????? | 19.95 |
~~~
~~~
+---------+--------+-------+
~~~
### 3.6.1.?列的最大值
“最大的物品號是什么?”
~~~
SELECT MAX(article) AS article FROM shop;
+---------+
| article |
+---------+
| 4 |
+---------+
~~~
### 3.6.2.?擁有某個列的最大值的行
*任務:找出最貴物品的編號、銷售商和價格。*
這很容易用一個子查詢做到:
~~~
SELECT article, dealer, price
~~~
~~~
FROM?? shop
~~~
~~~
WHERE? price=(SELECT MAX(price) FROM shop);
~~~
另一個解決方案是按價格降序排序所有行并用**MySQL**特定LIMIT子句只得到第一行:
~~~
SELECT article, dealer, price
~~~
~~~
FROM shop
~~~
~~~
ORDER BY price DESC
~~~
~~~
LIMIT 1;
~~~
**注**:如果有多項最貴的物品( 例如每個的價格為19.95),LIMIT解決方案僅僅顯示其中一個!
### 3.6.3.?列的最大值:按組
*任務:每項物品的的最高價格是多少?*
~~~
SELECT article, MAX(price) AS price
FROM shop
GROUP BY article
+---------+-------+
| article | price |
+---------+-------+
| 0001 | 3.99 |
| 0002 | 10.99 |
| 0003 | 1.69 |
| 0004 | 19.95 |
+---------+-------+
~~~
### 3.6.4.?擁有某個字段的組間最大值的行
*任務:對每項物品,找出最貴價格的物品的經銷商。*
可以用這樣一個子查詢解決該問題:
~~~
SELECT article, dealer, price
FROM shop s1
WHERE price=(SELECT MAX(s2.price)
FROM shop s2
WHERE s1.article = s2.article);
~~~
### 3.6.5.?使用用戶變量
你可以清空MySQL用戶變量以記錄結果,不必將它們保存到客戶端的臨時變量中。(參見 [ 9.3節,“用戶變量”](# "9.3.?User Variables").)。
例如,要找出價格最高或最低的物品的,其方法是:
~~~
mysql> SELECT @min_price:=MIN(price),@max_price:=MAX(price) FROM shop;
mysql> SELECT * FROM shop WHERE price=@min_price OR price=@max_price;
+---------+--------+-------+
| article | dealer | price |
+---------+--------+-------+
| 0003 | D | 1.25 |
| 0004 | D | 19.95 |
+---------+--------+-------+
~~~
### 3.6.6.?使用外鍵
在MySQL中,InnoDB表支持對外部關鍵字約束條件的檢查。參見[15.2節,“InnoDB存儲引擎”](# "15.2.?The InnoDB Storage Engine")。還可以參見 [ 1.8.5.5節,“外鍵”](# "1.8.5.5.?Foreign Keys")。
只是聯接兩個表時,不需要外部關鍵字。對于除InnoDB類型的表,當使用REFERENCES *tbl_name*(*col_name*)子句定義列時可以使用外部關鍵字,該子句沒有實際的效果,*只作為備忘錄或注釋來提醒,你目前正定義的列指向另一個表中的一個列。*執行該語句時,實現下面很重要:
·???????? MySQL不執行表*tbl_name*中的動作,例如作為你正定義的表中的行的動作的響應而刪除行;換句話說,該句法不會致使ON DELETE或ON UPDATE行為(如果你在REFERENCES子句中寫入ON DELETE或ON UPDATE子句,將被忽略)。
·???????? 該句法可以創建一個*column*;但不創建任何索引或關鍵字。
·???????? 如果用該句法定義InnoDB表,將會導致錯誤。
你可以使用作為聯接列創建的列,如下所示:
~~~
CREATE TABLE person (
~~~
~~~
??? id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
~~~
~~~
??? name CHAR(60) NOT NULL,
~~~
~~~
??? PRIMARY KEY (id)
~~~
~~~
);
~~~
~~~
?
~~~
~~~
CREATE TABLE shirt (
~~~
~~~
??? id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
~~~
~~~
??? style ENUM('t-shirt', 'polo', 'dress') NOT NULL,
~~~
~~~
??? color ENUM('red', 'blue', 'orange', 'white', 'black') NOT NULL,
~~~
~~~
??? owner SMALLINT UNSIGNED NOT NULL REFERENCES person(id),
~~~
~~~
??? PRIMARY KEY (id)
~~~
~~~
);
~~~
~~~
?
~~~
~~~
INSERT INTO person VALUES (NULL, 'Antonio Paz');
~~~
~~~
?
~~~
~~~
SELECT @last := LAST_INSERT_ID();
~~~
~~~
?
~~~
~~~
INSERT INTO shirt VALUES
~~~
~~~
(NULL, 'polo', 'blue', @last),
~~~
~~~
(NULL, 'dress', 'white', @last),
~~~
~~~
(NULL, 't-shirt', 'blue', @last);
~~~
~~~
?
~~~
~~~
INSERT INTO person VALUES (NULL, 'Lilliana Angelovska');
~~~
~~~
?
~~~
~~~
SELECT @last := LAST_INSERT_ID();
~~~
~~~
?
~~~
~~~
INSERT INTO shirt VALUES
~~~
~~~
(NULL, 'dress', 'orange', @last),
~~~
~~~
(NULL, 'polo', 'red', @last),
~~~
~~~
(NULL, 'dress', 'blue', @last),
~~~
~~~
(NULL, 't-shirt', 'white', @last);
~~~
~~~
?
~~~
~~~
SELECT * FROM person;
~~~
~~~
+----+---------------------+
~~~
~~~
| id | name??????????????? |
~~~
~~~
+----+---------------------+
~~~
~~~
|? 1 | Antonio Paz???????? |
~~~
~~~
|? 2 | Lilliana Angelovska |
~~~
~~~
+----+---------------------+
~~~
~~~
?
~~~
~~~
SELECT * FROM shirt;
~~~
~~~
+----+---------+--------+-------+
~~~
~~~
| id | style?? | color? | owner |
~~~
~~~
+----+---------+--------+-------+
~~~
~~~
|? 1 | polo??? | blue?? |???? 1 |
~~~
~~~
|? 2 | dress?? | white? |???? 1 |
~~~
~~~
|? 3 | t-shirt | blue?? |???? 1 |
~~~
~~~
|? 4 | dress?? | orange |???? 2 |
~~~
~~~
|? 5 | polo??? | red??? |???? 2 |
~~~
~~~
|? 6 | dress?? | blue?? |???? 2 |
~~~
~~~
|? 7 | t-shirt | white? |???? 2 |
~~~
~~~
+----+---------+--------+-------+
~~~
~~~
?
~~~
~~~
?
~~~
~~~
SELECT s.* FROM person p, shirt s
~~~
~~~
WHERE p.name LIKE 'Lilliana%'
~~~
~~~
?? AND s.owner = p.id
~~~
~~~
?? AND s.color <> 'white';
~~~
~~~
?
~~~
~~~
+----+-------+--------+-------+
~~~
~~~
| id | style | color? | owner |
~~~
~~~
+----+-------+--------+-------+
~~~
~~~
|? 4 | dress | orange |???? 2 |
~~~
~~~
|? 5 | polo? | red??? |???? 2 |
~~~
~~~
|? 6 | dress | blue?? |???? 2 |
~~~
~~~
+----+-------+--------+-------+
~~~
按照這種方式使用,REFERENCES子句不會顯示在SHOW CREATE TABLE或DESCRIBE的輸出中:
~~~
SHOW CREATE TABLE shirt\G
~~~
~~~
*************************** 1. row ***************************
~~~
~~~
Table: shirt
~~~
~~~
Create Table: CREATE TABLE `shirt` (
~~~
~~~
`id` smallint(5) unsigned NOT NULL auto_increment,
~~~
~~~
`style` enum('t-shirt','polo','dress') NOT NULL,
~~~
~~~
`color` enum('red','blue','orange','white','black') NOT NULL,
~~~
~~~
`owner` smallint(5) unsigned NOT NULL,
~~~
~~~
PRIMARY KEY? (`id`)
~~~
~~~
) ENGINE=MyISAM DEFAULT CHARSET=latin1
~~~
在列定義中,按這種方式使用REFERENCES作為注釋或“提示”適用于表MyISAM和BerkeleyDB。
### 3.6.7.?根據兩個鍵搜索
可以充分利用使用單關鍵字的OR子句,如同AND的處理。
一個比較靈活的例子是尋找兩個通過OR組合到一起的關鍵字:
~~~
SELECT field1_index, field2_index FROM test_table
~~~
~~~
WHERE field1_index = '1' OR? field2_index = '1'
~~~
該情形是已經優化過的。參見[7.2.6節,“索引合并優化”](# "7.2.6.?Index Merge Optimization")。
還可以使用UNION將兩個單獨的SELECT語句的輸出合成到一起來更有效地解決該問題。參見[13.2.7.2節,“UNION語法
”](# "13.2.7.2.?UNION Syntax")。
每個SELECT只搜索一個關鍵字,可以進行優化:
~~~
SELECT field1_index, field2_index
~~~
~~~
??? FROM test_table WHERE field1_index = '1'
~~~
~~~
UNION
~~~
~~~
SELECT field1_index, field2_index
~~~
~~~
? ??FROM test_table WHERE field2_index = '1';
~~~
### 3.6.8.?根據天計算訪問量
下面的例子顯示了如何使用位組函數來計算每個月中用戶訪問網頁的天數。
~~~
CREATE TABLE t1 (year YEAR(4), month INT(2) UNSIGNED ZEROFILL,
~~~
~~~
???????????? day INT(2) UNSIGNED ZEROFILL);
~~~
~~~
INSERT INTO t1 VALUES(2000,1,1),(2000,1,20),(2000,1,30),(2000,2,2),
~~~
~~~
??????????? (2000,2,23),(2000,2,23);
~~~
示例表中含有代表用戶訪問網頁的年-月-日值。可以使用以下查詢來確定每個月的訪問天數:
~~~
SELECT year,month,BIT_COUNT(BIT_OR(1<<day)) AS days FROM t1
~~~
~~~
?????? GROUP BY year,month;
~~~
將返回:
~~~
+------+-------+------+
~~~
~~~
| year | month | days |
~~~
~~~
+------+-------+------+
~~~
~~~
| 2000 |??? 01 |??? 3 |
~~~
~~~
| 2000 |??? 02 |??? 2 |
~~~
~~~
+------+-------+------+
~~~
該查詢計算了在表中按年/月組合的不同天數,可以自動去除重復的詢問。
### 3.6.9.?使用AUTO_INCREMENT
可以通過AUTO_INCREMENT屬性為新的行產生唯一的標識:
~~~
CREATE TABLE animals (
~~~
~~~
???? id MEDIUMINT NOT NULL AUTO_INCREMENT,
~~~
~~~
???? name CHAR(30) NOT NULL,
~~~
~~~
???? PRIMARY KEY (id)
~~~
~~~
);
~~~
~~~
?
~~~
~~~
INSERT INTO animals (name) VALUES
~~~
~~~
????('dog'),('cat'),('penguin'),
~~~
~~~
??? ('lax'),('whale'),('ostrich');
~~~
~~~
?
~~~
~~~
SELECT * FROM animals;
~~~
將返回:
~~~
+----+---------+
~~~
~~~
| id | name??? |
~~~
~~~
+----+---------+
~~~
~~~
|? 1 | dog???? |
~~~
~~~
|? 2 | cat???? |
~~~
~~~
|? 3 | penguin |
~~~
~~~
|? 4 | lax???? |
~~~
~~~
|? 5 | whale?? |
~~~
~~~
|? 6 | ostrich |
~~~
~~~
+----+---------+
~~~
你可以使用LAST_INSERT_ID()SQL函數或mysql_insert_id() C API函數來查詢最新的AUTO_INCREMENT值。這些函數與具體連接有關,因此其返回值不會被其它執行插入功能的連接影響。
**注釋:**對于多行插入,LAST_INSERT_ID()和mysql_insert_id()從插入的第一行實際返回AUTO_INCREMENT關鍵字。在復制設置中,通過該函數可以在其它服務器上正確復制多行插入。
對于MyISAM和BDB表,你可以在第二欄指定AUTO_INCREMENT以及多列索引。此時,AUTO_INCREMENT列生成的值的計算方法為:MAX(*auto_increment_column*) + 1 WHERE prefix=*given-prefix*。如果想要將數據放入到排序的組中可以使用該方法。
~~~
CREATE TABLE animals (
~~~
~~~
??? grp ENUM('fish','mammal','bird') NOT NULL,
~~~
~~~
??? id MEDIUMINT NOT NULL AUTO_INCREMENT,
~~~
~~~
??? name CHAR(30) NOT NULL,
~~~
~~~
??? PRIMARY KEY (grp,id)
~~~
~~~
);
~~~
~~~
?
~~~
~~~
INSERT INTO animals (grp,name) VALUES
~~~
~~~
????('mammal','dog'),('mammal','cat'),
~~~
~~~
??? ('bird','penguin'),('fish','lax'),('mammal','whale'),
~~~
~~~
??? ('bird','ostrich');
~~~
~~~
?
~~~
~~~
SELECT * FROM animals ORDER BY grp,id;
~~~
將返回:
~~~
+--------+----+---------+
~~~
~~~
| grp??? | id | name??? |
~~~
~~~
+--------+----+---------+
~~~
~~~
| fish?? |? 1 | lax???? |
~~~
~~~
| mammal |? 1 | dog???? |
~~~
~~~
| mammal |? 2 | cat???? |
~~~
~~~
| mammal |? 3 | whale?? |
~~~
~~~
| bird?? |? 1 | penguin |
~~~
~~~
| bird?? |? 2 | ostrich |
~~~
~~~
+--------+----+---------+
~~~
請注意在這種情況下(AUTO_INCREMENT列是多列索引的一部分),如果你在任何組中刪除有最大AUTO_INCREMENT值的行,將會重新用到AUTO_INCREMENT值。對于MyISAM表也如此,對于該表一般不重復使用AUTO_INCREMENT值。
如果AUTO_INCREMENT列是多索引的一部分,MySQL將使用該索引生成以AUTO_INCREMENT列開始的序列值。。例如,如果animals表含有索引PRIMARY KEY (grp, id)和INDEX(id),MySQL生成序列值時將忽略PRIMARY KEY。結果是,該表包含一個單個的序列,而不是符合grp值的序列。
要想以AUTO_INCREMENT值開始而不是1,你可以通過CREATE TABLE或ALTER TABLE來設置該值,如下所示:
~~~
mysql> ALTER TABLE tbl AUTO_INCREMENT = 100;
~~~
關于AUTO_INCREMENT的詳細信息:
·???????? 如何為列指定AUTO_INCREMENT屬性:[13.1.5節,“CREATE TABLE語法”](# "13.1.5.?CREATE TABLE Syntax")和 [ 13.1.2節,“ALTER TABLE語法”](# "13.1.2.?ALTER TABLE Syntax")。
·???????? AUTO_INCREMENT的動作取決于SQL模式:[5.3.2節,“SQL服務器模式”](# "5.3.2.?The Server SQL Mode")。
·???????? 找出含有最新AUTO_INCREMENT值的行:[12.1.3節,“比較函數和操作符”](# "12.1.3.?Comparison Functions and Operators")。
·???????? 設置將用到的AUTO_INCREMENT值: [ 13.5.3節,“SET語法”](# "13.5.3.?SET Syntax")。
·???????? AUTO_INCREMENT和復制:[6.7節,“復制特性和已知問題”](# "6.7.?Replication Features and Known Problems").
·???????? AUTO_INCREMENT相關的可用于復制的Server-system變量(auto_increment_increment和auto_increment_offset):[5.3.3節,“服務器系統變量”](# "5.3.3.?Server System Variables")。
### 3.7.?孿生項目的查詢
[ 3.7.1. 查找所有未分發的孿生項](#)[ 3.7.2. 顯示孿生對狀態的表](#)
這個項目是Institute of Environmental Medicine at Karolinska Institutet Stockholm 和 the Section on Clinical Research in Aging and Psychology at the University of Southern California的合作項目。
該項目包括篩選部分,即通過電話回訪在瑞典超過 65 歲的所有孿生。滿足某種標準的孿生進入下一階段。在下一階段中,醫生/護士小組將訪問想參加的孿生。部分檢查包括物理檢查和神經、心理檢查、實驗室試驗、神經成像、心理狀況評估和家族歷史搜集。并且,應根據醫療和環境風險因素來搜集數據。
可從以下鏈接找到孿生研究的更多信息:
[ http://www.mep.ki.se/twinreg/index_en.html](http://www.mep.ki.se/twinreg/index_en.html)
用一個用Perl和**MySQL**編寫的web接口來管理項目的后面部分。
每天晚上所有會談的數據被移入一個**MySQL**數據庫。
### 3.7.1.?查找所有未分發的孿生項
下列查詢用來決定誰進入項目的第二部分:
~~~
SELECT
CONCAT(p1.id, p1.tvab) + 0 AS tvid,
CONCAT(p1.christian_name, ' ', p1.surname) AS Name,
p1.postal_code AS Code,
p1.city AS City,
pg.abrev AS Area,
IF(td.participation = 'Aborted', 'A', ' ') AS A,
p1.dead AS dead1,
l.event AS event1,
td.suspect AS tsuspect1,
id.suspect AS isuspect1,
td.severe AS tsevere1,
id.severe AS isevere1,
p2.dead AS dead2,
l2.event AS event2,
h2.nurse AS nurse2,
h2.doctor AS doctor2,
td2.suspect AS tsuspect2,
id2.suspect AS isuspect2,
td2.severe AS tsevere2,
id2.severe AS isevere2,
l.finish_date
FROM
twin_project AS tp
/* For Twin 1 */
LEFT JOIN twin_data AS td ON tp.id = td.id
AND tp.tvab = td.tvab
LEFT JOIN informant_data AS id ON tp.id = id.id
AND tp.tvab = id.tvab
LEFT JOIN harmony AS h ON tp.id = h.id
AND tp.tvab = h.tvab
LEFT JOIN lentus AS l ON tp.id = l.id
AND tp.tvab = l.tvab
/* For Twin 2 */
LEFT JOIN twin_data AS td2 ON p2.id = td2.id
AND p2.tvab = td2.tvab
LEFT JOIN informant_data AS id2 ON p2.id = id2.id
AND p2.tvab = id2.tvab
LEFT JOIN harmony AS h2 ON p2.id = h2.id
AND p2.tvab = h2.tvab
LEFT JOIN lentus AS l2 ON p2.id = l2.id
AND p2.tvab = l2.tvab,
person_data AS p1,
person_data AS p2,
postal_groups AS pg
WHERE
/* p1 gets main twin and p2 gets his/her twin. */
/* ptvab is a field inverted from tvab */
p1.id = tp.id AND p1.tvab = tp.tvab AND
p2.id = p1.id AND p2.ptvab = p1.tvab AND
/* Just the screening survey */
tp.survey_no = 5 AND
/* Skip if partner died before 65 but allow emigration (dead=9) */
(p2.dead = 0 OR p2.dead = 9 OR
(p2.dead = 1 AND
(p2.death_date = 0 OR
(((TO_DAYS(p2.death_date) - TO_DAYS(p2.birthday)) / 365)
>= 65))))
AND
(
/* Twin is suspect */
(td.future_contact = 'Yes' AND td.suspect = 2) OR
/* Twin is suspect - Informant is Blessed */
(td.future_contact = 'Yes' AND td.suspect = 1
AND id.suspect = 1) OR
/* No twin - Informant is Blessed */
(ISNULL(td.suspect) AND id.suspect = 1
AND id.future_contact = 'Yes') OR
/* Twin broken off - Informant is Blessed */
(td.participation = 'Aborted'
AND id.suspect = 1 AND id.future_contact = 'Yes') OR
/* Twin broken off - No inform - Have partner */
(td.participation = 'Aborted' AND ISNULL(id.suspect)
AND p2.dead = 0))
AND
l.event = 'Finished'
/* Get at area code */
AND SUBSTRING(p1.postal_code, 1, 2) = pg.code
/* Not already distributed */
AND (h.nurse IS NULL OR h.nurse=00 OR h.doctor=00)
/* Has not refused or been aborted */
AND NOT (h.status = 'Refused' OR h.status = 'Aborted'
OR h.status = 'Died' OR h.status = 'Other')
ORDER BY
tvid;
~~~
一些解釋:
·???????? CONCAT(p1.id, p1.tvab) + 0 AS tvid
我們想要在id和tvab的連接上以數字順序排序。結果加0使得**MySQL**把結果變為一個數字。
·???????? 列id
這標識一對孿生。它是所有表中的一個鍵。
·???????? 列tvab
這標識孿生中的一個。它的值為1或2。
·???????? 列ptvab
這是tvab的一個逆運算。當tvab是1,它是2,反之亦然。它用來保存輸入并且使**MySQL的**優化查詢更容易。
這個查詢表明,怎樣用聯結(p1和p2)從同一個表中查找表。在例子中,這被用來檢查孿生的一個是否在65歲前死了。如果如此,行不返回值。
上述所有孿生信息存在于所有表中。我們對id,tvab(所有表)和id,ptvab (person_data) 上采用鍵以使查詢更快。
在我們的生產機器上(一臺200MHz UltraSPARC),這個查詢返回大約 150-200 行并且時間不超過一秒。
<table border="1" cellpadding="0" id="table5"><tr><td width="236"> <p><strong><span>表</span></strong></p></td> <td width="153"> <p><strong><span>行數</span></strong></p></td> </tr><tr><td width="236"> <p> <span>person_data</span></p></td> <td width="153"> <p><span>71074</span></p></td> </tr><tr><td width="236"> <p> <span>lentus</span></p></td> <td width="153"> <p><span>5291</span></p></td> </tr><tr><td width="236"> <p> <span>twin_project</span></p></td> <td width="153"> <p><span>5286</span></p></td> </tr><tr><td width="236"> <p> <span>twin_data</span></p></td> <td width="153"> <p><span>2012</span></p></td> </tr><tr><td width="236"> <p> <span>informant_data</span></p></td> <td width="153"> <p><span>663</span></p></td> </tr><tr><td width="236"> <p> <span>harmony</span></p></td> <td width="153"> <p><span>381</span></p></td> </tr><tr><td width="236"> <p> <span>postal_groups</span></p></td> <td width="153"> <p><span>100</span></p></td> </tr></table>
### 3.7.2.?顯示孿生對狀態的表
每一次會面以一個稱為event的狀態碼結束。下面顯示的查詢被用來顯示按事件組合的所有孿生的表。這表明多少對孿生已經完成,多少對的其中之一已完成而另一個拒絕了,等等。
~~~
SELECT
~~~
~~~
??????? t1.event,
~~~
~~~
??????? t2.event,
~~~
~~~
??????? COUNT(*)
~~~
~~~
FROM
~~~
~~~
??????? lentus AS t1,
~~~
~~~
??????? lentus AS t2,
~~~
~~~
??????? twin_project AS tp
~~~
~~~
WHERE
~~~
~~~
??????? /* We are looking at one pair at a time */
~~~
~~~
??????? t1.id = tp.id
~~~
~~~
??????? AND t1.tvab=tp.tvab
~~~
~~~
??????? AND t1.id = t2.id
~~~
~~~
??????? /* Just the screening survey */
~~~
~~~
??????? AND tp.survey_no = 5
~~~
~~~
??????? /* This makes each pair only appear once */
~~~
~~~
??????? AND t1.tvab='1' AND t2.tvab='2'
~~~
~~~
GROUP BY
~~~
~~~
??????? t1.event, t2.event;
~~~
### 3.8.?與Apache一起使用MySQL
還有一些項目,你可以從MySQL數據庫鑒別用戶,并且你還可以將日志文件寫入MySQL數據庫表。
你可以將以下內容放到Apache配置文件中,更改Apache日志格式,使MySQL更容易讀取:
~~~
LogFormat \
~~~
~~~
??????? "\"%h\",%{%Y%m%d%H%M%S}t,%>s,\"%b\",\"%{Content-Type}o\",? \
~~~
~~~
??????? \"%U\",\"%{Referer}i\",\"%{User-Agent}i\""
~~~
要想將該格式的日志文件裝載到MySQL,你可以使用以下語句:
~~~
LOAD DATA INFILE '/local/access_log' INTO TABLE tbl_name
~~~
~~~
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' ESCAPED BY '\\'
~~~
所創建的表中的列應與寫入日志文件的LogFormat行對應。
這是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許可例外
- 索引