現(xiàn)在我們已經(jīng)具備了所需的所有基礎(chǔ)知識;可以將MySQL 投入工作了!本節(jié)提供一個(gè)教程,幫助熟悉M y S Q L。在完成這個(gè)教程時(shí),將創(chuàng)建一個(gè)樣例數(shù)據(jù)庫和這個(gè)數(shù)據(jù)庫中的表,然后增加、檢索、刪除和修改信息與數(shù)據(jù)庫進(jìn)行交互。此外,在操作這個(gè)樣例數(shù)據(jù)庫的過程中,將能學(xué)到下列東西:
■ 如何利用mysql 客戶機(jī)程序與MySQL 通信。
■ SQL 語言的基本語句。(如果您曾經(jīng)使用過其他R D B M S,從而熟悉S Q L,那么瀏覽一下這個(gè)教程,看看SQL 的MySQL 版與您熟悉的版本有何差別也是很好的。)正如上一節(jié)所述, MySQL 采用客戶機(jī)/服務(wù)器體系結(jié)構(gòu),其中服務(wù)器運(yùn)行在存放數(shù)據(jù)庫的機(jī)器上,而客戶機(jī)通過網(wǎng)絡(luò)連接到服務(wù)器。這個(gè)教程主要基于mysql 客戶機(jī)的應(yīng)用。m y s q l讀取您的SQL 查詢,將它們發(fā)送給服務(wù)器,并顯示結(jié)果。mysql 運(yùn)行在MySQL 所支持的所有平臺(tái)上,并提供與服務(wù)器交互的最直接的手段,因此,它首先是一個(gè)邏輯上的客戶機(jī)。
在本書中,我們將用samp_db 作為樣例數(shù)據(jù)庫的名稱。但是有可能在您完成本例子的過程中需要使用另一個(gè)數(shù)據(jù)庫名。因?yàn)榭赡茉谀南到y(tǒng)上已經(jīng)有某個(gè)人使用了samp_db 這個(gè)名稱,或者管理員給您指定了另一個(gè)數(shù)據(jù)庫名稱。在后面的例子中,無論是哪種情況,都用數(shù)
據(jù)庫的實(shí)際名稱代替s a m p _ d b。表名可以像例子所顯示的那樣精確地使用,即使系統(tǒng)中的多個(gè)人都具有他們自己的樣例數(shù)據(jù)庫也是如此。順便說一下,在MySQL 中,如果有人使用了相同的表名也沒什么關(guān)系。一旦各個(gè)用戶都具有自己的數(shù)據(jù)庫, MySQL 將一直保留這些數(shù)據(jù)庫名,防止各用戶互相干擾。
1.4.1 基本要求
為了試驗(yàn)這個(gè)教程中的例子,必須安裝M y S Q L。特別是必須具有對MySQL 客戶機(jī)和某個(gè)MySQL 服務(wù)器的訪問權(quán)。相應(yīng)的客戶機(jī)程序必須位于您的機(jī)器上。至少需要有m y s q l程序,最好還有m y s q l i m p o r t程序。服務(wù)器也可以位于您的機(jī)器上,盡管這不是必須的。實(shí)際上,只要允許連接到服務(wù)器,那么服務(wù)器位于何位置都沒有關(guān)系。若服務(wù)器正巧運(yùn)行在您的機(jī)器上,適當(dāng)?shù)目蛻魴C(jī)程序又已經(jīng)安裝,那么就可以開始試驗(yàn)了。如果您尚需設(shè)法搞到M y S Q L,可參閱附錄A“獲得和安裝軟件”的說明。如果您正自己安裝M y S Q L,可參閱這一章,或把它給管理員看。如果網(wǎng)絡(luò)訪問是通過一個(gè)因特網(wǎng)服務(wù)商(I S P)進(jìn)行的,那么可查看該服務(wù)商是否擁有M y S Q L。如果該ISP 不提供MySQL 服務(wù),可查看附錄J“因特網(wǎng)服務(wù)商”以得到某些選擇更適合的服務(wù)商的建議。
除M y S Q L軟件外,還需要得到創(chuàng)建樣例數(shù)據(jù)庫及其表的權(quán)限。如果您沒有這種權(quán)限,可以向MySQL 管理員咨詢。管理員可通過運(yùn)行mysql 并發(fā)布如下的命令提供這種權(quán)限:

MySQL 與mysql 的區(qū)別
為了避免混淆,應(yīng)該說明,“M y S Q L”指的是整個(gè)MySQL RDBMS,而“m y s q l”代表的是一個(gè)特定的客戶機(jī)程序名。它們的發(fā)音都是相同的,但可通過不同的大小寫字符和字體來區(qū)分。關(guān)于發(fā)音,MySQL 的發(fā)音為“m y - e s s - q u e u e - e l l”。我們知道這是因?yàn)镸ySQL 參考指南中是這樣發(fā)音的。而SQL 的發(fā)音為“s e q u e l”或“e s s - q u e u e - e l l”。我不認(rèn)為哪個(gè)發(fā)音更好一些。愿意讀哪個(gè)音都可以,不過在您對別人讀的時(shí)候,他可能會(huì)用他認(rèn)為是“正確”的發(fā)音對您進(jìn)行糾正。
前一個(gè)命令在paul 從l o c a l h o s t(服務(wù)器運(yùn)行在正運(yùn)行的同一主機(jī))連接時(shí),允許它完全訪問samp_db 數(shù)據(jù)庫及它的所有表。它還給出了一個(gè)口令s e c r e t。第二個(gè)命令與第一個(gè)類似,但允許paul 從任何主機(jī)上連接(“%”為通配符)。也可以用特定的主機(jī)名取代“%”,使p a u l只能從該主機(jī)上進(jìn)行連接。(如果您的服務(wù)器允許從localhost 匿名訪問,由于服務(wù)器搜索授權(quán)表查找輸入連接匹配的方式的原因,這樣一個(gè)GRANT 語句可能是必須的。)關(guān)于G R A N T語句以及設(shè)置MySQL 用戶賬號的更詳細(xì)信息,可在第11 章“常規(guī)的MySQL 管理”找到。
1.4.2 取得樣例數(shù)據(jù)庫的分發(fā)包
這個(gè)教程在某些地方要涉及來自“樣例數(shù)據(jù)庫分發(fā)包”中的文件。有的文件含有幫助來設(shè)置樣例數(shù)據(jù)庫的查詢或數(shù)據(jù)。為了得到這個(gè)分發(fā)包,可參閱附錄A。在打開這個(gè)分發(fā)包時(shí),將創(chuàng)建一個(gè)名為samp_db 的目錄,此目錄中含有所需的文件。無論您在哪個(gè)地方試驗(yàn)與樣例數(shù)據(jù)庫有關(guān)的例子,建議都移入該目錄。
1.4.3 建立和中止與服務(wù)器的連接
為了連接到服務(wù)器,從外殼程序(即從UNIX 提示符,或從Windows 下的DOS 控制臺(tái))激活mysql 程序。命令如下:

其中的“%”在本書中代表外殼程序提示符。這是UNIX 標(biāo)準(zhǔn)提示符之一;另一個(gè)為“$”。在Windows 下,提示符類似“c : \ >”。
mysql 命令行的options 部分可能是空的,但更可能的是發(fā)布一條類似如下的命令:

在激活mysql 時(shí),有可能不必提供所有這些選項(xiàng);確切使用的命令請咨詢MySQL 管理員。此外,可能還需要至少指定一個(gè)名稱和一個(gè)口令。
在剛開始學(xué)習(xí)MySQL 時(shí),大概會(huì)為其安全系統(tǒng)而煩惱,因?yàn)樗鼓y于做自己想做的事。(您必須取得創(chuàng)建和訪問數(shù)據(jù)庫的權(quán)限,任何時(shí)候連接到數(shù)據(jù)庫都必須給出自己的名字和口令。)但是,在您通過數(shù)據(jù)庫錄入和使用自己的記錄后,看法就會(huì)馬上改變了。這時(shí)您會(huì)很欣賞MySQL 阻止了其他人窺視(或者更惡劣一些,破壞?。┠馁Y料。
下面介紹選項(xiàng)的含義:
■ - h h o s t _ n a m e(可選擇形式:- - h o s t =h o s t _ n a m e)
希望連接的服務(wù)器主機(jī)。如果此服務(wù)器運(yùn)行在與mysql 相同的機(jī)器上,這個(gè)選項(xiàng)一般可
省略。
■ -u u s e r _ n a m e(可選擇的形式:- - u s e r =u s e r _ n a m e)您的MySQL 用戶名。如果使用UNIX 且您的MySQL 用戶名與注冊名相同,則可以省去這個(gè)選項(xiàng);mysql 將使用您的注冊名作為您的MySQL 名。在Windows 下,缺省的用戶名為O D B C。這可能不一定非常有用。可在命令行上指定一個(gè)名字,也可以通過設(shè)置USER 變量在環(huán)境變量中設(shè)置一個(gè)缺省名。如用下列set 命令指定paul 的一個(gè)用戶名:

■ - p(可選擇的形式:- - p a s s w o r d)
這個(gè)選項(xiàng)告訴mysql 提示鍵入您的MySQL 口令。注意:可用-pyour_password 的形式(可選擇的形式: - - p a s s w o r d = y o u r _ p a s s w o r d)在命令行上鍵入您的口令。但是,出于安全的考慮,最好不要這樣做。選擇-p 不跟口令告訴mysql 在啟動(dòng)時(shí)提示您鍵入口令。例如:

在看到Enter password: 時(shí),鍵入口令即可。(口令不會(huì)顯到屏幕,以免給別人看到。)請注意,MySQL 口令不一定必須與UNIX 或Windows 口令相同。如果完全省略了-p 選項(xiàng),mysql 就認(rèn)為您不需要口令,不作提示。請注意: -h 和-u 選項(xiàng)與跟在它們后面的詞有關(guān),無論選項(xiàng)和后跟的詞之間是否有空格。而-p 卻不是這樣,如果在命令行上給出口令, -p 和口令之間一定不加空格。例如,假定我的MySQL 用戶名和口令分別為paul 和s e c r e t,希望連接到在我注冊的同一機(jī)器上運(yùn)行的服務(wù)器上。下面的mysql 命令能完成這項(xiàng)工作:

在我鍵入命令后, mysql 顯示Enter password: 提示鍵入口令。然后我鍵入口令( * * * * * *表明我鍵入了s e c r e t)。如果一切順利的話, mysql 顯示一串消息和一個(gè)“ m y s q l >”提示,表示它正等待我發(fā)布查詢。完整的啟動(dòng)序列如下所示:

為了連接到在其他某個(gè)機(jī)器上運(yùn)行的服務(wù)器,需要用-h 指定主機(jī)名。如果該主機(jī)為pit -v i p e r. s n a k e . n e t,則相應(yīng)的命令如下所示:

在后面的說明mysql 命令行的多數(shù)例子中,為簡單起見,我們打算省去- h、-u 和-p 選項(xiàng)。并且假定您將會(huì)提供任何所需的選項(xiàng)。
有很多設(shè)置賬號的方法,從而不必在每次運(yùn)行mysql 時(shí)都在連接參數(shù)中進(jìn)行鍵入。這個(gè)問題在1 . 5節(jié)“與mysql 交互的技巧”中介紹。您可能會(huì)希望現(xiàn)在就跳到該節(jié),以便找到一些更易于連接到服務(wù)器的辦法。
在建立了服務(wù)器的一個(gè)連接后,可在任何時(shí)候鍵入下列命令來結(jié)束會(huì)話:

還可以鍵入Control-D 來退出,至少在UNIX 上可以這樣。
1.4.4 發(fā)布查詢
在連接到服務(wù)器后,就可以發(fā)布查詢了。本節(jié)介紹有關(guān)與mysql 交互應(yīng)該了解的一些知識。為了在mysql 中輸入一個(gè)查詢,只需鍵入它即可。在查詢的結(jié)尾處,鍵入一個(gè)分號(“;”)并按Enter 鍵。分號告訴mysql 該查詢是完整的。(如果您喜歡鍵入兩個(gè)字符的話,也可以使用“\ g”終止查詢。)在鍵入一個(gè)查詢之后, mysql 將其發(fā)送到服務(wù)器上。該服務(wù)器處理此查詢并將結(jié)果送回m y s q l,mysql 將此結(jié)果顯示出來。下面是一個(gè)簡單的查詢例子和結(jié)果:

它給出當(dāng)前的日期和時(shí)間。(NOW() 函數(shù)本身并無多大用處,但可將其用于表達(dá)式中。如比較當(dāng)前日期和其他日期的差異。)
mysql 還在結(jié)果中顯示行數(shù)計(jì)數(shù)。本書在例子中一般不給出這個(gè)計(jì)數(shù)。因?yàn)閙ysql 需要見到分號才發(fā)送查詢到服務(wù)器,所以在單一的行上不需要鍵入分號。如果有必要,可將一個(gè)查詢分為幾行,如下所示:

請注意,在鍵入查詢的第一行后,提示符從‘m y s q l’ 變成了‘- >’;這表示mysql 允許繼續(xù)鍵入這個(gè)查詢。這是一個(gè)重要的提示,因?yàn)槿绻诓樵兊哪┪餐浟朔痔?,此提示將有助于提醒您查詢尚不完整。否則您會(huì)一直等下去,心里納悶為什么mysql 執(zhí)行查詢?yōu)槭裁催@么長的時(shí)間還沒完;而mysql 也搞不清為什么結(jié)束查詢的鍵入要花您那么多的時(shí)間!
大部分情況下,用大寫字符、小寫字符或大小寫字符混合鍵入查詢沒什么關(guān)系。下列查詢?nèi)堑葍r(jià)的:

本書中的例子用大寫字符表示SQL 關(guān)鍵字和函數(shù)名,用小寫字符表示數(shù)據(jù)庫、表和列名。
如果在查詢中調(diào)用一個(gè)函數(shù),在函數(shù)名和后跟的圓括號中間不允許有空格,例:

這兩個(gè)查詢看上去差別不大,但第二個(gè)失敗了,因?yàn)閳A括號并沒有緊跟在函數(shù)名的后面。如果已經(jīng)開始鍵入一個(gè)多行的查詢,而又不想立即執(zhí)行它,可鍵入‘\ c’ 來跳過(放棄)它,如:

請注意,提示符又變回了‘m y s q l >’,這表示mysql 為鍵入的新查詢作好了準(zhǔn)備。可將查詢存儲(chǔ)在一個(gè)文件中并告訴mysql 從文件中讀取查詢而不是等待鍵盤輸入。可利用外殼程序鍵入重定向?qū)嵱贸绦騺硗瓿蛇@項(xiàng)工作。例如,如果在文件my_file.sql 中存放有查
詢,可如下執(zhí)行這些查詢:

可用這種辦法調(diào)用任何所需的文件。這里用后綴為“ . s q l”來表示該文件含有SQL 語句。執(zhí)行mysql 的這種方法將在輸入數(shù)據(jù)到samp_db 數(shù)據(jù)庫時(shí)的“增加新記錄”中使用。為了裝載一個(gè)表,讓mysql 從某個(gè)文件中讀取I N S E RT 語句比每次用手工鍵入這些語句更為方便。
本教程的其余部分向您提供了許多可以自己試試的查詢。這些查詢以‘m y s q l >’ 提示為前導(dǎo)后跟結(jié)束分號,這些例子通常都給出了查詢輸出結(jié)果??梢园唇o出的形式鍵入這些查詢,所得到的結(jié)果應(yīng)該與自學(xué)材料中的相同。給出的查詢中無提示符的或無分號語句結(jié)束符的只是用來說明某個(gè)要點(diǎn),不用執(zhí)行它們。(如果愿意您可以試一下,但如果試的話,請記住給語句末尾加一個(gè)分號。)本書后面的章節(jié)中,我們一般不給出‘m y s q l >’ 提示或SQL 語句的分號。這樣做的原因是為了可以在非mysql 客戶機(jī)程序的語言環(huán)境(如在Perl 腳本中或PHP 腳本中)中發(fā)布查詢,在這些語言環(huán)境中,既無提示符也不需要分號。在專門針對mysql 輸入一個(gè)查詢的場合會(huì)作出相應(yīng)的說明。
1.4.5 創(chuàng)建數(shù)據(jù)庫
現(xiàn)在開始創(chuàng)建samp_db 樣例數(shù)據(jù)庫及其表,填充這些表并對包含在這些表中的數(shù)據(jù)進(jìn)行一些簡單的查詢。
使用數(shù)據(jù)庫涉及幾個(gè)步驟:
1) 創(chuàng)建(初始化)數(shù)據(jù)庫。
2) 創(chuàng)建數(shù)據(jù)庫中的表。
3) 對表進(jìn)行數(shù)據(jù)插入、檢索、修改或刪除。
檢索現(xiàn)有數(shù)據(jù)是對數(shù)據(jù)庫執(zhí)行的最簡單且常見的操作。另外幾個(gè)最簡單且常見的操作是插入新數(shù)據(jù)、更新或刪除現(xiàn)有數(shù)據(jù)。較少使用的操作是創(chuàng)建表的操作,而最不常用的操作是創(chuàng)建數(shù)據(jù)庫。
我們將從頭開始,先創(chuàng)建數(shù)據(jù)庫,再插入數(shù)據(jù),然后對數(shù)據(jù)進(jìn)行檢索。為了創(chuàng)建一個(gè)新的數(shù)據(jù)庫,用mysql 連接到數(shù)據(jù)庫然后發(fā)布C R E ATE DATABASE 語句,此語句指定了數(shù)據(jù)庫名:

在創(chuàng)建表以及對這些表進(jìn)行各種操作之前,必須先創(chuàng)建samp_db 數(shù)據(jù)庫。創(chuàng)建數(shù)據(jù)庫后,這個(gè)新創(chuàng)建的數(shù)據(jù)庫并不是當(dāng)前數(shù)據(jù)庫。這可從執(zhí)行下面的查詢看出:

為了使samp_db 成為當(dāng)前數(shù)據(jù)庫,發(fā)布USE 語句即可:

USE 為少數(shù)幾個(gè)不需要終結(jié)符的語句之一,當(dāng)然,加上終結(jié)符也不會(huì)出錯(cuò)。HELP 是另一個(gè)不需要終結(jié)符的語句。如果想了解不需要終結(jié)符的語句有哪些,可發(fā)布HELP 語句。在發(fā)布了USE 語句后,samp_db 成為缺省數(shù)據(jù)庫:

使數(shù)據(jù)庫成為當(dāng)前數(shù)據(jù)庫的另一個(gè)方法是在激活mysql 時(shí)在命令行上指定它,如下所示:

事實(shí)上,這是一個(gè)命名要使用的數(shù)據(jù)庫的方法。如果需要連接參數(shù)可在數(shù)據(jù)庫名前指定。例如,下列兩個(gè)命令使我們能連接到在本地主機(jī)和p i t - v i p e r.snake.net 上的samp_db 數(shù)據(jù)庫上:

除非另有指定,否則后面的例子都假定在激活mysql 時(shí),在命令行上給出samp_db 使其成為當(dāng)前數(shù)據(jù)庫。如果激活數(shù)據(jù)庫時(shí)忘了在命令行上指定數(shù)據(jù)庫,只需發(fā)布USE samp_db 語句即可。
1.4.6 創(chuàng)建表
本節(jié)中,我們將創(chuàng)建樣例數(shù)據(jù)庫samp_db 所需的表。我們首先考慮美國歷史同盟需要的表。然后再考慮學(xué)分保存方案所需的表。在某些數(shù)據(jù)庫的書籍中,在這里要大講分析與設(shè)計(jì)、實(shí)體—關(guān)系圖、標(biāo)準(zhǔn)程序以及諸如此類的東西。這里確實(shí)也可以講這些東西,但是我寧可只講點(diǎn)實(shí)用的東西,比方說,我們的數(shù)據(jù)庫應(yīng)該是怎樣的:數(shù)據(jù)庫中將包含什么內(nèi)容,每個(gè)表中有哪些數(shù)據(jù)以及由決定如何表示數(shù)據(jù)而帶來的一些問題。這里所作出的關(guān)于數(shù)據(jù)表示的選擇并不是絕對的。在其他場合下,可能會(huì)選擇不同的方式來表示類似的數(shù)據(jù),這取決于應(yīng)用的需要以及打算將數(shù)據(jù)派何用途。
1. 美國歷史同盟所需的表
美國歷史同盟的表設(shè)計(jì)相當(dāng)簡單:
■ 總統(tǒng)( p r e s i d e n t )表。此表含有描述每位總統(tǒng)的記錄。同盟站點(diǎn)上的聯(lián)機(jī)測驗(yàn)要使用這個(gè)表。
■ 會(huì)員( m e m b e r )表。此表用來維護(hù)同盟每個(gè)會(huì)員的當(dāng)前信息。這些信息將用來建立會(huì)員地址名錄的書面和聯(lián)機(jī)版本、發(fā)送會(huì)員資格更新提示等等。
(1) president表
president 表很簡單,因此我們先討論它。這個(gè)表將包含每位美國總統(tǒng)的一些基本信息:
■ 姓名。姓名在一個(gè)表中可用幾種方式表示。如,可以用一個(gè)單一的列來存放完整的姓名,或者用分開的列來分別容納名和姓。當(dāng)然用單一的列更為簡單,但是在使用上會(huì)帶來一些限制,如:
■ 如果先輸入只有名的姓名,則不可能對姓進(jìn)行排序。
■ 如果先輸入只有姓的姓名,就不可能對具有名的姓名進(jìn)行顯示。
■ 難以對姓名進(jìn)行搜索。例如,如果要搜索某個(gè)特定的姓,則必須使用一個(gè)特定的模式,并且查找與這個(gè)模式匹配的姓名。這樣較之只查找姓效率更低和更慢。member 表將使用單獨(dú)的名和姓的列以避免這些限制。名列還存放中名(注:西方國家的姓名一般將名放在前,姓放在后,而且除了有名和姓外,有時(shí)還有中名,這是在位置上介于名和姓之間的中間名字)或首字母。這樣應(yīng)該不會(huì)削弱我們可能進(jìn)行的任何一種排序,因?yàn)橐话悴豢赡軐χ忻M(jìn)行排序(或者甚至不會(huì)對名進(jìn)行排序)。姓名即可以“ Bush, George W. ”格式顯示,也可以“G e o rge W. B u s h”格式顯示。還有一種稍顯復(fù)雜一點(diǎn)的情形。一個(gè)總統(tǒng)( Jimmy Carter)在其姓名的末尾處有一
個(gè)“ J r. ”,這時(shí)怎樣做?根據(jù)名字打印的格式,這個(gè)總統(tǒng)的姓名顯示為“ J a m e s E . C a r t e r, J r.”或“C a r t e r, James E., Jr.”,“J r.”與名和姓都沒有關(guān)系,因此我們將建另外一個(gè)字段來存放姓名的后綴。這表明在試圖確定怎樣表示數(shù)據(jù)時(shí),即使一個(gè)特殊的值也可能會(huì)帶來問題。它也表明,為什么在將數(shù)據(jù)放入數(shù)據(jù)庫前,盡量對數(shù)據(jù)值的類型進(jìn)行了解是一個(gè)很好的想法。如果對數(shù)據(jù)了解不夠,那么有可能在已經(jīng)開始使用一個(gè)表后,不得不更改該表的結(jié)構(gòu)。這不一定是個(gè)災(zāi)難,但通常應(yīng)該避免。
■ 出生地(城市和州)。就像姓名一樣,出生地也可以用單個(gè)列或多個(gè)列來表示。使用單列更為簡單些,但正如姓名中的情形一樣,獨(dú)立的多個(gè)列使我們可以完成用單個(gè)列不方便完成的事情。例如,如果城市和州分別給出,查找各位總統(tǒng)出生在哪個(gè)州的記錄就會(huì)更容易一些。
■ 出生日期和死亡日期。這里,唯一特殊的問題是我們不能要求都填上死亡日期,因?yàn)橛械目偨y(tǒng)現(xiàn)在還健在。MySQL 提供了一個(gè)特殊的值N U L L,表示“無值”,可將其用在死亡日期列中以表示“仍然健在”。
(2) member 表
存儲(chǔ)歷史同盟會(huì)員清單的member 表在每個(gè)記錄都包含單個(gè)人員的基本描述信息這一點(diǎn)上,類似于president 表。但是每個(gè)member 的記錄所含的列更多,member 表的各列如下:
■ 姓名。使用如president 表一樣的三個(gè)列來表示:姓、名(如果可能的話還有中名)、后綴。
■ ID 號。這是開始記錄會(huì)員時(shí)賦給每個(gè)會(huì)員的唯一值。以前同盟未用ID 號,但現(xiàn)在的記錄做得更有系統(tǒng)性,所以最好開始使用ID 號。(我希望您找到有利于使用M y S Q L并考慮到其他的將它用于歷史同盟記錄的方法。使用數(shù)字,將member 表中的記錄與其他與會(huì)員有關(guān)的表中的記錄相關(guān)聯(lián)要更容易一些。)
■ 截止日期。會(huì)員必須定期更新他們的會(huì)員資格以免作廢。對于某些應(yīng)用,可能會(huì)用到最近更新的日期,但是近更新日期不適合于歷史同盟。會(huì)員資格可在可變的年數(shù)內(nèi)(一般為一年、二年、三年或五年)更新,而最近更新的日期將不能表示下一次更新必須在何時(shí)進(jìn)行。此外,歷史同盟還允許有終生會(huì)員。我們可以用未來一個(gè)很長的日期來表示終生會(huì)員,但是用NULL 似乎更為合適,因?yàn)?#8220;無值”在邏輯上對應(yīng)于“永不終止”。
■ 電子郵件地址。對于有電子郵件地址的會(huì)員,這將使他們能很容易地進(jìn)行相互之間的通信。作為歷史同盟秘書,這使您能電子化地發(fā)送更新通知給會(huì)員,而用不著發(fā)郵政信函。這比到郵局發(fā)送信函更容易,而且也不貴。還可以用電子郵件給會(huì)員發(fā)送他們的地址名錄條目的當(dāng)前內(nèi)容,并要求他們在有必要時(shí)更新信息。
■ 郵政地址。這是與沒有電子郵件(或沒有返回信息)的會(huì)員聯(lián)絡(luò)所需要的。將分別使用街道地址、城市、州和Zip 號。街道地址列又可以用于有諸如P.O. Box 123 而不是123 Elm St. 的會(huì)員的信箱號。我們假定所有同盟會(huì)員全都住在美國。當(dāng)然,對于具有國際會(huì)員的機(jī)構(gòu),此假設(shè)過于簡化了。如果希望處理多個(gè)國家的地址,還需要對不同國家的地址格式作一些工作。例如,這里的Zip 號就不是一個(gè)國際標(biāo)準(zhǔn),有的國家有省而不是州。
■ 電話號碼。與地址字段一樣,這個(gè)列對于聯(lián)絡(luò)會(huì)員也是很有用的。
■ 特殊愛好的關(guān)鍵詞。假定每個(gè)會(huì)員一般都對美國歷史都有興趣,但可能有的會(huì)員對某些領(lǐng)域有特殊的興趣。此列記錄了這些特殊的興趣。會(huì)員可以利用這個(gè)信息來找到其他具有類似興趣的會(huì)員。
(3) 創(chuàng)建表
現(xiàn)在我們已經(jīng)作好了創(chuàng)建歷史同盟表的準(zhǔn)備。我們用C R E ATE TABLE 語句來完成這項(xiàng)工作,其一般格式如下:

其中tbl_name 代表希望賦予表的名稱。column_specs 給出表中列的說明,以及索引的說明(如果有的話)。索引能使查找更快;我們將在第4 章“查詢優(yōu)化”中對其作進(jìn)一步的介紹。
president 表的C R E ATE TABLE 語句如下所示:

如果想自己鍵入這條語句,則調(diào)用m y s q l,使samp_db 為當(dāng)前數(shù)據(jù)庫:

然后,鍵入如上所示的C R E ATE TABLE 語句。(請記住,語句結(jié)尾要增加一個(gè)分號,否則mysql 將不知道哪兒是語句的結(jié)尾。)
為了利用來自樣例數(shù)據(jù)庫分發(fā)包的預(yù)先寫下的描述文件來創(chuàng)建president 表,可從外殼程序運(yùn)行下列命令:

不管用哪種方法調(diào)用m y s q l,都應(yīng)該在命令行中數(shù)據(jù)庫名的前面指定連接參數(shù)(主機(jī)名、用戶名或口令)。C R E ATE TABLE 語句中每個(gè)列的說明由列名、類型(該列將存儲(chǔ)的值的種類)以及一些可能的列屬性組成。president 表中所用的兩種列類型為VARCHAR 和D AT E。VA R C H A R(n)代表該列包含可變長度的字符(串)值,其最大長度為n 個(gè)字符??筛鶕?jù)期望字符串能有多長來選擇n 值。
state 定義為VA R C H A R ( 2 );即所有州名都只用其兩個(gè)字符的縮寫來表示。其他的字符串列則需要更長一些,以便存放更長的值。
我們使用過的其他列類型為D AT E。這種列類型表示該列存儲(chǔ)的是日期值,這一點(diǎn)也不令人吃驚。而令人吃驚的是,日期的表示以年份開頭。其標(biāo)準(zhǔn)格式為“ Y Y Y Y- M M - D D”(例如,“1 9 9 9 - 0 7 - 1 8”)。這是日期表示的ANSI SQL 標(biāo)準(zhǔn)。我們用于president 表的唯一列屬性為N U L L(值可以缺少)和NOT NULL(必須填充值)。多數(shù)列是NOT NULL 的,因?yàn)槲覀兛傄幸粋€(gè)它們的值??捎蠳ULL 值的兩個(gè)列是s u ff i x(多數(shù)姓名沒有后綴)和d e a t h(有的總統(tǒng)仍然健在,所以沒有死亡日期)。member 表的C R E ATE TABLE 語句如下所示:

將此語句鍵入mysql 或執(zhí)行下列外殼程序命令:

從列的類型來看,member 表并不很有趣:所有列中,除了一列之外,其他列都是可變長字符串。這個(gè)例外的列就是e x p i r a t i o n,為D ATE 型。終止日期值有一個(gè)缺省值為“0 0 0 0 - 0 0 -0 0”,這是一個(gè)非NULL 的值,它表示未輸入合法的日期值。這樣做的原因是expiration 可以是N U L L,它表示一個(gè)會(huì)員是終身會(huì)員。但是,因?yàn)榇肆锌梢詾镹 U L L,除非另外指定一個(gè)不同的值,否則它將取缺省值“ 0 0 0 0 - 0 0 - 0 0”。如果創(chuàng)建了一個(gè)新會(huì)員記錄,但忘了指定終止日期,該會(huì)員將成為一個(gè)終身會(huì)員!通過采用缺省值“ 0 0 0 0 - 0 0 - 0 0”的方法,避免了這個(gè)問題。它還向我們提供了一種手段,即可以定期地搜索這個(gè)值,以找出過去未正確輸入終止日期的記錄。
請注意,我們“忘了”放入會(huì)員ID 號的列。這是專門為了以后練習(xí)使用A LTER TA B L E語句而遺留下的?,F(xiàn)在讓我們來驗(yàn)證一下MySQL 是否確實(shí)如我們所期望的那樣創(chuàng)建了表。在mysql 中,發(fā)布下列查詢:

與MySQL 3.23 一樣,此輸出還包括了顯示訪問權(quán)限信息的另一個(gè)列,這里沒有給出,
因?yàn)樗姑啃刑L,不易顯示。
這個(gè)輸出結(jié)果看上去和我們所期望的非常一致,除了state 列的信息顯示它的類型為C H A R ( 2 )。這就有點(diǎn)古怪了,我們不是定義它為VARCHAR(2) 了嗎?是的,是這樣定義的,但是MySQL 已經(jīng)悄悄地將此類型從VARCHAR 換成了C H A R。原因是為了使短字符串列的
存儲(chǔ)空間利用更為有效,這里不多討論。如果希望詳細(xì)了解,可參閱第3 章中關(guān)于A LT E RTABLE 語句的介紹。但對這里的使用來說,兩種類型沒有什么差別。
如果發(fā)布一個(gè)DESCRIBE member 查詢,mysql 也會(huì)顯示member 表的類似信息。DESCRIBE 在您忘了表中的列名、需要知道列的類型、了解列有多寬等的時(shí)候很有用。它對于了解MySQL 存儲(chǔ)表行中列的次序也很有用。列的這個(gè)存儲(chǔ)次序在使用I N S E RT 或LOAD DATA 語句時(shí)非常重要,因?yàn)檫@些語句期望列值以缺省列的次序列出。DESCRIBE 可以省寫為D E S C,或者,如果您喜歡鍵入較多字符,則D E S C R I B Etbl_name 另一個(gè)等同的語句為SHOW COLUMNS FROM tbl_name。
如果忘了表名怎么辦?這時(shí)可以使用SHOW TA B L E S。對于samp_db 數(shù)據(jù)庫,我們目前
為止創(chuàng)建了兩個(gè)表,其輸出結(jié)果如下:

如果您甚至連數(shù)據(jù)庫名都記不住,可在命令行上調(diào)用mysql 而不用給出數(shù)據(jù)庫名,然后發(fā)布SHOW DATABASES 查詢:

數(shù)據(jù)庫的列表在不同的服務(wù)器上是不同的,但是至少可以看到samp_db 和m y s q l;后一個(gè)數(shù)據(jù)庫存放控制MySQL 訪問權(quán)限的授權(quán)表。DESCRIBE 與SHOW 查詢具有可從外殼程序中使用的命令行等同物,如下:
% mysqlshow 與SHOW DATABASES 一樣列出所有數(shù)據(jù)庫
% mysqlshow d b _ n a m e 與SHOW TABLES 一樣列出給定數(shù)據(jù)庫的表
% mysqlshow db_name tbl_name 與DESCRIBE tbl_name 一樣,列出給定表中的列
2. 用于學(xué)分保存方案的表
為了知道學(xué)分保存方案需要什么表,我們來看看在原來學(xué)分簿上是怎樣記學(xué)分的。圖1 - 2示出學(xué)分簿的一頁。該頁的主體是一個(gè)記錄學(xué)分矩陣。還有一些對學(xué)分有意義的必要信息。學(xué)生名和ID 號列在矩陣的一端。(為了簡單好看,只列出了四個(gè)學(xué)生。)在矩陣頂端,記錄了進(jìn)行測驗(yàn)和測試的日期。圖中示出9月3號、6號、1 6號和2 3號進(jìn)行測驗(yàn), 9月9號和1 0月1號進(jìn)
行測試。
為了利用數(shù)據(jù)庫來記錄這些信息,需要一個(gè)學(xué)分表。這個(gè)表中應(yīng)該包含什么記錄呢?很明顯,每一行都需要有學(xué)生名、測驗(yàn)或測試的日期以及學(xué)分。圖1-3 示出了用這樣的表表示的一些來自學(xué)分簿的學(xué)分。(日期以MySQL 的表示格式“Y Y Y Y- M M - D D”表示。)

但是,以這種方式設(shè)置表似乎有點(diǎn)問題。好像少了點(diǎn)什么。請看圖1 - 3中的記錄,我們分辨不出是測驗(yàn)的學(xué)分還是測試的學(xué)分。如果測驗(yàn)和測試的學(xué)分權(quán)重不同,在確定最終的學(xué)分等級時(shí)知道學(xué)分的類型是很重要的?;蛟S可以試著從學(xué)分的取值范圍來確定學(xué)分的類型(測驗(yàn)的學(xué)分一般比測試的學(xué)分少),但是這樣做很不方便,因?yàn)檫@需要進(jìn)行判斷,而且在數(shù)據(jù)中也不明顯??梢酝ㄟ^記錄學(xué)分的類型來進(jìn)行區(qū)分,如對學(xué)分表增加一列,此列包含“ T”或“Q”以表示是“測試”或是“測驗(yàn)”,如圖1-4 所示。這具有使學(xué)分?jǐn)?shù)據(jù)類型清析易辨的優(yōu)點(diǎn)。不利的地方是這個(gè)信息有點(diǎn)冗余。顯然對具有同一給定日期的記錄,學(xué)分的類型列總是取相同的值。9月2 3日的學(xué)分總是為“ Q”類型,而1 0月1日的學(xué)分其類型總是具有“ T”類型。這樣令人很不滿意。如果我們以這種方式記錄一組測驗(yàn)或測試的學(xué)分,不僅要為每個(gè)新記錄輸入相同的日期,而且還要一再重復(fù)地輸入相同的學(xué)分類型。誰會(huì)希望一再輸入冗余的信息呢?

我們可以試試另外一種表示。不在score 表中記錄學(xué)分類型,而是從日期上區(qū)分它們。我們可以做一個(gè)日期列表,用它來記錄每個(gè)日期發(fā)生的“學(xué)分事件”(測驗(yàn)或測試)。然后可以將學(xué)分與這個(gè)事件列表中的信息結(jié)合,確定學(xué)分是測驗(yàn)學(xué)分還是測試學(xué)分。這只要將score 表記錄中的日期與event 表中的日期相匹配得出事件類型即可。圖1 - 5示出這個(gè)表的設(shè)計(jì)并演示了score 表記錄與9月2 3日這個(gè)日期相關(guān)聯(lián)的工作。通過將score 表中的記錄與event 表中記錄相對應(yīng),我們知道這個(gè)學(xué)分來自測驗(yàn)。

這比根據(jù)某些猜測來推斷學(xué)分類型要好得多;我們可以根據(jù)明確記錄在數(shù)據(jù)庫中的數(shù)據(jù)來直接得到學(xué)分類型。這也比在score 表中記錄學(xué)分類型更好,因?yàn)槲覀冎恍鑼γ總€(gè)類型記錄一次。
但是,在第一次聽到這種事情時(shí)(即結(jié)合使用多個(gè)表中的信息),可能會(huì)想,“嗯,這是一個(gè)好主意,但是不是要做很多工作呢?會(huì)不會(huì)使工作更復(fù)雜了?”在某種程度上,這種想法是對的。處理兩個(gè)記錄表比處理一個(gè)要復(fù)雜。但是再來考察一下學(xué)分簿(見圖1 - 2)。不是也記錄了兩套東西嗎?考慮下列事實(shí):
■ 在學(xué)分矩陣中用兩個(gè)單元記錄學(xué)分,其中每個(gè)單元都是按學(xué)生名字和日期(在矩陣的旁邊和頂上)進(jìn)行索引的。這代表了一組記錄;與score 表的作用相同。
■ 怎樣知道每個(gè)日期代表的事件類型呢?在日期上方寫了字符“ T”或“Q”!因此,也在矩陣頂上記錄了日期和學(xué)分類型之間的關(guān)系。它代表第二組記錄;與event 表的作用相同。
換句話說,這里建議在兩個(gè)表中記錄信息與用學(xué)分簿記錄信息所做的工作沒什么不同。唯一不同的是,這兩組信息在學(xué)分簿中不是那么明顯地被分開。在圖1 - 5中所示的event 表的設(shè)計(jì)中加了一個(gè)要求,那就是日期必須是唯一的,因?yàn)橐盟B接score 與event 表的記錄。換句話說,同一天不能進(jìn)行兩次測驗(yàn),或者同一天不能進(jìn)行一次測驗(yàn)和一次測試。否則,將會(huì)在score 表中有兩個(gè)記錄并且在event 表中也有兩個(gè)記錄,全都具有相同的日期,這時(shí)就不知道應(yīng)如何將score 的記錄與event 的記錄進(jìn)行匹配。如果每天不多于一個(gè)學(xué)分事件,這就是一個(gè)永遠(yuǎn)不會(huì)出現(xiàn)的問題,可是事實(shí)并非如此簡單。有時(shí),一天中可能會(huì)有不止一個(gè)學(xué)分事件。我常聽有的人說他們的數(shù)據(jù),“那種古怪情況從不會(huì)出現(xiàn)。”然而,如果這種情況確實(shí)出現(xiàn)時(shí),就必須重新設(shè)計(jì)表以適應(yīng)這種情況引起的問題。最好是預(yù)先考慮以后可能出現(xiàn)的問題,并預(yù)先準(zhǔn)備好怎樣處理他們。因此,我們假定有時(shí)可能會(huì)需要同一天記錄兩組學(xué)分。我們怎樣處理呢?如果出現(xiàn)這種情況,問題并不難解決。只要對處理數(shù)據(jù)的方式作一點(diǎn)小的更改,就可使同一日期上有多個(gè)事件而不會(huì)引起問題:
1) 增加一個(gè)列到event 表,并用它來給表中每個(gè)記錄分配一個(gè)唯一的編號。實(shí)際上這就給了每個(gè)事件一個(gè)唯一的ID 號,因此我們稱該列為event_id 列。(如果覺得這好像是做傻事,可看一下圖1-2 中的學(xué)分簿,其中已經(jīng)有這個(gè)特征了。事件ID 正好與學(xué)分簿分?jǐn)?shù)矩陣中列號相似。這個(gè)編號可能沒有清晰地寫在那兒并標(biāo)上“事件I D,”但是它確實(shí)在那兒。)
2) 當(dāng)向score 表中輸入學(xué)分時(shí),輸入的是事件ID 而不是日期。這些改變的結(jié)果如圖1-6 所示?,F(xiàn)在連接score 和event 表時(shí),用的是事件ID 而不是日期,而且不僅用event 表來決定每個(gè)學(xué)分的類型,而且還用它來決定其日期。并且在event 表中不再有日期必須唯一這個(gè)限制,而唯一的是事件I D。這表示同一天可以有一打測試和測驗(yàn),而且能夠在記錄里邊直接保存它們。(毫無疑問,學(xué)生們聽到這個(gè)一定渾身發(fā)抖。)不幸的是,從人的觀點(diǎn)來看,圖1-6 中的表設(shè)計(jì)較前一個(gè)更不能令人滿意。score 表也更為抽象一些,因?yàn)樗膹闹庇^上可以理解的列更少。而圖1-4 中此表的設(shè)計(jì)直觀且容易理解,因?yàn)槟莻€(gè)score 表具有日期和學(xué)分類型的列。當(dāng)前的score 表如圖1-6 所示,日期和學(xué)分類型的列都沒有了。這極大地去除了作為人能夠很容易考慮的一切。誰希望看到其中有“事件I D”的score 表?如果有的話,也不代表我們大多數(shù)人。

此時(shí),可看到能夠電子化地完成學(xué)分記錄,且在賦予學(xué)分等級時(shí)不必做各種乏味的手工計(jì)算。但是,在考慮了如何實(shí)際在一個(gè)數(shù)據(jù)庫中表示學(xué)分信息后,又會(huì)被怎樣抽象和拆分組成學(xué)分信息的表示難住了。自然會(huì)產(chǎn)生一個(gè)問題:“根本不使用數(shù)據(jù)庫可能會(huì)更好一些?或許MySQL 不適合我?”正如您所猜測的那樣,筆者將從否定的方面對這個(gè)問題進(jìn)行回答,否則這本書就沒必要再往下寫了。不過,在考慮如何做一件工作時(shí),應(yīng)考慮各種情況并提問是否最好不使用數(shù)據(jù)庫系統(tǒng)(如M y S Q L)而使用一些別的東西(如電子表格等):
■ 學(xué)分簿有行和列,而電子表格也有。這使學(xué)分簿和電子表格在概念上和外觀上都非常類似。
■ 電子表格能夠完成計(jì)算,可以利用一個(gè)計(jì)算字段來累計(jì)每個(gè)學(xué)生的學(xué)分。但是,要對測驗(yàn)和測試進(jìn)行加權(quán)可能有點(diǎn)麻煩,但這也是可以辦得到的。另一方面,如果希望只查看某部分?jǐn)?shù)據(jù)(如只查看學(xué)分或測試),進(jìn)行諸如男孩與女孩的比較,或以一種靈活的方式顯示合計(jì)信息等,情況又大有不同了。電子表格的功能顯得要差一些,而關(guān)系數(shù)據(jù)庫系統(tǒng)完成這些工作相當(dāng)容易。另外要考慮的一點(diǎn)是為了在關(guān)系數(shù)據(jù)庫中進(jìn)行表示而對數(shù)據(jù)進(jìn)行抽象和分解,這個(gè)問題并不真的那么難以應(yīng)付。只要考慮安排數(shù)據(jù)庫使其不會(huì)以一種對您希望做的事無意義的方式來表示數(shù)據(jù)即可。但是,在確定了表示方式之后,就要靠數(shù)據(jù)庫引擎來協(xié)調(diào)和表示數(shù)據(jù)了。您肯定不會(huì)希望將它視為一堆支離破碎的東西。
例如,在從score 表中檢索學(xué)分時(shí),不希望看到事件I D;但希望看到日期。這沒有什么問題。數(shù)據(jù)庫將會(huì)根據(jù)事件ID 從event 表中查找出日期。您還可能想要看看是測驗(yàn)的學(xué)分或測試的學(xué)分。這也不成問題。數(shù)據(jù)庫將用相同的方法查找出學(xué)分類型,也是利用事件I D。請記住,這就是如像MySQL 這樣的關(guān)系數(shù)據(jù)庫的優(yōu)勢所在,即,使一樣?xùn)|西與另一樣?xùn)|西相關(guān)聯(lián),以便從多個(gè)來源得出信息并以您實(shí)際想看到的形式提供出來。在學(xué)分保存數(shù)據(jù)的情況中,MySQL 確實(shí)利用事件ID 將信息組合到了一起,而無需人工來完成這件事。
現(xiàn)在我們先來看看,如何使MySQL 完成這種將一個(gè)東西與另一個(gè)東西相聯(lián)系的工作。
假定希望看到1 9 9 9年9月2 3號的學(xué)分,針對某個(gè)特定日期中給出的事件的學(xué)分查詢?nèi)缦滤荆?br>
相當(dāng)嚇人,是嗎?這個(gè)查詢通過將score 表的記錄與event 表的記錄連接(關(guān)聯(lián))來檢索學(xué)生名、日期、學(xué)分和學(xué)分的類型。其結(jié)果如下所示:

您肯定注意到了,它與圖1-4 中給出的表設(shè)計(jì)相同,而且不需要知道事件ID 就可得出這個(gè)結(jié)果,只需指出感興趣的日期并讓MySQL 查找出哪個(gè)學(xué)分記錄具有該日期即可。如果您一直擔(dān)心抽象和分解會(huì)使我們損失一些東西的話,看到這個(gè)世界,就不會(huì)有這種擔(dān)心了。
當(dāng)然,在考慮過查詢后,您還可能對其他別的東西產(chǎn)生擔(dān)心。即,這個(gè)查詢看上去有點(diǎn)長并且也有點(diǎn)復(fù)雜;是不是做了很多工作寫出這樣的東西只是為了查找某個(gè)給定日期的學(xué)分?是的,確實(shí)是這樣。但是,在每次想要發(fā)布一個(gè)查詢時(shí),有幾種方法可以避免鍵入多行的S Q L。一般情況下,一旦您決定如何執(zhí)行這樣一個(gè)查詢并將它保存起來后,就可以按需要多次執(zhí)行它。我們將在1 . 5節(jié)“與mysql 交互的技巧”中介紹怎樣完成這項(xiàng)工作。
在上述查詢的介紹中,我們有點(diǎn)超前了。不過,這個(gè)查詢比起我們要實(shí)際用來得出學(xué)分的查詢是有點(diǎn)簡單了。原因是,我們還要對表的設(shè)計(jì)作更多的修改。我們將采用一個(gè)唯一的學(xué)生I D,而不在score 表中記錄學(xué)生名。(即,我們將使用來自學(xué)分簿的“ I D”列的值而不是來自“ N a m e”列的值。)然后,創(chuàng)建另一個(gè)稱為student 的表來存放name 和student_id 列(見圖1 - 7)。

為什么要作出這種修改呢?只有一個(gè)原因,可能有兩個(gè)學(xué)生有相同的名字。采用唯一的學(xué)生ID 號可幫助區(qū)分他們的學(xué)分。(這與利用唯一的事件ID 而不是日期來分辨出相同日期的測試或測驗(yàn)完全類似。)在對表的設(shè)計(jì)作了這樣的修改后,實(shí)際用來獲得給定日期的學(xué)分查詢變得更為復(fù)雜了一些,這個(gè)查詢?nèi)缦拢?br>
如果您不能立即清楚地讀懂這個(gè)查詢的意思的話,也不必?fù)?dān)心。在進(jìn)一步深入這個(gè)教程之后,就能看懂這個(gè)查詢了。將會(huì)從圖1 - 7中注意到,在student 表中增加了點(diǎn)學(xué)分簿中沒有的東西。它包含了一個(gè)性別列。這便可以做一些簡單的事情,如對班級中男孩和女孩的人數(shù)計(jì)數(shù);也可以做一些更為復(fù)雜的事情,如比較男孩和女孩的學(xué)分。我們已經(jīng)設(shè)計(jì)完了學(xué)分保存的幾乎所有的表?,F(xiàn)在只需要另外
一個(gè)表來記錄出勤情況即可。這個(gè)表的內(nèi)容相對較為直觀,即,一個(gè)學(xué)生ID 號和一個(gè)日期(見圖1 - 8)。表中的每行表示特定的學(xué)生在
給定的日期缺勤。在學(xué)分時(shí)段末,我們將調(diào)用MySQL 的計(jì)數(shù)功能來匯總此表的內(nèi)容,以便得出每個(gè)學(xué)生的缺勤數(shù)。

既然現(xiàn)在已經(jīng)知道學(xué)分保存的各個(gè)表的結(jié)構(gòu),現(xiàn)在可以創(chuàng)建它們了。student 表的C R E ATE TABLE 語句如下:

將上述語句鍵入mysql 或執(zhí)行下列外殼程序命令:

C R E ATE TABLE 語句創(chuàng)建了一個(gè)名為student 的表,它含有三列,分別為: n a m e、s e x和s t u d e n t _ i d。name 是一個(gè)可變長的字符串列,最多可存放20 個(gè)字符。這個(gè)名字的表示比歷史同盟表中所用的表示要簡單,它只用了單一的列而不是分別的名和姓列。這是因?yàn)槲覀円呀?jīng)預(yù)先知道,不存在無需做另外的工作就使得在多個(gè)列上工作得更好的查詢樣例。sex 表示學(xué)生是男孩還是女孩。這是一個(gè)E N U M(枚舉)列,表示只能取明確地列在說明中的值之一,這里列出的值為:“F”和“M”,分別表示女和男。在某列只具有一組有限值時(shí),ENUM 類型非常有用。我們可以用CHAR(1) 來代替它,但是ENUM 更明確規(guī)定了列可以取什么值。如果對包括一個(gè)ENUM 列的表發(fā)布一條DESCRIBE tbl_name 語句,MySQL 將確切地顯示可取的值有哪些。順便說一下, ENUM 列中的值不一定只是單個(gè)字符。此列還可以定義為E N U M(‘f e m a l e’,‘m a l e’)。
student_id 為一個(gè)整數(shù)型列,它將包含唯一的ID 號。通常,大概會(huì)從一個(gè)中心資料來源處(如學(xué)校辦公室)取得學(xué)生的ID 號,但在這里是我們自己定的。雖然student_id 列只包含一個(gè)數(shù),但其定義包括幾個(gè)部分:
■ INT 說明此列的值必須取整數(shù)(即無小數(shù)部分)。
■ UNSIGNED 不允許負(fù)數(shù)。
■ NOT NULL 表示此列的值必須填入。(任何學(xué)生都必須有一個(gè)ID 號。)
■ A U TO_INCREMENT 是MySQL 中的一個(gè)特殊的屬性。其作用為:如果在創(chuàng)建一個(gè)新的student 表記錄時(shí)遺漏了student_id 的值(或?yàn)镹 U L L),MySQL 自動(dòng)地生成一個(gè)大于當(dāng)前此列中最大值的唯一ID 號。在錄入學(xué)生表時(shí)將用到這個(gè)這特性,錄入學(xué)生表時(shí)可以只給出name 和sex 的值,讓MySQL 自動(dòng)生成student_id 列值。
■ P R I M A RY KEY 表示相應(yīng)列的值為快速查找進(jìn)行索引,并且列中的每個(gè)值都必須是惟一的。這樣可防止同一名字的I D出現(xiàn)兩次,這對于學(xué)生ID 號來說是一個(gè)必須的特性。(不僅如此,而且MySQL 還要求每個(gè)A U TO_INCREMENT 列都具有一個(gè)惟一索引。)如果您不理解A U TO_INCREMENT 和P R I M A RY KEY 的含義,只要將其想像為一種為每個(gè)學(xué)生產(chǎn)生ID 號的魔術(shù)方法即可。除了要求值唯一外,沒有什么別的東西。請注意:如果確實(shí)打算從學(xué)校辦公室取得學(xué)生ID 號而不是自動(dòng)生成它們,則可以按相同的方法定義student_id 列,只不過不定義A U TO_INCREMENT 屬性即可。event 表如下定義:

將此語句鍵入mysql 或執(zhí)行下列外殼程序的命令:

所有列都定義為NOT NULL,因?yàn)樗鼈冎腥魏我粋€(gè)值都不能省略。date 列存儲(chǔ)標(biāo)準(zhǔn)的MySQL DATE 日期值,格式為“Y Y Y Y- M M - D D”(首先是年)。type 代表學(xué)分類型。像student 表中的sex 一樣,type 也是一個(gè)枚舉列。所允許的值為“T”和“Q”,分別表示“測試”和“測驗(yàn)”。event_id 是一個(gè)AUTO_INCREMENT 列,類似于student 表中的student_id 列。采用AUTO_INCREMENT 允許生成唯一的事件ID 值。正如student 表中的student_id 列一樣,與值的惟一性相比,某個(gè)特定的值并不重要。score 表如下定義:

將此語句鍵入mysql 或執(zhí)行下列外殼程序的命令:

score 為一個(gè)INT (整型)列。即,假定學(xué)分值總是為一個(gè)整數(shù)。如果希望使學(xué)分值具有小數(shù)部分,如5 8 . 5,應(yīng)該采用浮點(diǎn)列類型,如F L O AT 或D E C I M A L。student_id 列和event_id 列都是整型,分別表示每個(gè)學(xué)分所對應(yīng)的學(xué)生和事件。通過利用它們來連接到student 和event 表,我們能夠知道學(xué)生名和事件的日期。我們將兩個(gè)列組成了P R I M A RY KEY。這保證我們不會(huì)對同一測驗(yàn)或測試重復(fù)一個(gè)學(xué)生的學(xué)分。而且,這樣還很容易在以后更改某個(gè)學(xué)分。例如,在發(fā)現(xiàn)學(xué)分錄入錯(cuò)時(shí),可以在利用MySQL 的R E P L A C E語句放入一個(gè)新記錄,替換掉舊的記錄。不需要執(zhí)行DELETE 語句與I N S E RT 語句;M y S Q L自動(dòng)替我們做了。請注意,它是惟一的event_id 和student_id 的組合。在score 表中,兩者自身都可能不惟一。一個(gè)event_id 值可有多個(gè)學(xué)分記錄(每個(gè)學(xué)生對應(yīng)一個(gè)記錄),而每個(gè)student_id 值都對應(yīng)多個(gè)記錄(每個(gè)測驗(yàn)和測試有一個(gè)記錄)。用于出勤情況的absence 表如下定義:

將此語句鍵入mysql 或執(zhí)行下列外殼程序的命令:

student_id 和date 列兩者都定義為NOT NULL,不允許省略值。應(yīng)定義這兩列的組合為主鍵,以免不當(dāng)心建立了重復(fù)的記錄。重要的是不要對同一天某個(gè)學(xué)生的缺曠進(jìn)行重復(fù)計(jì)數(shù)。
1.4.7 增加新記錄
至此,我們的數(shù)據(jù)庫及其表都已經(jīng)創(chuàng)建了,在下一節(jié)“檢索信息”中,我們將看到怎樣從數(shù)據(jù)庫中取出數(shù)據(jù)?,F(xiàn)在我們先將一些數(shù)據(jù)放入表中。在數(shù)據(jù)庫中加入數(shù)據(jù)有幾種方法??赏ㄟ^發(fā)布I N S E RT 語句手工將記錄插入某個(gè)表中。還可以通過從某個(gè)文件讀取它們來增加記錄,在這個(gè)文件中,記錄既可以是利用L O A DD ATA 語句或mysqlimport 實(shí)用程序裝入的原始數(shù)據(jù)值,也可以是預(yù)先寫成可饋入mysql 的I N S E RT 語句的形式。本節(jié)介紹將記錄插入表的每種方法。您所應(yīng)做的是演習(xí)各種方法以明了它們是如何起作用的。然后到本節(jié)結(jié)束處運(yùn)行那兒給出的命令來清除表并重裝它們。這樣做,能夠保證表中含有作者撰寫下一節(jié)時(shí)所處理的相同記錄,您也能得到相同的結(jié)果。讓我們開始利用I N S E RT 語句來增加記錄,這是一個(gè)SQL 語句,需要為它指定希望插入數(shù)據(jù)行的表或?qū)⒅蛋葱蟹湃氲谋?。I N S E RT 語句具有幾種形式:
■ 可指定所有列的值

例如:

“I N TO”一詞自MySQL 3.22.5 以來是可選的。(這一點(diǎn)對其他形式的I N S E RT 語句也成立。)VALUES 表必須包含表中每列的值,并且按表中列的存放次序給出。(一般,這就是創(chuàng)建表時(shí)列的定義次序。如果不能肯定的話,可使用DESCRIBE tbl_name 來查看這個(gè)次序。)在MySQL 中,可用單引號或雙引號將串和日期值括起來。上面例子中的N U L L值是用于student 和event 表中的A U TO_INCREMENT 列的。(插入“錯(cuò)誤”的值將導(dǎo)致下一個(gè)student_id 或event_id 號的自動(dòng)生成。)自3.22.5 以來的MySQL 版本允許通過指定多個(gè)值的列表,利用單個(gè)的I N S E RT語句將幾行插入一個(gè)表中,如下所示:

例如:

這比多個(gè)I N S E RT 語句的鍵入工作要少,而且服務(wù)器執(zhí)行的效率也更高。
■ 可以給出要賦值的那個(gè)列,然后再列出值。這對于希望建立只有幾個(gè)列需要初始設(shè)置的記錄是很有用的。

例如:

自MySQL 3.22.5 以來,這種形式的I N S E RT 也允許多個(gè)值表:

在列的列表中未給出名稱的列都將賦予缺省值。
■ 自MySQL 3.22 .10 以來,可以col_name = value 的形式給出列和值。

例如:

在SET 子句中未命名的行都賦予一個(gè)缺省值。使用這種形式的I N S E RT 語句不能插入多行。將記錄裝到表中的另一種方法是直接從文件讀取數(shù)據(jù)值??梢杂肔OAD DATA 語句或用mysqlimport 實(shí)用程序來裝入記錄。LOAD DATA 語句起批量裝載程序的作用,它從一個(gè)文件中讀取數(shù)據(jù)??稍趍ysql 內(nèi)使用它,如下所示:

該語句讀取位于客戶機(jī)上當(dāng)前目錄中數(shù)據(jù)文件m e m b e r.txt 的內(nèi)容,并將其發(fā)送到服務(wù)器裝入member 表。如果您的MySQL 版本低于3 . 2 2 . 1 5,則LOAD DATA LOCAL 不起作用,因?yàn)槟菚r(shí)從客戶機(jī)讀取數(shù)據(jù)的能力是在LOAD DATA 上的。(沒有LOCAL 關(guān)鍵字,被讀取的文件必須位于服務(wù)器主機(jī)上,并且需要大多數(shù)MySQL 用戶都不具備的服務(wù)器訪問權(quán)限。)缺省時(shí),LOAD DATA 語句假定列值由tab 鍵分隔,而行則以換行符結(jié)束。還假定各個(gè)值是按列在表中的存放次序給出的。也有可能需要讀取其他格式的文件,或者指定不同的列次
序。更詳細(xì)的內(nèi)容請參閱附錄D的LOAD DATA 的條款。mysqlimport 實(shí)用程序起LOAD DATA 的命令行接口的作用。從外殼程序調(diào)用mysqlimport ,它生成一個(gè)LOAD DATA 語句:

mysqlimport 生成一個(gè)LOAD DATA 語句,此語句使m e m b e r.txt 文件被裝入member 表。如果您的MySQL 版本低于3 . 2 2 . 1 5,這個(gè)實(shí)用程序不起作用,因?yàn)?-local 選項(xiàng)需要L O A DD ATA LOCAL。正如使用mysql 一樣,如果您需要指定連接參數(shù),可在命令行上數(shù)據(jù)庫名前指定它們。mysqlimport 從數(shù)據(jù)文件名中導(dǎo)出表名(它將文件名第一個(gè)圓點(diǎn)前的所有字符作為表名)。例如,m e m b e r.txt 將被裝入member 表,而president.txt 將被裝入president 表。如果您有多個(gè)需要裝入單個(gè)表的文件,應(yīng)仔細(xì)地選擇文件名,否則mysqlimport 將不能使用正確的表名。對于如像member1.txt 與member2.txt 這樣的文件名, mysqlimport 將會(huì)認(rèn)為相應(yīng)的表名為
member1 和m e m b e r 2。不過,可以使用如m e m b e r.1.txt 和m e m b e r.2.txt 或m e m b e r.txt1 和m e m b e r.txt2 這樣的文件名。在試用過這些記錄追加的方法后,應(yīng)該清除各個(gè)表并重新裝載它們,以便它們的內(nèi)容與下一節(jié)假定的內(nèi)容相同。從外殼程序執(zhí)行下列命令:

每個(gè)文件都含有一個(gè)刪除可能曾經(jīng)插入到表中的記錄的DELETE 語句,后跟一組INSERT 語句以初始化表的內(nèi)容。如果不希望分別鍵入這些命令,可試一下下列語句:

1.4.8 檢索信息
現(xiàn)在各個(gè)表已經(jīng)創(chuàng)建并裝有數(shù)據(jù)了,因此讓我們來看看可以對這些數(shù)據(jù)做點(diǎn)什么。SELECT 語句允許以一般的或特殊的方式檢索和顯示表中的信息。它可以顯示表的整個(gè)內(nèi)容:

或者只顯示單個(gè)行中單個(gè)列的內(nèi)容:

SELECT 語句有幾個(gè)子句(部件),可以根據(jù)需要用來檢索感興趣的信息。每個(gè)子句都可簡單、可復(fù)雜,從而SELECT 作為一個(gè)總的語句也繁簡皆宜。但是,可以放心,本書中不會(huì)有花一個(gè)鐘頭來編寫的長達(dá)數(shù)頁的查詢。(我在書中看到有很長的查詢時(shí),一般會(huì)立即跳過它們,因此我猜您也會(huì)這樣。)SELECT 語句的一般形式為:SELECT 要選擇的東西FROM 一個(gè)或多個(gè)表WHERE 數(shù)據(jù)必須滿足的條件記住,SQL 為一個(gè)自由格式的語言,因此在您編寫SELECT 查詢時(shí),語句的斷行不必嚴(yán)格依照本書。
為了編寫SELECT 語句,只需指定需要檢索什么,然后再選擇某些子句即可。剛才給出的子句“ F R O M”、“W H E R E”是最常用的,還有一些其他的子句,如GROUP BY、O R D E RBY 和LIMIT 等。FROM 子句一般都要給出,但是如果不從表中選擇數(shù)據(jù),也可不給出。例如,下列查詢只顯示某些可以直接計(jì)算而不必引用任何表的表達(dá)式的值,因此不需要用FROM 子句:

在確實(shí)使用一個(gè)FROM 子句指定了要從其中檢索數(shù)據(jù)的表時(shí), SELECT 語句的最“普通”的格式是檢索所有內(nèi)容。用“ *”來表示“所有列”。下面的查詢將從student 表中檢索所有行并顯示:

各列按它們MySQL 在表中存放的次序出現(xiàn)。該次序與發(fā)布DESCRIBE student 語句時(shí)顯示的列次序相同。(例子末尾的“. . .”表示此查詢返回的輸出行比這里顯示的還要多。)可明確地命名希望得到的一列或多列。如果只選擇學(xué)生名,發(fā)布下列語句:

如果名字不止一列,可用逗號分隔它們。下列的語句與SELECT * FROM student 等價(jià),只是明確地指出了每一列:

可按任意次序給出列:

如果有必要,同一列甚至也可以給出多次,雖然這樣做一般是沒有意義的。列名在MySQL 中不區(qū)分大小寫的。下面的查詢是等同的:

數(shù)據(jù)庫和表名有可能區(qū)分大小寫的;這有取決服務(wù)器主機(jī)上使用的文件系統(tǒng)。在U N I X上運(yùn)行的服務(wù)器對數(shù)據(jù)庫名和表名是區(qū)分大小寫的,因?yàn)閁NIX 的文件名是區(qū)分大小寫的。Windows 的文件名不區(qū)分大小寫,因此運(yùn)行在Windows 上的服務(wù)器對數(shù)據(jù)庫名和表名不區(qū)分
大小寫。MySQL 允許您一次從多個(gè)表中選擇列。我們將這個(gè)內(nèi)容留到“從多個(gè)表中檢索信息”小節(jié)去介紹。
1. 指定檢索條件
為了限制SELECT 語句檢索出來的記錄集,可使用WHERE 子句,它給出選擇行的條件??赏ㄟ^查找滿足各種條件的列值來選擇行。
可查找數(shù)字值:

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

可以查找日期值:

可搜索組合值:

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


在用表達(dá)式表示一個(gè)需要邏輯運(yùn)算的查詢時(shí),要注意別混淆邏輯與運(yùn)算符與我們平常使用的“與”的含義。假如希望查找“出生在Vi rginia 的總統(tǒng)與出生在Maryland 的總統(tǒng)”。應(yīng)該注意怎樣表示“與”的關(guān)系,能寫成如下的查詢嗎?

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


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

為了進(jìn)行NULL 值的搜索,必須采用特殊的語法。不能用= 或!= 來測試等于NULL 或不等于N U L L,取而代之的是使用IS NULL 或IS NOT NULL 來測試。例如,因?yàn)槲覀儗⒔≡诳偨y(tǒng)的死亡日期表示為N U L L,那么可按如下語句查找健在的總統(tǒng):

MySQL3.23 及以后的版本具有一個(gè)特殊的MySQL 專有的比較運(yùn)算符“ < = >”,即使是NULL 與NULL 的比較,它也是可行的。用這個(gè)比較運(yùn)算符,可將前面的兩個(gè)查詢重寫為:

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

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

如果在ORDER BY 子句中,對某個(gè)列名既不指定ASC 又不指定D E S C,則缺省的次序?yàn)樯颉T趯赡馨琋ULL 值的列進(jìn)行排序時(shí),如果是升序排序, NULL 值出現(xiàn)在最前面,如果是按降序排序,NULL 值出現(xiàn)在最后。
查詢結(jié)果可在多個(gè)列上進(jìn)行排序,而每個(gè)列的升序或降序可以互相獨(dú)立。下面的查詢從president 表中檢索行,并按出生的州降序、在每個(gè)州中再按姓氏的升序?qū)z索結(jié)果進(jìn)行排序:

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

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

自MySQL 3.23.2 以來,可按照一個(gè)公式來排序查詢結(jié)果。例如,利用ORDER BYRAND( ) 與LIMIT 結(jié)合,從president 表中隨機(jī)抽取一個(gè)記錄:


5. 計(jì)算并命名輸出的列值
前面的多數(shù)查詢通過從表中檢索值已經(jīng)產(chǎn)生了輸出結(jié)果。MySQL 還允許作為一個(gè)公式的結(jié)果來計(jì)算輸出列的值。表達(dá)式可以簡單也可以復(fù)雜。下面的查詢求一個(gè)簡單表達(dá)式的值(常量)以及一個(gè)涉及幾個(gè)算術(shù)運(yùn)算符和兩個(gè)函數(shù)調(diào)用的較復(fù)雜的表達(dá)式的值:

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


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

為了測試或檢索日期的成分,可使用諸如YEAR( )、MONTH( ) 或D AYOFMONTH( ) 這樣的函數(shù)。例如,可通過查找月份值為3 的日期,找出與筆者出生在相同月份(三月)的總統(tǒng)。


為了更詳細(xì),詳細(xì)到天,可組合測試MONTH( ) 和D AYOFMONTH( ) 以找出在筆者的生日出生的總統(tǒng):

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

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

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

自MySQL 3.22 以來,可使用D ATE_ADD( ) 或D ATE_SUB( ) 從一個(gè)日期計(jì)算另一個(gè)日期。這些函數(shù)取一個(gè)日期及時(shí)間間隔并產(chǎn)生一個(gè)新日期。例如:

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

會(huì)員更新查詢可根據(jù)D ATE_ADD( ) 寫出如下:

本章前面給出了一個(gè)查詢?nèi)缦拢_定不久要來檢查但還沒來診所的牙科病人:

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

此查詢給出了一個(gè)常見的錯(cuò)誤,它對一個(gè)算術(shù)比較運(yùn)算符使用了模式。這種比較成功的惟一可能是相應(yīng)的列確實(shí)包含串“ W %”或“w %”。下列模式匹配任意位置包含“W”或“w”的姓:


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

其他的匯總形式涉及計(jì)數(shù),可利用COUNT( ) 函數(shù)。如果使用COUNT (*),它將給出查詢所選擇的行數(shù)。如果一個(gè)查詢無WHERE 子句,COUNT(*) 將給出表中的行數(shù)。下列查詢給出共有多少人當(dāng)過美國總統(tǒng):

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


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

這表示,總共有41 位總統(tǒng),他們中只有一個(gè)具有名字后綴,并且大多數(shù)總統(tǒng)都已去世。自MySQL 3.23.2 以來,可以將COUNT( ) 與DISTINCT 組合對選擇結(jié)果集中不同的值進(jìn)行計(jì)數(shù)。例如,為了對總統(tǒng)出生的不同州進(jìn)行計(jì)數(shù),可執(zhí)行下列查詢:

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

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

雖然這個(gè)方法可行,但是它很繁鎖而且并不真正適合于可能有許多不同的值的列??紤]一下怎樣以這種方式確定每個(gè)州出生的總統(tǒng)人數(shù)。您不得不找出有哪些州,從而不能省略(SELECT DISTINCT state FROM president),然后對每個(gè)州執(zhí)行一個(gè)SELECT COUNT(*) 查詢。很顯然,有些事是可以簡化的。所幸MySQL 可以利用單個(gè)查詢對一個(gè)列中不同的值進(jìn)行計(jì)數(shù)。因此,針對學(xué)生表可以按如下得出男孩和女孩的人數(shù):


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


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

我不認(rèn)為按位置引用列易讀。如果增加、刪除或重新排序輸出列,必須注意檢查O R D E RBY 子句,并且如果列號改變后還得記住它。別名就不存在這種問題。如果想與計(jì)算出來的列一道使用GROUP BY,正如ORDER BY 一樣,應(yīng)該利用別名或列位置來引用它。下面的查詢確定在一年的每個(gè)月中出生的總統(tǒng)人數(shù):


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

從更為普遍的意義上說,這是一種在要查找的列中重復(fù)值時(shí)執(zhí)行的查詢類型。H AVING 類似于W H E R E,但它是在查詢結(jié)果已經(jīng)選出后才應(yīng)用的,用來縮減服務(wù)器實(shí)際送%