1. 指定檢索條件
為了限制SELECT 語(yǔ)句檢索出來(lái)的記錄集,可使用WHERE 子句,它給出選擇行的條件??赏ㄟ^(guò)查找滿(mǎn)足各種條件的列值來(lái)選擇行。
可查找數字值:

也可以查找串值。(注意,一般串的比較是不區分大小寫(xiě)的。)

可以查找日期值:


可搜索組合值:

WHERE 子句中的表達式可使用表1-1中的算術(shù)運算符、表1-2 的比較運算符和表1-3 的邏輯運算符。還可以使用圓括號將一個(gè)表達式分成幾個(gè)部分??墒褂贸A?、表列和函數來(lái)完成運算。在本教程的查詢(xún)中,我們有時(shí)使用幾個(gè)MySQL函數,但是MySQL的函數遠不止這里
給出的這些。請參閱附錄C,那里給出了所有MySQL函數的清單。



錯了,因為這個(gè)查詢(xún)的意思是“選擇既出生在Vi rginia 又出生在M a r y l a n d的總統”,不可能有同時(shí)出生在兩個(gè)地點(diǎn)的總統,因此這個(gè)查詢(xún)無(wú)意義。在英語(yǔ)中,可以用“a n d”表示這種選擇,但在SQL 中,應該用OR 來(lái)連接兩個(gè)條件,如下所示:


這有時(shí)是可以覺(jué)察到的,不僅僅是在編寫(xiě)自己的查詢(xún)時(shí)可以覺(jué)察到,而且在為他人編寫(xiě)查詢(xún)時(shí)也可以知道。最好是在他人描述想要檢索什么時(shí)仔細聽(tīng),但不一定使用相同的邏輯運算符將他人的描述轉錄成SQL 語(yǔ)句。對剛才所舉的例子,正確的英語(yǔ)等價(jià)描述為“選擇出生在Vi rginia 或者出生在Maryland 的總統。”
2. NULL 值
NULL 值是特殊的;因為它代表“無(wú)值”。不可能以評估兩個(gè)已知值的相同方式來(lái)將它與已知值進(jìn)行評估。如果試圖與通常的算術(shù)比較運算符一道使用NULL,其結果是未定義的:

為了進(jìn)行NULL 值的搜索,必須采用特殊的語(yǔ)法。不能用= 或!= 來(lái)測試等于NULL 或不等于NULL,取而代之的是使用IS NULL 或IS NOT NULL 來(lái)測試。例如,因為我們將健在總統的死亡日期表示為NULL,那么可按如下語(yǔ)句查找健在的總統:


3. 對查詢(xún)結果進(jìn)行排序
有時(shí)我們注意到,在一個(gè)表裝入初始數據后,對其發(fā)布一條SELECT * FROM tbl_name查詢(xún),檢索出的行與這些行被插入的順序是相同的。但不要認為這種情況是有規律的。如果在初始裝入表后進(jìn)行了行的刪除和插入,就會(huì )發(fā)現服務(wù)器返回表的行次序被改變了。(刪除記錄在表中留下了未使用的“空位”,MySQL在以后插入新記錄時(shí)將會(huì )試圖對其填補。)缺省時(shí),如果選擇了行,服務(wù)器對返回行的次序不作任何保證。為了對行進(jìn)行排序,可
使用ORDER BY 子句:

在ORDER BY 子句中,可在列名之后利用ASC 或DESC 關(guān)鍵字指定排序是按該列值的升序或降序進(jìn)行的。例如,為了按倒序(降序)名排列總統名,可如下使用DESC:

如果在ORDER BY 子句中,對某個(gè)列名既不指定ASC 又不指定DESC,則缺省的次序為升序。在對可能包含NULL 值的列進(jìn)行排序時(shí),如果是升序排序, NULL 值出現在最前面,如果是按降序排序,NULL 值出現在最后。

4. 限制查詢(xún)結果如果一個(gè)查詢(xún)返回許多行,但您只想看其中的幾行,則可以利用LIMIT 子句,特別是與ORDER BY 子句結合時(shí)更是如此。MySQL允許限制一個(gè)查詢(xún)的輸出為前n 行。下面的查詢(xún)選擇了5 位出生日期最早的總統:

如果利用ORDER BY birth DESC 按降序排序,將得到5 位最晚出生的總統。LIMIT 也可以從查詢(xún)結果中取出中間部分。為了做到這一點(diǎn),必須指定兩個(gè)值。第一個(gè)值為結果中希望看到的第一個(gè)記錄(第一個(gè)結果記錄的編號為0 而不是1)。第二個(gè)值為希望看到的記錄個(gè)數。下面的查詢(xún)類(lèi)似于前面那個(gè)查詢(xún),但只顯示從第11行開(kāi)始的5 個(gè)記錄:

自MySQL3.23.2 以來(lái),可按照一個(gè)公式來(lái)排序查詢(xún)結果。例如,利用ORDER BYRAND( ) 與LIMIT 結合,從president 表中隨機抽取一個(gè)記錄:


5. 計算并命名輸出的列值

此查詢(xún)把名和姓連接起來(lái),中間間隔一個(gè)空格,將總統名形成一個(gè)單一字符串,而且將出生城市和州連接在一起,中間隔一個(gè)逗號,形成出生地。
在利用表達式來(lái)計算列值時(shí),此表達式被用作列標題。如果表達式很長(cháng)(如前面的一些查詢(xún)樣例中那樣),那么可能會(huì )出現一個(gè)很寬的列。為了處理這種情況,此列可利用AS name結構來(lái)重新命名標題。這樣的名稱(chēng)為列別名。用這種方法可使上面的輸出更有意義,如下所示:


6. 使用日期
在MySQL中使用日期時(shí)要記住的是,在表示日期時(shí)首先給出年份。1999 年7 月27 日表示為“1999 - 07 - 27”,而不是像通常那樣表示為“ 07 - 27 - 1999”或“27 - 07 - 1999”。MySQL提供了幾種對日期進(jìn)行處理的方法??梢詫θ掌谶M(jìn)行的一些運算如下:
■ 按日期排序。(這點(diǎn)我們已經(jīng)看到幾次了。)
■ 查找特定的日期或日期范圍。
■ 提取日期值的組成部分,如年、月或日。
■ 計算日期的差。
■ 日期增加或減去一個(gè)間隔得出另一日期。
下面給出一些日期運算的例子。
為了查找特定的日期,可使用精確的日期值或與其他日期值進(jìn)行比較,將一個(gè)DATE 列與有關(guān)的日期值進(jìn)行比較:



為了更詳細,詳細到天,可組合測試MONTH( ) 和DAYOFMONTH( ) 以找出在筆者的生日出生的總統:

這是一種可用來(lái)生成類(lèi)似報紙上娛樂(lè )部分所刊登的那種“這些人今天過(guò)生日”清單的查詢(xún)。但是,不必按前面的查詢(xún)那樣插入一個(gè)特殊的日期。為了查找每年的今天出生的總統,只要將他們的生日與C U R R E N T _ DATE 進(jìn)行比較即可:

可從一個(gè)日期減去另一個(gè)日期。這樣可以知道日期間的間隔,這對于確定年齡是非常有用的。例如,為了確定哪位總統活得最長(cháng),可將其逝世日期減去出生日期。為此,可利用函數TO _ DAYS( ) 將出生日期和逝世日期轉換為天數,求出差,然后除以365 得出大概的年齡:

此查詢(xún)中所用的FLOOR( ) 函數截掉了年齡的小數部分,得到一個(gè)整數。得出日期之差,還可以確定相對于某個(gè)特定日期有多長(cháng)時(shí)間。這樣可以告訴歷史同盟的會(huì )員,他們還有多久就應該更新自己的會(huì )員資格了。計算他們的截止日期和當前日期之差,如果小于某個(gè)閾值,則不久就需要更新了。下面的查詢(xún)是查找需要在60 天內更新的會(huì )員:

自MySQL3.22 以來(lái),可使用DATE_ADD( ) 或DATE_SUB( ) 從一個(gè)日期計算另一個(gè)日期。這些函數取一個(gè)日期及時(shí)間間隔并產(chǎn)生一個(gè)新日期。例如:

本節中前面給出的一個(gè)查詢(xún)選擇70 年代逝世的總統,它對選擇范圍的端點(diǎn)使用直接的日期值。該查詢(xún)可以利用一個(gè)字符串日期和一個(gè)由開(kāi)始日期和時(shí)間間隔計算出的結束日期來(lái)重寫(xiě):

會(huì )員更新查詢(xún)可根據DATE_ADD( ) 寫(xiě)出如下:

本章前面給出了一個(gè)查詢(xún)如下,確定不久要來(lái)檢查但還沒(méi)來(lái)診所的牙科病人:

現在回過(guò)頭來(lái)看,讀者會(huì )更清楚這個(gè)查詢(xún)的含義了。
7. 模式匹配
MySQL允許查找與某個(gè)模式相配的值。這樣,可以選擇記錄而不用提供精確的值。為了進(jìn)行模式匹配運算,可使用特殊的運算符( LIKE 和NOT LIKE),并且指定一個(gè)包含通配符的串。字符“_”匹配任意單個(gè)字符,而“%”匹配任意字符序列(包括空序列)。使用L I K E或NOT LIKE 的模式匹配都是不區分大小寫(xiě)的。下列模式匹配以“W”或“w”開(kāi)始的姓:



MySQL還提供基于擴展正規表達式的模式匹配。正規表達式在附錄C 的REGEXP 運算符的介紹中描述。
8. 生成匯總
MySQL所能做的最有用的事情是濃縮大量的原始數據行并對其進(jìn)行匯總。當學(xué)會(huì )了利用MySQL來(lái)生成匯總時(shí),它就變成了用戶(hù)強有力的好幫手了,因為手工進(jìn)行匯總是一項冗長(cháng)的、費時(shí)的、易出錯的工作。匯總的一種簡(jiǎn)單的形式是確定在一組值中哪些值是唯一值。利用DISTINCT 關(guān)鍵字來(lái)刪除結果中的重復行。例如,總統出生的各個(gè)州可按如下找出:

其他的匯總形式涉及計數,可利用COUNT( ) 函數。如果使用COUNT (*),它將給出查詢(xún)所選擇的行數。如果一個(gè)查詢(xún)無(wú)WHERE 子句,COUNT(*) 將給出表中的行數。下列查詢(xún)給出共有多少人當過(guò)美國總統:

如果查詢(xún)有WHERE 子句,COUNT(*) 將給出此子句選擇多少行。下面的查詢(xún)給出目前為止對班級進(jìn)行了多少次測試:


COUNT(*) 對選中的行進(jìn)行計數。而COUNT(col_name) 只對非NULL 值進(jìn)行計數。下面的查詢(xún)說(shuō)明了這些差異:

這表示,總共有41位總統,他們中只有一個(gè)具有名字后綴,并且大多數總統都已去世。自MySQL3.23.2 以來(lái),可以將COUNT( ) 與DISTINCT 組合對選擇結果集中不同的值進(jìn)行計數。例如,為了對總統出生的不同州進(jìn)行計數,可執行下列查詢(xún):

可以根據匯總列中單獨的值對計數值進(jìn)行分解。例如,您可能根據下列的查詢(xún)結果知道班級中所有學(xué)生的人數:

但是,有多少是男孩?有多少是女孩?分別得出男孩、女孩的一種方法是分別對每種性別進(jìn)行計數:



如果以這種方法對值計數, GROUP BY 子句是必須的;它告訴MySQL在對值計數之前怎樣進(jìn)行聚集。如果將其省去,則要出錯。COUNT(*) 與GROUP BY 一起用來(lái)對值進(jìn)行計數比分別對每個(gè)不同的列值進(jìn)行計數有更多的優(yōu)點(diǎn),這些優(yōu)點(diǎn)是:
■ 不必事先知道要匯總的列中有些什么值。
■ 不用編寫(xiě)多個(gè)查詢(xún),只需編寫(xiě)單個(gè)查詢(xún)即可。
■ 用單一查詢(xún)就可以得出所有結果,因此可以對結果進(jìn)行排序。
前兩個(gè)優(yōu)點(diǎn)對于更方便地表示查詢(xún)很重要。第三個(gè)優(yōu)點(diǎn)也較為重要,因為它提供了顯示
結果的靈活性。在使用GROUP BY 子句時(shí),其結果是在要分組的列上進(jìn)行排序的,但是可以
使用ORDER BY 來(lái)按不同的次序進(jìn)行排序。例如,如果想得到各州產(chǎn)生的總統人數,并按產(chǎn)
生人數最多的州優(yōu)先排出,可以如下使用ORDER BY 子句:


如果希望進(jìn)行排序的列是從計算得出的,則可以給該列一個(gè)別名,并在ORDER BY 子句中引用這個(gè)別名。前面的查詢(xún)說(shuō)明了這一點(diǎn); COUNT(*) 列的別名為count。引用這樣的列的另一種方法是引用它在輸出結果中的位置。前面的查詢(xún)可編寫(xiě)如下:



如果不想用LIMIT 子句來(lái)限制查詢(xún)輸出,而是利用查找特定的COUNT( ) 值來(lái)達到這個(gè)目的,可使用HAVING 子句。下面的查詢(xún)給出了產(chǎn)生兩個(gè)以上總統的州:

從更為普遍的意義上說(shuō),這是一種在要查找的列中重復值時(shí)執行的查詢(xún)類(lèi)型。HAVING 類(lèi)似于WHERE,但它是在查詢(xún)結果已經(jīng)選出后才應用的,用來(lái)縮減服務(wù)器實(shí)際送到客戶(hù)機的結果。除了COUNT( ) 外還有許多匯總函數。MIN( )、MAX( )、SUM( ) 和AVG( ) 函數在確定列的最大、最小、總數和平均值時(shí)都非常有用,甚至可以同時(shí)使用它們。下面的查詢(xún)得出給定的測試和測驗的各種數字特性。它還給出有多少學(xué)分參與了每個(gè)值的計算(有的學(xué)生可能缺曠或未計入)。



此查詢(xún)選擇已經(jīng)去世的總統,按出生地對他們進(jìn)行分組,并計算出他們逝世時(shí)的年齡,計算出平均年齡(每個(gè)州的),然后按平均年齡進(jìn)行排序。換句話(huà)說(shuō),此查詢(xún)按所出生地確定已故總統的平均壽命。但這說(shuō)明了什么呢?它僅僅說(shuō)明您可寫(xiě)該查詢(xún),當然并不說(shuō)明此查詢(xún)是否值得寫(xiě)。并不是用一個(gè)數據庫可以做的所有事情都同樣有意義;但是,人們有時(shí)在發(fā)現可以利用自己的數據庫進(jìn)行查詢(xún)時(shí)感到很開(kāi)心。這可能說(shuō)明關(guān)于轉播運動(dòng)會(huì )的不斷增加的深奧的(空洞的)統計數據在過(guò)去幾年里正在不斷增多的原因。運動(dòng)統計者可以使用他們的數據庫來(lái)計算出某個(gè)隊的歷史紀錄,而這些數字你可能感興趣,也可能毫無(wú)興致。
9. 從多個(gè)表中檢索信息
到目前為止,我們所編寫(xiě)的查詢(xún)都是從單個(gè)表中得到數據的?,F在,我們將進(jìn)行一件更為有趣的工作。以前筆者曾經(jīng)提到過(guò),關(guān)系DBMS 的強大功能在于它能夠將一樣東西與另一樣東西相關(guān)聯(lián),因為這樣使得能夠結合多個(gè)表中的信息來(lái)解答單個(gè)表不能解答的問(wèn)題。本節介紹怎樣編寫(xiě)這種查詢(xún)。在從多個(gè)表中選擇信息時(shí),需要執行一種稱(chēng)為連接( j o i n)的操作。這是因為需要將一個(gè)表中的信息與其他表中的信息相連接來(lái)得出查詢(xún)結果。即通過(guò)協(xié)調各表中的值來(lái)完成這項工作。


此查詢(xún)找出具有給定日期的記錄,然后利用該記錄中的事件ID 查找具有相同事件ID 的學(xué)分。對于每個(gè)匹配的事件記錄和學(xué)分記錄組合,顯示學(xué)生ID、學(xué)分、日期和事件類(lèi)型。此查詢(xún)在兩個(gè)重要方面不同于我們曾經(jīng)編寫(xiě)過(guò)的其他查詢(xún)。它們是:
■ FROM 子句給出了不止一個(gè)表名,因為我們要檢索的數據來(lái)自不止一個(gè)表:
FROM event,score
■ WHERE 子句說(shuō)明event 和score 表是由每個(gè)表中的event_id 值的匹配連接起來(lái)的:
where event.event_id=score.event_id
請注意,我們是怎樣利用tbl_name.col_name 語(yǔ)法引用列,以便MySQL知道引用的是哪些表的列。(event_id 出現在兩個(gè)表中,如果不用表名來(lái)限定它的話(huà)將會(huì )出現混淆。)此查詢(xún)中的其他列( date、score、type)可單獨使用而不用表名限定符,因為它們在表中只出現一次,從而不會(huì )出現含混。但是,一般在連接中我們對每個(gè)列都進(jìn)行限定以便清晰地表示出每個(gè)列是屬于哪個(gè)表。在完全限定的形式下,查詢(xún)如下:

從現在起,我們將使用完全限定的形式。第二步,我們利用student 表完成查詢(xún)以便顯示學(xué)生名。(第一步中查詢(xún)的輸出給出了student_id 字段,但是名字更有意義。)名字顯示是利用score 表和student 表兩者都具有student_id 列,使它們中的記錄可被連接這個(gè)事實(shí)來(lái)完成的。最終的查詢(xún)如下:

■ student 表被增加到了FROM 子句中,因為除了event 表和score 表外還用到了它。
■ student_id 列現在不明確了(因為現在有兩個(gè)引用到的表都含有此列),因此必須限定為score.student_id 或student.student_id 以表明使用的是哪個(gè)表。
■ WHERE 子句有一個(gè)附加項,它說(shuō)明根據學(xué)生ID 將score 表記錄與student 表記錄進(jìn)行匹配。
■ 此查詢(xún)是顯示學(xué)生名而不是學(xué)生ID。(當然,如果愿意的話(huà),可以?xún)烧叨硷@示。)利用此查詢(xún),可以加入任意日期,得到該日期的學(xué)分,用學(xué)生名和學(xué)分類(lèi)型完善查詢(xún)結果。不一定要了解關(guān)于學(xué)生ID 或事件ID 的情況。MySQL小心地得出相關(guān)的ID 值并利用它們自動(dòng)地使各表的行相配。
學(xué)分保存方案涉及的另一項工作是匯總學(xué)生的缺勤情況。缺勤情況是按學(xué)生ID 和日期在absence 表中記錄的。為得到學(xué)生名(而不僅僅是ID),我們需要根據student_id 的值將absence 表連接到student 表。下面的查詢(xún)給出了學(xué)生的ID 號和名字以及缺勤計數:


前面,在“生成匯總”一節中,我們執行了一個(gè)查詢(xún),它生成score 表中數據的數值特征。該查詢(xún)的輸出列出了事件ID,但不包括學(xué)分日期或類(lèi)型,因為我們不知道怎樣將score 表連接到event 表以得到學(xué)分的日期和類(lèi)型?,F在可以做到了。下面的查詢(xún)類(lèi)似于早先的那個(gè),但是它給出了學(xué)分的日期和類(lèi)型而不只是簡(jiǎn)單的數字事件ID:

可利用諸如COUNT( ) 和AVG( ) 這樣的函數生成多個(gè)列上的匯總,即使這些列來(lái)自不同的表也是如此。下面的查詢(xún)確定學(xué)分數,以及事件日期與學(xué)生性別的每種組合的平均學(xué)分。

我們可以使用一個(gè)類(lèi)似的查詢(xún)來(lái)完成學(xué)分保存方案的一個(gè)任務(wù),即在學(xué)期末計算每個(gè)學(xué)生的總學(xué)分。相應的查詢(xún)如下:


此查詢(xún)有兩個(gè)技巧性的東西:
■ 我們需要使用同一表的兩個(gè)實(shí)例,因此建立了表的別名( p 1、p 2),并利用它們無(wú)歧義地引用表列。
■ 每個(gè)總統的記錄與自身相匹配,但是我們不希望在輸出中看到同一總統出再現兩次。WHERE 子句的第二行保證比較的記錄為不同總統的記錄,使記錄不與自身匹配??梢跃帉?xiě)一個(gè)查找出生在同一天的總統的類(lèi)似查詢(xún)。出生日期不能直接比較,因為那樣會(huì )錯過(guò)出生在不同年份的總統。我們用MONTH( ) 和DAYOFMONTH( ) 來(lái)比較出生日期的月和日,相應的查詢(xún)如下:

利用DAYOFYEAR( ) 而不是MONTH( ) 和DAYOFMONTH( ) 將得出一個(gè)更為簡(jiǎn)單的查詢(xún),但是在比較閏年日期與非閏年日期時(shí)將會(huì )得出不正確的結果。迄今所執行的連接結合了來(lái)自那些在某種意義上具有邏輯關(guān)系的表中的信息,但是只有您知道該關(guān)系無(wú)意義。MySQL并不知道(或不關(guān)心)所連接的表相互之間是否相關(guān)。例如,可將event 表連接到president 表以找出在某個(gè)總統生日那天是否進(jìn)行了測驗或測試,此查詢(xún)如下:

它產(chǎn)生了您所想要的東西。但說(shuō)明了什么呢?這說(shuō)明MySQL將愉快地制造出結果,至于這些結果是否有意義它不管。這是因為您使用的是計算機,所以它不能自動(dòng)地判斷查詢(xún)的結果有用或無(wú)用。無(wú)論如何,我們都必須為自己所做的事負責。
聯(lián)系客服