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

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

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

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

■ - p(可選擇的形式:- - p a s s w o r d)
這個選項告訴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 在啟動時提示您鍵入口令。例如:

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

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

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

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

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

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

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

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

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

請注意,提示符又變回了‘m y s q l >’,這表示mysql 為鍵入的新查詢作好了準(zhǔn)備??蓪⒉樵兇鎯υ谝粋€文件中并告訴mysql 從文件中讀取查詢而不是等待鍵盤輸入??衫猛鈿こ绦蜴I入重定向?qū)嵱贸绦騺硗瓿蛇@項工作。例如,如果在文件my_file.sql 中存放有查
詢,可如下執(zhí)行這些查詢:

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

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

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

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

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

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

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

其中tbl_name 代表希望賦予表的名稱。column_specs 給出表中列的說明,以及索引的說明(如果有的話)。索引能使查找更快;我們將在第4 章“查詢優(yōu)化”中對其作進一步的介紹。
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é)尾要增加一個分號,否則mysql 將不知道哪兒是語句的結(jié)尾。)
為了利用來自樣例數(shù)據(jù)庫分發(fā)包的預(yù)先寫下的描述文件來創(chuàng)建president 表,可從外殼程序運行下列命令:

不管用哪種方法調(diào)用m y s q l,都應(yīng)該在命令行中數(shù)據(jù)庫名的前面指定連接參數(shù)(主機名、用戶名或口令)。C R E ATE TABLE 語句中每個列的說明由列名、類型(該列將存儲的值的種類)以及一些可能的列屬性組成。president 表中所用的兩種列類型為VARCHAR 和D AT E。VA R C H A R(n)代表該列包含可變長度的字符(串)值,其最大長度為n 個字符。可根據(jù)期望字符串能有多長來選擇n 值。
state 定義為VA R C H A R ( 2 );即所有州名都只用其兩個字符的縮寫來表示。其他的字符串列則需要更長一些,以便存放更長的值。
我們使用過的其他列類型為D AT E。這種列類型表示該列存儲的是日期值,這一點也不令人吃驚。而令人吃驚的是,日期的表示以年份開頭。其標(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 的,因為我們總要有一個它們的值??捎蠳ULL 值的兩個列是s u ff i x(多數(shù)姓名沒有后綴)和d e a t h(有的總統(tǒng)仍然健在,所以沒有死亡日期)。member 表的C R E ATE TABLE 語句如下所示:

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

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

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

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

數(shù)據(jù)庫的列表在不同的服務(wù)器上是不同的,但是至少可以看到samp_db 和m y s q l;后一個數(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é)分簿的一頁。該頁的主體是一個記錄學(xué)分矩陣。還有一些對學(xué)分有意義的必要信息。學(xué)生名和ID 號列在矩陣的一端。(為了簡單好看,只列出了四個學(xué)生。)在矩陣頂端,記錄了進行測驗和測試的日期。圖中示出9月3號、6號、1 6號和2 3號進行測驗, 9月9號和1 0月1號進
行測試。
為了利用數(shù)據(jù)庫來記錄這些信息,需要一個學(xué)分表。這個表中應(yīng)該包含什么記錄呢?很明顯,每一行都需要有學(xué)生名、測驗或測試的日期以及學(xué)分。圖1-3 示出了用這樣的表表示的一些來自學(xué)分簿的學(xué)分。(日期以MySQL 的表示格式“Y Y Y Y- M M - D D”表示。)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

例如:

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

例如:

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

例如:

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

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

例如:

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

該語句讀取位于客戶機上當(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 不起作用,因為那時從客戶機讀取數(shù)據(jù)的能力是在LOAD DATA 上的。(沒有LOCAL 關(guān)鍵字,被讀取的文件必須位于服務(wù)器主機上,并且需要大多數(shù)MySQL 用戶都不具備的服務(wù)器訪問權(quán)限。)缺省時,LOAD DATA 語句假定列值由tab 鍵分隔,而行則以換行符結(jié)束。還假定各個值是按列在表中的存放次序給出的。也有可能需要讀取其他格式的文件,或者指定不同的列次
序。更詳細(xì)的內(nèi)容請參閱附錄D的LOAD DATA 的條款。mysqlimport 實用程序起LOAD DATA 的命令行接口的作用。從外殼程序調(diào)用mysqlimport ,它生成一個LOAD DATA 語句:

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

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

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

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

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

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

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

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

可按任意次序給出列:

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

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

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

可以查找日期值:

可搜索組合值:

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


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

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


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

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

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

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

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

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

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

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

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


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

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


6. 使用日期
在MySQL 中使用日期時要記住的是,在表示日期時首先給出年份。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īng)看到幾次了。)
■ 查找特定的日期或日期范圍。
■ 提取日期值的組成部分,如年、月或日。
■ 計算日期的差。
■ 日期增加或減去一個間隔得出另一日期。
下面給出一些日期運算的例子。
為了查找特定的日期,可使用精確的日期值或與其他日期值進行比較,將一個D ATE 列與有關(guān)的日期值進行比較:

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


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

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

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

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

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

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

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

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

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

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


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

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

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


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

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

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

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

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


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


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

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


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

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