实现有价值的IT服务
全部博文(709)
分类: Mysql/postgreSQL
2006-06-29 14:29:32
本章通過演示如何使用mysql
客戶程序創造和使用一個簡單的數據庫,提供一個MySQL的入門教程。mysql
(有時稱為“終端監視器”或只是“監視”)是一個交互式程序,允許你連接一個MySQL服務器,運行查詢並察看結果。mysql
可以用于批模式:你預先把查詢放在一個文件中,然後告訴mysql
執行文件的內容。使用mysql
的兩個方法都在這里涉及。
為了看清由mysql
提供的一個選擇項目表了,用--help
選項調用它:
shell> mysql --help
本章假定mysql
已經被安裝在你的機器上,並且有一個MySQL服務器你可以連接。如果這不是真的,聯絡你的MySQL管理員。(如果你是管理員,你將需要請教這本手冊的其他章節。)
本章描述建立和使用一個數據庫的全過程。如果你僅僅對存取一個已經存在數據庫感興趣,你可能想要跳過描述怎樣創建數據庫及它所包含的表的章節。
既然本章本質上是一個教程,許多細節有必要被省略。對于這里所涉及的話題的更多信息,咨詢本手冊的相關章節。
為了連接服務器,當你調用mysql
時,你通常將需要提供一個MySQL用戶名和很可能,一個口令。如果服務器運行在不是你登錄的一台機器上,你也將需要指定主機名。聯系你的管理員以找出你應該使用什麼連接參數進行連接(即,那個主機,用戶名字和使用的口令)。一旦你知道正確的參數,你應該能象這樣連接:
shell> mysql -h host -u user -p Enter password: ********
********
代表你的口令;當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 459 to server version: 3.22.20a-log Type 'help' for help. mysql>
提示符告訴你mysql
准備為你輸入命令。
一些MySQL安裝允許用戶以“anoymous”(匿名)用戶連接在本地主機上運行的服務器。如果在你的機器是這種情況,你應該能通過沒有任何選項地調用mysql
與該服務器連接:
shell> mysql
在你成功地連接後,你可以在mysql>
提示下打入QUIT
隨時斷開:
mysql> QUIT Bye
你也可以鍵入control-D斷開。
在下列章節的大多數例子都假設你連接到服務器。由mysql>
提示指明他們。
確保你連接上了服務器,如在先前的章節討論的。這樣做本身將不選擇任何數據庫來工作,但是那很好。從這點講,知道關于如何出詢問的一點知識,比馬上跳至創建表、給他們裝載數據並且從他們檢索數據要來的重要寫。本節描述輸入命令的基本原則,使用幾個查詢,你能嘗試讓自己mysql
是如何工作的。
這是一個簡單的命令,要求服務器告訴你它的版本號和當前日期。在mysql>
提示打入如下命令並按回車鍵:
mysql> SELECT VERSION(), CURRENT_DATE; +--------------+--------------+ | version() | CURRENT_DATE | +--------------+--------------+ | 3.22.20a-log | 1999-03-19 | +--------------+--------------+ 1 row in set (0.01 sec) mysql>
這詢問說明關于mysql
幾件事:
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.707107 | 25 | +-------------+---------+
至今顯示的命令是相當短的,單行語句。你甚至能在單行上輸入多條語句,只是以一個分號結束每一條:
mysql> SELECT VERSION(); SELECT NOW(); +--------------+ | version() | +--------------+ | 3.22.20a-log | +--------------+ +---------------------+ | NOW() | +---------------------+ | 1999-03-19 00:15:33 | +---------------------+
一個命令不必全在一個單獨行給出,所以需要多行的較長命令不是一個問題。mysql
通過尋找終止的分號而不是尋找輸入行的結束來決定你的語句在哪兒結束。(換句話說,mysql
接受自由格式輸入:它收集輸入行但執行他們直到它看見分號。)
這里是一個簡單的多行語句的例子:
mysql> SELECT -> USER() -> , -> CURRENT_DATE; +--------------------+--------------+ | USER() | CURRENT_DATE | +--------------------+--------------+ | joesmith@localhost | 1999-03-18 | +--------------------+--------------+
在這個例子中,在你輸入一個多行查詢的第一行後,要注意提示符如何從mysql>
變為->
,這正是mysql
如何指出它沒見到完整的語句並且正在等待剩余的部分。提示符是你的朋友,因為它提供有價值的反饋,如果你使用該反饋,你將總是知道mysql
正在等待什麼。
如果你決定,你不想要執行你在輸入過程中輸入的一個命令,打入\c
取消它:
mysql> SELECT -> USER() -> \c mysql>
這里也要注意提示符,在你打入\c
以後,它切換回到mysql>
,提供反饋以表明mysql
准備接受一個新命令。
下表顯示出你可以看見的各個提示符並總結他們意味著mysql
在什麼狀態下:
提示符 | 意思 |
mysql> |
准備好接受新命令 |
-> |
等待多行命令的下一行 |
'> |
等待下一行,收集以單引號(“'”)開始的字符串 |
"> |
等待下一行,收集以雙引號(“"”)開始的字符串 |
當你打算在一個單行上發出一個命令時,多行語句通常“偶然”出現,但是忘記終止的分號。在這種情況中,mysql
等待進一步輸入:
mysql> SELECT USER() ->
如果這發生在你身上(你認為你輸完了語句但是唯一的反應是一個->
提示符),很可能mysql
正在等待分號。如果你沒有注意到提示符正在告訴你什麼,在認識到你需要做什麼之前,你可能花一會兒時間呆坐在那兒。進入一個分號完成語句,並且mysql
將執行它:
mysql> SELECT USER() -> ; +--------------------+ | USER() | +--------------------+ | joesmith@localhost | +--------------------+
'>
和">
提示符出現在在字符串收集期間。在MySQL中,你可以寫由“'”或“"”字符括起來的字符串 (例如,'hello'
或"goodbye"
),並且mysql
讓你進入跨越多行的字符串。當你看到一個'>
或">
提示符時,這意味著你已經輸入了包含以“'”或“"”括號字符開始的字符串的一行,但是還沒有輸入終止字符串的匹配引號。如果你確實正在輸入一個多行字符串,很好,但是果真如此嗎?不盡然。更常見的,'>
和">
提示符顯示你粗心地省掉了一個引號字符。例如:
mysql> SELECT * FROM my_table WHERE name = "Smith AND age < 30; ">
如果你輸入該SELECT
語句,然後按回車鍵並等待結果,什麼都沒有出現。不要驚訝,“為什麼該查詢這麼長呢?”,注意">
提示符提供的線索。它告訴你mysql
期望見到一個未終止字符串的余下部分。(你在語句中看見錯誤嗎?字符串"Smith
正好丟失第二個引號。)
走到這一步,你該做什麼?最簡單的是取消命令。然而,在這種情況下,你不能只是打入\c
,因為mysql
作為它正在收集的字符串的一部分來解釋它!相反,輸入關閉的引號字符(這樣mysql
知道你完成了字符串),然後打入\c
:
mysql> SELECT * FROM my_table WHERE name = "Smith AND age < 30; "> "\c mysql>
提示符回到mysql>
,顯示mysql
准備好接受一個新命令了。
知道'>
和">
提示符意味著什麼是很重要的,因為如果你錯誤地輸入一個未終止的字符串,任何比你下一步輸入的行好象將要被mysql
忽略--包括包含QUIT
的行!這可能相當含糊,特別是在你能取消當前命令前,如果你不知道你需要提出終止引號。
下面是一些學習如何用MySQL解決一些常見問題的例子。
一些例子使用數據庫表“shop”,包含某個商人的每篇文章(物品號)的價格。假定每個商人的每篇文章有一個單獨的固定價格,那麼(物品,商人)是記錄的主鍵。
你能這樣創建例子數據庫表:
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)); 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);
好了,例子數據是這樣的:
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 | +---------+--------+-------+
“最大的物品號是什麼?”
SELECT MAX(article) AS article FROM shop +---------+ | article | +---------+ | 4 | +---------+
“找出最貴的文章的編號、商人和價格”
在ANSI-SQL中這很容易用一個子查詢做到:
SELECT article, dealer, price FROM shop WHERE price=(SELECT MAX(price) FROM shop)
在MySQL中(還沒有子查詢)就用2步做到:
SELECT
語句從表中得到最大值。
SELECT article, dealer, price FROM shop WHERE price=19.95
另一個解決方案是按價格降序排序所有行並用MySQL特定LIMIT
子句只得到的第一行:
SELECT article, dealer, price FROM shop ORDER BY price DESC LIMIT 1
注意:如果有多個最貴的文章( 例如每個19.95),LIMIT
解決方案僅僅顯示他們之一!
“每篇文章的最高的價格是什麼?”
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 | +---------+-------+
“對每篇文章,找出有最貴的價格的交易者。”
在ANSI SQL
中,我可以用這樣一個子查詢做到:
SELECT article, dealer, price FROM shop s1 WHERE price=(SELECT MAX(s2.price) FROM shop s2 WHERE s1.article = s2.article)
在MySQL中,最好是分幾步做到:
這可以很容易用一個臨時表做到:
CREATE TEMPORARY TABLE tmp ( article INT(4) UNSIGNED ZEROFILL DEFAULT '0000' NOT NULL, price DOUBLE(16,2) DEFAULT '0.00' NOT NULL); LOCK TABLES article read; INSERT INTO tmp SELECT article, MAX(price) FROM shop GROUP BY article; SELECT article, dealer, price FROM shop, tmp WHERE shop.article=tmp.articel AND shop.price=tmp.price; UNLOCK TABLES; DROP TABLE tmp;
如果你不使用一個TEMPORARY
表,你也必須鎖定“tmp”表。
“它能一個單個查詢做到嗎?”
是的,但是只有使用我稱之為“MAX-CONCAT詭計”的一個相當低效的詭計:
SELECT article, SUBSTRING( MAX( CONCAT(LPAD(price,6,'0'),dealer) ), 7) AS dealer, 0.00+LEFT( MAX( CONCAT(LPAD(price,6,'0'),dealer) ), 6) AS price FROM shop GROUP BY article; +---------+--------+-------+ | article | dealer | price | +---------+--------+-------+ | 0001 | B | 3.99 | | 0002 | A | 10.99 | | 0003 | C | 1.69 | | 0004 | D | 19.95 | +---------+--------+-------+
最後例子當然能通過在客戶程序中分割連結的列使它更有效一點。
不需要外鍵聯結2個表。
MySQL
唯一不做的事情是CHECK
以保証你使用的鍵確實在你正在引用表中存在,並且它不自動從有一個外鍵定義的表中刪除行。如果你象平常那樣使用你的鍵值,它將工作得很好!
CREATE TABLE persons ( id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, name CHAR(60) NOT NULL, PRIMARY KEY (id) ); CREATE TABLE shirts ( 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 persons, PRIMARY KEY (id) ); INSERT INTO persons VALUES (NULL, 'Antonio Paz'); INSERT INTO shirts VALUES (NULL, 'polo', 'blue', LAST_INSERT_ID()), (NULL, 'dress', 'white', LAST_INSERT_ID()), (NULL, 't-shirt', 'blue', LAST_INSERT_ID()); INSERT INTO persons VALUES (NULL, 'Lilliana Angelovska'); INSERT INTO shirts VALUES (NULL, 'dress', 'orange', LAST_INSERT_ID()), (NULL, 'polo', 'red', LAST_INSERT_ID()), (NULL, 'dress', 'blue', LAST_INSERT_ID()), (NULL, 't-shirt', 'white', LAST_INSERT_ID()); SELECT * FROM persons; +----+---------------------+ | id | name | +----+---------------------+ | 1 | Antonio Paz | | 2 | Lilliana Angelovska | +----+---------------------+ SELECT * FROM shirts; +----+---------+--------+-------+ | 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 persons p, shirts 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 | +----+-------+--------+-------+
既然你知道怎樣輸入命令,現在是存取一個數據庫的時候了。
假定在你的家(你的“動物園”)中有很多寵物,並且你想追蹤關于他們各種各樣類型的信息。你可以通過創建表來保存你的數據並根據所需要的信息裝載他們做到,然後你可以通過從表中檢索數據來回答關于你的動物不同種類的問題。本節顯示如何做到所有這些事情:
動物園數據庫將會是簡單的(故意的),但是不難把它想象成可能用到相似類型數據庫的真實世界情況。例如,這樣的一個數據庫能被一個農夫用來追蹤家畜,或由一個獸醫追蹤病畜記錄。
使用SHOW
語句找出在服務器上當前存在什麼數據庫:
mysql> SHOW DATABASES; +----------+ | Database | +----------+ | mysql | | test | | tmp | +----------+
數據庫列表可能在你的機器上是不同的,但是mysql
和test
數據庫很可能的在其間。mysql
是必需的,因為它描述用戶存取權限,test
數據庫經常作為一個工作區提供給用戶試試身手。
如果test
數據庫存在,嘗試存取它:
mysql> USE test Database changed
注意,USE
,類似QUIT
,不需要一個分號。(如果你喜歡,你可以用一個分號終止這樣的語句;這無礙)USE
語句在使用上也有另外一個特殊的地方:它必須在一個單行上給出。
你可列在後面的例子中使用test
數據庫(如果你能訪問它),但是你在該數據庫創建的任何東西可以被與訪問它的其他人刪除,為了這個原因,你可能應該詢問你的MySQL管理員許可你自己使用的一個數據庫。假定你想要調用你的menagerie
,管理員需要執行一個這樣的命令:
mysql> GRANT ALL ON menagerie.* TO your_mysql_name;
這里your_mysql_name
是分配給你的MySQL用戶名。
如果在設置你的權限時,管理員為你創建了數據庫,你可以開始使用它。否則,你需要自己創建它:
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
)。然而,不建議把你的口令放在命令行上,因為這樣做把它暴露出來,能被在你的機器上登錄的其他用戶窺探到。
創建數據庫是容易的部分,但是在這時它是空的,正如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
到255
的任何長度,無論哪個對你來說好象最合理。(如果你做了較差的選擇,以後會變得你需要一個更長的字段,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
,例如,如果你忘記在你表中的列的名字或他們是什麼類型。
在你創建表後,你需要充實它。LOAD DATA
和INSERT
語句用于此。
假定你的寵物紀錄描述如下。(觀察到MySQL期望日期時以YYYY-MM-DD
格式;這可能與你習慣的不同。)
name | owner | species | sex | birth | death |
Fluffy | Harold | cat | f | 1993-02-04 | |
Claws | Gwen | cat | m | 1994-03-17 | |
Buffy | Harold | dog | f | 1989-05-13 | |
Fang | Benny | dog | m | 1990-08-27 | |
Bowser | Diane | dog | m | 1998-08-31 | 1995-07-29 |
Chirpy | Gwen | bird | f | 1998-09-11 | |
Whistler | Gwen | bird | 1997-12-09 | ||
Slim | Benny | snake | m | 1996-04-29 |
因為你是從一張空表開始的,充實它的一個容易方法是創建包含為你的動物各一行一個文本文件,然後用一個單個語句裝載文件的內容到表中。
你可以創建一個文本文件“pet.txt”,每行包含一個記錄,用定位符(tab)把值分開,並且以在CREATE TABLE
語句中列出的列次序給出。對于丟失的值(例如未知的性別,或仍然活著的動物的死亡日期),你可以使用NULL
值。為了在你的文本文件表示這些,使用\N
。例如,對Whistler鳥的記錄看起來像這樣的(這里在值之間的空白是一個單個的定位字符):
Whistler |
Gwen |
bird |
\N |
1997-12-09 |
\N |
為了裝載文本文件“pet.txt”到pet
表中,使用這個命令:
mysql> LOAD DATA LOCAL INFILE "pet.txt" INTO TABLE pet;
如果你願意,你能明確地在LOAD DATA
語句中指出列值的分隔符和行尾標記,但是缺省是定位符和換行符。這些對爭取讀入文件“pet.txt”的語句是足夠的。
當你想要一次增加一個新記錄時,INSERT
語句是有用的。在它最簡單的形式,你為每一列提供值,以列在CREATE TABLE
語句被列出的順序。假定Diane把一只新倉鼠命名為Puffball,你可以使用一個這樣INSERT
語句增加一條新記錄:
mysql> INSERT INTO pet -> VALUES ('Puffball','Diane','hamster','f','1999-03-30',NULL);
注意,這里字符串和日期值被指定為引號擴起來的字符串。另外,用INSERT
,你能直接插入NULL
代表不存在的值。你不能使用\N
,就像你用LOAD DATA
做的那樣。
從這個例子,你應該能看到涉及很多的鍵入用多個INSERT
語句而非單個LOAD DATA
語句裝載你的初始記錄。
SELECT
語句被用來從一張桌子拉出信息。語句的一般格式是:
SELECT what_to_select FROM which_table WHERE conditions_to_satisfy
what_to_select
指出你想要看到的,這可以是列的一張表,或*
表明“所有的列”。which_table
指出你想要從其檢索數據的表。WHERE
子句是可選的,如果它在,conditions_to_satisfy
指定行必須滿足的檢索條件。
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 | 1998-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,而不是1998。
至少有一些修正它的方法:
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";
如上所示,檢索整個表是容易的,但是一般你不想那樣做,特別地當表變得很大時。相反,你通常對回答一個特別的問題更感興趣,在這種情況下你在你想要的信息上指定一些限制。讓我們看一些他們回答有關你寵物的問題的選擇查詢。
你能從你的表中只選擇特定的行。例如,如果你想要驗証你對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,而不是1998。
字符串比較通常是大小些無關的,因此你可以指定名字為"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
可以混用。如果你這樣做,使用括號指明條件應該如何被分組是一個好主意:
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 | +-------+--------+---------+------+------------+-------+
如果你不想要看到你的表的整個行,就命名你感興趣的列,用逗號分開。例如,如果你想要知道你的動物什麼時候出生的,精選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 | +--------+---------+------------+
你可能已經注意到前面的例子中結果行沒有以特定的次序被顯示。然而,當行以某個有意義的方式排序,檢驗查詢輸出通常是更容易的。為了排序結果,使用一個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 | +----------+------------+
為了以逆序排序,增加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
值仍然以升序被排序。
MySQL提供幾個函數,你能用來執行在日期上的計算,例如,計算年齡或提取日期的部分。
為了決定你的每個寵物有多大,用出生日期和當前日期之間的差別計算年齡。通過變換2個日期到天數,取差值,並且用365除(在一年里的天數):
mysql> SELECT name, (TO_DAYS(NOW())-TO_DAYS(birth))/365 FROM pet; +----------+-------------------------------------+ | name | (TO_DAYS(NOW())-TO_DAYS(birth))/365 | +----------+-------------------------------------+ | Fluffy | 6.15 | | Claws | 5.04 | | Buffy | 9.88 | | Fang | 8.59 | | Bowser | 9.58 | | Chirpy | 0.55 | | Whistler | 1.30 | | Slim | 2.92 | | Puffball | 0.00 | +----------+-------------------------------------+
盡管查詢可行,關于它還有能被改進的一些事情。首先,如果行以某個次序表示,其結果能更容易被掃描。第二,年齡列的標題不是很有意義的。
第一個問題通過增加一個ORDER BY name
子句按名字排序輸出來解決。為了處理列標題,為列提供一個名字以便一個不同的標簽出現在輸出中(這被稱為一個列別名):
mysql> SELECT name, (TO_DAYS(NOW())-TO_DAYS(birth))/365 AS age -> FROM pet ORDER BY name; +----------+------+ | name | age | +----------+------+ | Bowser | 9.58 | | Buffy | 9.88 | | Chirpy | 0.55 | | Claws | 5.04 | | Fang | 8.59 | | Fluffy | 6.15 | | Puffball | 0.00 | | Slim | 2.92 | | Whistler | 1.30 | +----------+------+
為了按age
而非name
排序輸出,只要使用一個不同ORDER BY
子句:
mysql> SELECT name, (TO_DAYS(NOW())-TO_DAYS(birth))/365 AS age -> FROM pet ORDER BY age; +----------+------+ | name | age | +----------+------+ | Puffball | 0.00 | | Chirpy | 0.55 | | Whistler | 1.30 | | Slim | 2.92 | | Claws | 5.04 | | Fluffy | 6.15 | | Fang | 8.59 | | Bowser | 9.58 | | Buffy | 9.88 | +----------+------+
一個類似的查詢可以被用來確定已經死亡動物的死亡年齡。你通過檢查death
值是否是NULL
來決定那些是哪些動物,然後,對于那些有非NULL
值,計算在death
和birth
值之間的差別:
mysql> SELECT name, birth, death, (TO_DAYS(death)-TO_DAYS(birth))/365 AS age -> FROM pet WHERE death IS NOT NULL ORDER BY age; +--------+------------+------------+------+ | name | birth | death | age | +--------+------------+------------+------+ | Bowser | 1989-08-31 | 1995-07-29 | 5.91 | +--------+------------+------------+------+
差詢使用death IS NOT NULL
而非death != NULL
,因為NULL
是特殊的值,這以後會解釋。見。
如果你想要知道哪個動物下個月過生日,怎麼辦?對于這類計算,年和天是無關的,你簡單地想要提取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(NOW(), INTERVAL 1 MONTH));
完成同樣任務的一個不同方法是加1
以得出當前月份的下一個月(在使用取模函數(MOD
)後,如果它當前是12
,則“繞回”月份到值0
):
mysql> SELECT name, birth FROM pet -> WHERE MONTH(birth) = MOD(MONTH(NOW()), 12) + 1;
注意,MONTH
返回在1和12之間的一個數字,且MOD(something,12)
返回在0和11之間的一個數字,因此必須在MOD()
以後加1,否則我們將從11月( 11 )跳到1月(1)。
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意味著假而1意味著真。
NULL
這樣特殊的處理是為什麼,在前面的章節中,為了決定哪個動物不再是活著的,使用death IS NOT NULL
而不是death != NULL
是必要的。
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
,它們是同義詞)。
擴展正則表達式的一些字符是:
為了說明擴展正則表達式如何工作,上面所示的LIKE
查詢在下面使用REGEXP
重寫:
為了找出以“b”開頭的名字,使用“^”匹配名字的開始並且“[bB]”匹配小寫或大寫的“b”:
mysql> SELECT * FROM pet WHERE name REGEXP "^[bB]"; +--------+--------+---------+------+------------+------------+ | 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 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”的名字,使用“[wW]”匹配小寫或大寫的“w”:
mysql> SELECT * FROM pet WHERE name REGEXP "[wW]"; +----------+-------+---------+------+------------+------------+ | 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 | +-------+--------+---------+------+------------+-------+
數據庫經常用于回答這個問題,“某個類型的數據在一張表中出現的頻度?”例如,你可能想要知道你有多少寵物,或每位主人有多少寵物,或你可能想要在你的動物上施行各種類型的普查。
計算你擁有動物的總數字與“在pet
表中有多少行?”是同樣的問題,因為每個寵物有一個記錄。COUNT()
函數計數非NULL
結果的數目,所以數你的動物的查詢看起來像這樣:
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(owner) FROM pet; ERROR 1140 at line 1: 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 | +---------+------+----------+
pet
表追蹤你有哪個寵物。如果你想要記錄他們的其他信息,例如在他們一生中事件看獸醫或何時後代出生,你需要另外的表。這張表應該像什麼呢?
給出了這些考慮,為event
表的CREATE TABLE
語句可能看起來像這樣:
mysql> CREATE TABLE event (name VARCHAR(20), date DATE, -> type VARCHAR(15), remark VARCHAR(255));
就象pet
表,最容易的示通過創建包含信息的一個定位符分隔的文本文件裝載初始記錄:
Fluffy | 1995-05-15 | litter | 4 kittens, 3 female, 1 male |
Buffy | 1993-06-23 | litter | 5 puppies, 2 female, 3 male |
Buffy | 1994-06-19 | litter | 3 puppies, 3 female |
Chirpy | 1999-03-21 | vet | needed beak straightened |
Slim | 1997-08-03 | vet | broken rib |
Bowser | 1991-10-12 | kennel | |
Fang | 1991-10-12 | kennel | |
Fang | 1998-08-28 | birthday | Gave him a new chew toy |
Claws | 1998-03-17 | birthday | Gave him a new flea collar |
Whistler | 1998-12-09 | birthday | First birthday |
象這樣裝載記錄:
mysql> LOAD DATA LOCAL INFILE "event.txt" INTO TABLE event;
基于你從已經運行在pet
表上的查詢中學到的,你應該能執行在event
表中記錄的檢索;原則是一樣的。但是什麼時候是event
表本身不足以回答你可能問的問題呢?
當他們有了一窩小動物時,假定你想要找出每只寵物的年齡。 event
表指出何時發生,但是為了計算母親的年齡,你需要她的出生日期。既然它被存儲在pet
表中,為了查詢你需要兩張表:
mysql> SELECT pet.name, (TO_DAYS(date) - TO_DAYS(birth))/365 AS age, remark -> FROM pet, event -> WHERE pet.name = event.name AND type = "litter"; +--------+------+-----------------------------+ | name | age | remark | +--------+------+-----------------------------+ | Fluffy | 2.27 | 4 kittens, 3 female, 1 male | | Buffy | 4.12 | 5 puppies, 2 female, 3 male | | Buffy | 5.10 | 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 | +--------+------+--------+------+---------+
在這個查詢中,我們為表名指定別名以便能引用列並且使得每一個列引用關聯于哪個表實例更直觀。
如果你忘記一個數據庫或表的名字,或一個給定的表的結構是什麼(例如,它的列叫什麼),怎麼辦? MySQL通過提供數據庫及其支持的表的信息的幾個語句解決這個問題。
你已經見到了SHOW DATABASES
,它列出由服務器管理的數據庫。為了找出當前選擇了哪個數據庫,使用DATABASE()
函數:
mysql> SELECT DATABASE(); +------------+ | DATABASE() | +------------+ | menagerie | +------------+
如果你還沒選擇任何數據庫,結果是空的。
為了找出當前的數據庫包含什麼表(例如,當你不能確定一個表的名字),使用這個命令:
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
生成有關它們的信息。
在前面的章節中,你交互式地使用mysql
輸入查詢並且查看結果。你也可以以批模式運行mysql
。為了做到這些,把你想要運行的命令放在一個文件中,然後告訴mysql
從文件讀取它的輸入:
shell> mysql < batch-file
如果你需要在命令行上指定連接參數,命令可能看起來像這樣:
shell> mysql -h host -u user -p < batch-file Enter password: ********
當你這樣使用mysql
時,你正在創建一個腳本文件,然後執行腳本。
為什麼要使用一個腳本?有很多原因:
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
。
在Analytikerna 和 Lentus,我們為一個大的研究項目工程一直在做系統和現場工作。這個項目是Institute of Environmental Medicine at Karolinska Institutet Stockholm 和 the Section on Clinical Research in Aging and Psychology at the University of Southern California的合作項目。
雙胞胎研究的更多信息可在下列鏈接找到:
項目的後面部分是用一個用Perl和MySQL編寫的web接口來管理。
每天晚上所有會談的數據被移入一個MySQL數據庫。
下列查詢用來決定誰進入項目的第二部分:
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 sceening 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 行並且不超過一秒的時間。
上面所用的表的當前記錄數是:
表 | 行數 |
person_data |
71074 |
lentus |
5291 |
twin_project |
5286 |
twin_data |
2012 |
informant_data |
663 |
harmony |
381 |
postal_groups |
100 |
每一次會面以一個稱為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 sceening 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;