既然你知道怎樣輸入命令,現在是存取一個數據庫的時候了。
假定在你的家(你的“動物園”)中有很多寵物,並且你想追蹤關他們各種各樣類型的信息。你可以通過創建表來保存你的數據並根據所需要的信息裝載他們做到,然你可以通過從表中檢索數據來回答關你的動物不同種類的問題。本節顯示如何做到所有這些事情:
- 怎樣創建一個數據庫
- 怎樣創建一個數據庫表
- 怎樣裝載數據到數據庫表
- 怎樣以各種方法從表中檢索數據
- 怎樣使用多個表
動物園數據庫將會是簡單的(故意的),但是不難把它想象成可能用到相似類型數據庫的真實世界情況。例如,這樣的一個數據庫能被一個農夫用來追蹤家畜,或由一個獸醫追蹤病畜記錄。
使用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。
至少有一些修正它的方法:
- 編輯文件“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";
如上所示,檢索整個表是容易的,但是一般你不想那樣做,特別地當表變得很大時。相反,你通常對回答一個特別的問題更感興趣,在這種情況下你在你想要的信息上指定一些限制。讓我們看一些他們回答有關你寵物的問題的選擇查詢。
你能從你的表中只選擇特定的行。例如,如果你想要驗証你對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是特殊的值,這以會解釋。見8.4.4.6 用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,它們是同義詞)。
擴展正則表達式的一些字符是:
- “.”匹配任何單個的字符。
- 一個字符類“[...]”匹配在方括號內的任何字符。例如,“[abc]”匹配“a”、“b”或“c”。為了命名字符的一個范圍,使用一個“-”。“[a-z]”匹配任何小寫字母,而“[0-9]”匹配任何數字。
- “ * ”匹配零個或多個在它前面的東西。例如,“x*”匹配任何數量的“x”字符,“[0-9]*”匹配的任何數量的數字,而“.*”匹配任何數量的任何東西。
- 正則表達式是區分大小寫的,但是如果你希望,你能使用一個字符類匹配兩種寫法。例如,“[aA]”匹配小寫或大寫的“a”而“[a-zA-Z]”匹配兩種寫法的任何字母。
- 如果它出現在被測試值的任何地方,模式就匹配(只要他們匹配整個值,SQL模式匹配)。
- 為了定位一個模式以便它必須匹配被測試值的開始或結尾,在模式開始處使用“^”或在模式的結尾用“$”。
為了說明擴展正則表達式如何工作,上面所示的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子句列出兩個表,因為查詢需要從他們兩個拉出信息。
- 當組合(聯結-join)來自多個表的信息時,你需要指定在一個表中的記錄怎樣能匹配其它表的記錄。這很簡單,因為它們都有一個
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 |
+--------+------+--------+------+---------+
在這個查詢中,我們為表名指定別名以便能引用列並且使得每一個列引用關聯哪個表實例更直觀。
(http://www.fanqiang.com)
進入【