|
現(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ù)器正巧運行在您的機器上,適當?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”。 我不認為哪個發(fā)音更好一些。愿意讀哪個音都可以,不過在您對別人讀的時候,他可能會用他認為是“正確”的發(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 用戶賬號的更詳細信息,可在第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 標準提示符之一;另一個為“$”。 在Windows 下,提示符類似“c : >”。 mysql 命令行的options 部分可能是空的,但更可能的是發(fā)布一條類似如下的命令:  在激活mysql 時,有可能不必提供所有這些選項;確切使用的命令請咨詢MySQL 管理員。此外,可能還需要至少指定一個名稱和一個口令。 在剛開始學(xué)習(xí)MySQL 時,大概會為其安全系統(tǒng)而煩惱,因為它使您難于做自己想做的事。(您必須取得創(chuàng)建和訪問數(shù)據(jù)庫的權(quán)限,任何時候連接到數(shù)據(jù)庫都必須給出自己的名字和口令。)但是,在您通過數(shù)據(jù)庫錄入和使用自己的記錄后,看法就會馬上改變了。這時您會很欣賞MySQL 阻止了其他人窺視(或者更惡劣一些,破壞~)您的資料。 下面介紹選項的含義: - 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 就認為您不需要口令,不作提示。請注意: -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é)果: 它給出當前的日期和時間。(NOW() 函數(shù)本身并無多大用處,但可將其用于表達式中。如比較當前日期和其他日期的差異。) 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 為鍵入的新查詢作好了準備??蓪⒉樵兇鎯υ谝粋€文件中并告訴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ù)庫并不是當前數(shù)據(jù)庫。這可從執(zhí)行下面的查詢看出: 為了使samp_db 成為當前數(shù)據(jù)庫,發(fā)布USE 語句即可: USE 為少數(shù)幾個不需要終結(jié)符的語句之一,當然,加上終結(jié)符也不會出錯。HELP 是另一個不需要終結(jié)符的語句。如果想了解不需要終結(jié)符的語句有哪些,可發(fā)布HELP 語句。在發(fā)布了USE 語句后,samp_db 成為缺省數(shù)據(jù)庫: 使數(shù)據(jù)庫成為當前數(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 使其成為當前數(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)系圖、標準程序以及諸如此類的東西。這里確實也可以講這些東西,但是我寧可只講點實用的東西,比方說,我們的數(shù)據(jù)庫應(yīng)該是怎樣的:數(shù)據(jù)庫中將包含什么內(nèi)容,每個表中有哪些數(shù)據(jù)以及由決定如何表示數(shù)據(jù)而帶來的一些問題。這里所作出的關(guān)于數(shù)據(jù)表示的選擇并不是絕對的。在其他場合下,可能會選擇不同的方式來表示類似的數(shù)據(jù),這取決于應(yīng)用的需要以及打算將數(shù)據(jù)派何用途。 1. 美國歷史同盟所需的表 美國歷史同盟的表設(shè)計相當簡單: 總統(tǒng)( p r e s i d e n t )表。此表含有描述每位總統(tǒng)的記錄。同盟站點上的聯(lián)機測驗要使用這個表。 會員( m e m b e r )表。此表用來維護同盟每個會員的當前信息。這些信息將用來建立會員地址名錄的書面和聯(lián)機版本、發(fā)送會員資格更新提示等等。 (1) president表 president 表很簡單,因此我們先討論它。這個表將包含每位美國總統(tǒ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ā)送他們的地址名錄條目的當前內(nèi)容,并要求他們在有必要時更新信息。 郵政地址。這是與沒有電子郵件(或沒有返回信息)的會員聯(lián)絡(luò)所需要的。將分別使用街道地址、城市、州和Zip 號。街道地址列又可以用于有諸如P.O. Box 123 而不是123 Elm St. 的會員的信箱號。我們假定所有同盟會員全都住在美國。當然,對于具有國際會員的機構(gòu),此假設(shè)過于簡化了。如果希望處理多個國家的地址,還需要對不同國家的地址格式作一些工作。例如,這里的Zip 號就不是一個國際標準,有的國家有省而不是州。 電話號碼。與地址字段一樣,這個列對于聯(lián)絡(luò)會員也是很有用的。 特殊愛好的關(guān)鍵詞。假定每個會員一般都對美國歷史都有興趣,但可能有的會員對某些領(lǐng)域有特殊的興趣。此列記錄了這些特殊的興趣。會員可以利用這個信息來找到其他具有類似興趣的會員。 (3) 創(chuàng)建表 現(xiàn)在我們已經(jīng)作好了創(chuàng)建歷史同盟表的準備。我們用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 為當前數(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。這種列類型表示該列存儲的是日期值,這一點也不令人吃驚。而令人吃驚的是,日期的表示以年份開頭。其標準格式為“ Y Y Y Y- M M - D D”(例 如,“1 9 9 9 - 0 7 - 1 8”)。這是日期表示的ANSI SQL 標準。我們用于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。原因是為了使短字符串列的 存儲空間利用更為有效,這里不多討論。如果希望詳細了解,可參閱第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é)分數(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é)分事件,這就是一個永遠不會出現(xiàn)的問題,可是事實并非如此簡單。有時,一天中可能會有不止一個學(xué)分事件。我常聽有的人說他們的數(shù)據(jù),“那種古怪情況從不會出現(xiàn)?!比欢绻@種情況確實出現(xiàn)時,就必須重新設(shè)計表以適應(yīng)這種情況引起的問題。最好是預(yù)先考慮以后可能出現(xiàn)的問題,并預(yù)先準備好怎樣處理他們。因此,我們假定有時可能會需要同一天記錄兩組學(xué)分。我們怎樣處理呢,如果出現(xiàn)這種情況,問題并不難解決。只要對處理數(shù)據(jù)的方式作一點小的更改,就可使同一日期上有多個事件而不會引起問題: 1) 增加一個列到event 表,并用它來給表中每個記錄分配一個唯一的編號。實際上這就給了每個事件一個唯一的ID 號,因此我們稱該列為event_id 列。(如果覺得這好像是做傻事,可看一下圖1-2 中的學(xué)分簿,其中已經(jīng)有這個特征了。事件ID 正好與學(xué)分簿分數(shù)矩陣中列號相似。這個編號可能沒有清晰地寫在那兒并標上“事件I D,”但是它確實在那兒。) 2) 當向score 表中輸入學(xué)分時,輸入的是事件ID 而不是日期。這些改變的結(jié)果如圖1-6 所示?,F(xiàn)在連接score 和event 表時,用的是事件ID 而不是日期,而且不僅用event 表來決定每個學(xué)分的類型,而且還用它來決定其日期。并且在event 表中不再有日期必須唯一這個限制,而唯一的是事件I D。這表示同一天可以有一打測試和測驗,而且能夠在記錄里邊直接保存它們。(毫無疑問,學(xué)生們聽到這個一定渾身發(fā)抖。)不幸的是,從人的觀點來看,圖1-6 中的表設(shè)計較前一個更不能令人滿意。score 表也更為抽象一些,因為它包含的從直觀上可以理解的列更少。而圖1-4 中此表的設(shè)計直觀且容易理解,因為那個score 表具有日期和學(xué)分類型的列。當前的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)可能有點麻煩,但這也是可以辦得到的。另一方面,如果希望只查看某部分數(shù)據(jù)(如只查看學(xué)分或測試),進行諸如男孩與女孩的比較,或以一種靈活的方式顯示合計信息等,情況又大有不同了。電子表格的功能顯得要差一些,而關(guān)系數(shù)據(jù)庫系統(tǒ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)缦滤? 相當嚇人,是嗎,這個查詢通過將score 表的記錄與event 表的記錄連接(關(guān)聯(lián))來檢索學(xué)生名、日期、學(xué)分和學(xué)分的類型。其結(jié)果如下所示: 您肯定注意到了,它與圖1-4 中給出的表設(shè)計相同,而且不需要知道事件ID 就可得出這個結(jié)果,只需指出感興趣的日期并讓MySQL 查找出哪個學(xué)分記錄具有該日期即可。如果您一直擔心抽象和分解會使我們損失一些東西的話,看到這個世界,就不會有這種擔心了。 當然,在考慮過查詢后,您還可能對其他別的東西產(chǎ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)缦? 如果您不能立即清楚地讀懂這個查詢的意思的話,也不必擔心。在進一步深入這個教程之后,就能看懂這個查詢了。將會從圖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 不允許負數(shù)。 NOT NULL 表示此列的值必須填入。(任何學(xué)生都必須有一個ID 號。) A U TO_INCREMENT 是MySQL 中的一個特殊的屬性。其作用為:如果在創(chuàng)建一個新的student 表記錄時遺漏了student_id 的值(或為N U L L),MySQL 自動地生成一個大于當前此列中最大值的唯一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 列存儲標準的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)定義這兩列的組合為主鍵,以免不當心建立了重復(fù)的記錄。重要的是不要對同一天某個學(xué)生的缺曠進行重復(fù)計數(shù)。 1.4.7 增加新記錄 至此,我們的數(shù)據(jù)庫及其表都已經(jīng)創(chuàng)建了,在下一節(jié)“檢索信息”中,我們將看到怎樣從數(shù)據(jù)庫中取出數(shù)據(jù)。現(xiàn)在我們先將一些數(shù)據(jù)放入表中。在數(shù)據(jù)庫中加入數(shù)據(jù)有幾種方法???/p> 通過發(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ù)值??梢杂肔OAD DATA 語句或用mysqlimport 實用程序來裝入記錄。LOAD DATA 語句起批量裝載程序的作用,它從一個文件中讀取數(shù)據(jù)??稍趍ysql 內(nèi)使用它,如下所示: 該語句讀取位于客戶機上當前目錄中數(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é)束。還假定各個值是按列在表中的存放次序給出的。也有可能需要讀取其他格式的文件,或者指定不同的列次 序。更詳細的內(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)仔細地選擇文件名,否則mysqlimport 將不能使用正確的表名。對于如像member1.txt 與member2.txt 這樣的文件名, mysqlimport 將會認為相應(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 查詢時,語句的斷行不必嚴格依照本書。 為了編寫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ù)遠不止這里 給出的這些。請參閱附錄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 來連接兩個條件,如下所示: 這有時是可以覺察到的,不僅僅是在編寫自己的查詢時可以覺察到,而且在為他人編寫查詢時也可以知道。最好是在他人描述想要檢索什么時仔細聽,但不一定使用相同的邏輯運算符將他人的描述轉(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 查詢,檢索出的行與這些行被插入的順序是相同的。但不要認為這種情況是有規(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)名形成一個單一字符串,而且將出生城市和州連接在一起,中間隔一個逗號,形成出生地。 在利用表達式來計算列值時,此表達式被用作列標題。如果表達式很長(如前面的一些查詢樣例中那樣),那么可能會出現(xiàn)一個很寬的列。為了處理這種情況,此列可利用AS name結(jié)構(gòu)來重新命名標題。這樣的名稱為列別名。用這種方法可使上面的輸出更有意義,如下所示: 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 提供了幾種對日期進行處理的方法??梢詫θ掌谶M行的一些運算如下: 按日期排序。(這點我們已經(jīng)看到幾次了。) 查找特定的日期或日期范圍。 提取日期值的組成部分,如年、月或日。 計算日期的差。 日期增加或減去一個間隔得出另一日期。 下面給出一些日期運算的例子。 為了查找特定的日期,可使用精確的日期值或與其他日期值進行比較,將一個D ATE 列與有關(guān)的日期值進行比較: 為了測試或檢索日期的成分,可使用諸如YEAR( )、MONTH( ) 或D AYOFMONTH( ) 這樣的函數(shù)。例如,可通過查找月份值為3 的日期,找出與筆者出生在相同月份(三月)的總統(tǒng)。 為了更詳細,詳細到天,可組合測試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)該更新自己的會員資格了。計算他們的截止日期和當前日期之差,如果小于某個閾值,則不久就需要更新了。下面的查詢是查找需要在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ù)行并對其進行匯總。當學(xué)會了利用MySQL 來生成匯總時,它就變成了用戶強有力的好幫手了,因為手工進行匯總是一項冗長的、費時的、易出錯的工作。匯總的一種簡單的形式是確定在一組值中哪些值是唯一值。利用DISTINCT 關(guān)鍵字來刪除結(jié)果中的重復(fù)行。例如,總統(tǒng)出生的各個州可按如下找出: 其他的匯總形式涉及計數(shù),可利用COUNT( ) 函數(shù)。如果使用COUNT (*),它將給出查 詢所選擇的行數(shù)。如果一個查詢無WHERE 子句,COUNT(*) 將給出表中的行數(shù)。下列查詢給出共有多少人當過美國總統(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é)果中的位置。前面的查詢可編寫如下: 我不認為按位置引用列易讀。如果增加、刪除或重新排序輸出列,必須注意檢查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ù)器實際送到客戶機的結(jié)果。除了COUNT( ) 外還有許多匯總函數(shù)。MIN( )、MAX( )、SUM( ) 和AVG( ) 函數(shù)在確定列的最大、最小、總數(shù)和平均值時都非常有用,甚至可以同時使用它們。下面的查詢得出給定的測試和測驗的各種數(shù)字特性。它還給出有多少學(xué)分參與了每個值的計算(有的學(xué)生可能缺曠或未計入)。 當然,如果您知道這些信息是來自測驗的還是測試的,則它們就會更有意義。但是,為了產(chǎn)生那樣的信息,還需要參考event 表;我們將在下一節(jié)“從多個表中檢索信息”討論這個查詢。匯總信息是很有意思的,因為它們是那么有用,但不太好控制,容易走樣。請看下列查詢: 此查詢選擇已經(jīng)去世的總統(tǒng),按出生地對他們進行分組,并計算出他們逝世時的年齡,計算出平均年齡(每個州的),然后按平均年齡進行排序。換句話說,此查詢按所出生地確定已故總統(tǒng)的平均壽命。但這說明了什么呢,它僅僅說明您可寫該查詢,當然并不說明此查詢是否值得寫。并不是用一個數(shù)據(jù)庫可以做的所有事情都同樣有意義;但是,人們有時在發(fā)現(xiàn)可以利用自己的數(shù)據(jù)庫進行查詢時感到很開心。這可能說明關(guān)于轉(zhuǎn)播運動會的不斷增加的深奧的(空洞的)統(tǒng)計數(shù)據(jù)在過去幾年里正在不斷增多的原因。運動統(tǒng)計者可以使用他們的數(shù)據(jù)庫來計算出某個隊的歷史紀錄,而這些數(shù)字你可能感興趣,也可能毫無興致。 9. 從多個表中檢索信息 到目前為止,我們所編寫的查詢都是從單個表中得到數(shù)據(jù)的?,F(xiàn)在,我們將進行一件更為有趣的工作。以前筆者曾經(jīng)提到過,關(guān)系DBMS 的強大功能在于它能夠?qū)⒁粯訓(xùn)|西與另一樣?xùn)|西相關(guān)聯(lián),因為這樣使得能夠結(jié)合多個表中的信息來解答單個表不能解答的問題。本節(jié)介紹怎樣編寫這種查詢。在從多個表中選擇信息時,需要執(zhí)行一種稱為連接( j o i n)的操作。這是因為需要將一個表中的信息與其他表中的信息相連接來得出查詢結(jié)果。即通過協(xié)調(diào)各表中的值來完成這項工作。 我們來研究一個例子。在前面的“學(xué)分保存方案”小節(jié)中,給出了一個檢索特定日期的測驗或測試學(xué)分的查詢,但沒有解釋?,F(xiàn)在可以進行解釋了。這個查詢實際涉及到三種連接方法,因此我們分兩步進行研究。第一步,我們構(gòu)造一個對特定日期的學(xué)分進行選擇的查詢,如下所示: 此查詢找出具有給定日期的記錄,然后利用該記錄中的事件ID 查找具有相同事件ID 的學(xué)分。對于每個匹配的事件記錄和學(xué)分記錄組合,顯示學(xué)生I D、學(xué)分、日期和事件類型。此查詢在兩個重要方面不同于我們曾經(jīng)編寫過的其他查詢。它們是: FROM 子句給出了不止一個表名,因為我們要檢索的數(shù)據(jù)來自不止一個表: FROM event,score WHERE 子句說明event 和score 表是由每個表中的event_id 值的匹配連接起來的: where event.event_id=score.event_id 請注意,我們是怎樣利用tbl_name.col_name 語法引用列,以便MySQL 知道引用的是哪些表的列。(event_id 出現(xiàn)在兩個表中,如果不用表名來限定它的話將會出現(xiàn)混淆。)此查詢中的其他列( d a t e、s c o r e、t y p e)可單獨使用而不用表名限定符,因為它們在表中只出現(xiàn)一次,從而不會出現(xiàn)含混。但是,一般在連接中我們對每個列都進行限定以便清晰地表示出每個列是屬于哪個表。在完全限定的形式下,查詢?nèi)缦? 從現(xiàn)在起,我們將使用完全限定的形式。第二步,我們利用student 表完成查詢以便顯示學(xué)生名。(第一步中查詢的輸出給出了student_id 字段,但是名字更有意義。)名字顯示是利用score 表和student 表兩者都具有student_id 列,使它們中的記錄可被連接這個事實來完成的。最終的查詢?nèi)缦? 此查詢與前一個查詢的差別在于: student 表被增加到了FROM 子句中,因 MySQL數(shù)據(jù)庫技術(shù)(05) 【大 中 小】【打印】【加入收藏】【關(guān)閉】 【收藏到新浪ViVi】【收藏到365KEY】 瀏覽 字號: line-height: 2em;">
日期:2005-09-23 人氣: 17 出處: 1.4.9 刪除或更新現(xiàn)有記錄 有時,希望除去某些記錄或更改它們的內(nèi)容。DELETE 和U P D ATE 語句令我們能做到這一點。 DELETE 語句有如下格式: DELETE FROM t b l _ n a m e WHERE 要刪除的記錄 WHERE 子句指定哪些記錄應(yīng)該刪除。它是可選的,但是如果不選的話,將會刪除所有的記錄。這意味著最簡單的DELETE 語句也是最危險的。 DELETE FROM tb1_name 這個查詢將清除表中的所有內(nèi)容。一定要當心~為了刪除特定的記錄,可用WHERE 子句來選擇所要刪除的記錄。這類似于SELECT 語 句中的WHERE 子句。例如,為了刪除president 表中所有出生在Ohio 的總統(tǒng)記錄,可用下列查詢: DELETE 語句中的WHERE 子句的一個限制是只能夠引用要刪除記錄的表中的列。在發(fā)布DELETE 語句以前,最好用SELECT 語句測試一下相應(yīng)的WHERE 子句以確保實際刪除的記錄就是確實想要刪除的記錄(而且只刪除這些記錄)。假如想要刪除Te d d yRoosevelt 的記錄。下面的查詢能完成這項工作嗎, 是的,感覺上它能刪除您頭腦中打算刪除的記錄。但是,錯了,實際上它也能刪除Franklin Roosevelt 的記錄。如果首先用WHERE 子句檢查一下就安全了,如下所示: 現(xiàn)在我們明白了能選擇出所需記錄的WHERE 子句了,因此DELETE 查詢可正確地構(gòu)造如下: 似乎刪除一個記錄需要做許多工作,不是嗎,但是安全第一~(如果想使鍵盤輸入工作盡量少,可利用拷貝和粘貼技術(shù)或采用輸入行編輯技術(shù)。更詳細的信息,請參閱“與m y s q l交互的技巧”一節(jié)。)為了修改現(xiàn)有記錄,可利用U P D ATE 語句,它具有下列格式: U P D ATE t b l_n a m e SET 要更改的列WHERE 要更新的記錄這里的WHERE 子句正如DELETE 語句一樣,是可選的,因此如果不指定的話,表中的每個記錄都被更新。下面 的查詢將每個學(xué)生的名字都更改為“ G e o rg e”: 顯然,對于這樣的查詢必須極為小心。一般對正在更新的記錄要更為小心。假定近來增加了一個新記錄到歷史同盟,但是只填寫了此實體的少數(shù)幾個列: 然后意識到忘了設(shè)置其會員終止日期。那么可如下進行設(shè)置: 可同時更新多個列。下面的語句將更新Jerome 的電子郵件和通信地址: 還可以通過設(shè)置某列的值為N U L L(假設(shè)此列允許NULL 值)“不設(shè)置”此列。如果在未來的某個時候Jerome 決定支付成為終生會員的會員資格更新費,那么可以設(shè)置其記錄的終止日期為N U L L(“永久”)以標記他為終生會員。具體設(shè)置如下: 正如DELETE 語句一樣,對于U P D AT E,用SELECT 語句測試WHERE 子句以確保選擇正確的更新記錄是一個好辦法。如果選擇條件范圍太窄或太寬,就會使更新的記錄太少或太多。如果您試驗過本節(jié)中的查詢,那么必定已經(jīng)刪除和修改了samp_db 表中的記錄。在繼續(xù)學(xué)習(xí)下一節(jié)的內(nèi)容以前,應(yīng)該撤消這些更改。按1 . 4 . 7節(jié)“增加新記錄”最后的說明重新裝載表的內(nèi)容來完成這項工作。 1.4.10 改變表的結(jié)構(gòu) 回顧我們創(chuàng)建歷史同盟member 表時缺了一個會員號列,因此我們可以進行一次A LT E RTABLE 語句的練習(xí)。需要用A LTER TA B L E,可以對表重新命名,增加或刪除列,更改列的類型等等。這里給出的例子是關(guān)于怎樣增加新列的。有關(guān)A LTER TABLE 功能的詳 細內(nèi)容,請參閱第3章。增加會員號列到member 表的主要考慮是,其值應(yīng)該是唯一的, 以免各會員條目混淆。A U TO_INCREMENT 列在此是很有用的,因為我們可以在增加新 的號碼時令MySQL 自動地生成唯一的號碼。在C R E ATE TABLE 語句中,這樣一個列 的說明如下: 對于A LTER TA B L E,相應(yīng)的句法也是類似的。可執(zhí)行下列查詢增加該列: 我們已經(jīng)有一個存放會員號的列,現(xiàn)在怎樣分配會員號給member 表中的現(xiàn)有記錄呢, 很容易~MySQL 已經(jīng)做了這項工作。在增加一列到某個表時, MySQL 將會用缺省值初 始化該列值。對于A U TO_INCREMENT 列,每個行將會產(chǎn)生一個新的順序號。 1.5 與mysql 交互的技巧 本節(jié)介紹怎樣更有效地且鍵入工作量較小地與mysql 客戶機程序進行交互。介紹怎樣更簡單地與服務(wù)器連接,以及怎樣不用每次都從頭開始鍵入查詢。 1.5.1 簡化連接過程 在激活mysql 時,有可能需要指定諸如主機名、用戶名或口令這樣的連接參數(shù)。運行一個程序需要做很多輸入工作,這很快就會讓人厭煩。有幾種方法可最小化所做的鍵入工作,使連接更為容易,它們分別為: 利用選項文件存儲連接參數(shù)。 利用外殼程序的命令歷史重復(fù)命令。 利用外殼程序的別名或腳本定義mysql 命令行快捷鍵。 1. 利用選項文件 自版本3.22 以來,MySQL 允許在一個選項文件中存儲連接參數(shù)。然后在運行mysql 時就不用重復(fù)鍵入這些參數(shù)了;僅當您曾經(jīng)在命令行上鍵入過它們時可以使用。這些參數(shù)也可以為其他MySQL 客戶機所用,如為mysqlimport 所用。這也表示在使用這些程序時,選項文件減少了鍵入工作。為了利用選項文件方法指定連接參數(shù),可建立一個名為~ / . m y.cnf (即主目錄中的一個名為. m y.cnf 的文件)。選項文件是一個無格式的文本文件,因此可用任何文本編輯器來創(chuàng)建它。文件的內(nèi)容所下所示: [client] 行標記客戶機選項組的開始;它后跟的所有行都是為MySQL 客戶機程序獲得選項值準備的,這些行一直沿續(xù)到文件的結(jié)尾或另一不同的參數(shù)組的開始。在連接到服務(wù)器時,用指定的主機名、用戶名和口令替換s e r v e r h o s t、yourname 和y o u r p a s s。對于筆者來說,. m y.cnf 如下所示: 只有[client] 行是必須的。定義參數(shù)值的行都是可選的;可以僅指定那些所需要的參數(shù)。例如,如果您的MySQL 用戶名與UNIX 的登錄名相同,則不需要包括user 行。在創(chuàng)建了. m y.cnf 文件后,設(shè)置其訪問方式為某個限定值以保證別人不能讀取它: 在Windows 下,選項文件的內(nèi)容是相同的,但其名稱不同( c : m y. c n f),而且不調(diào)用chmod 命令。因為選項文件在版本3.22 前未加到M y S Q L,所以更早的版本不能使用它們。特別是在Windows 下,您不能與共享MySQL 分發(fā)包一起得到的客戶機使用選項文件,因為它是基于MySQL 3.21 的。選項文件在注冊過的MySQL 的Windows 版本下工作得很好,否則可以從MySQL Web 站點取得更新的支持選項文件的客戶機。關(guān)于選項文件的詳細內(nèi)容可參閱附錄E“MySQL 程序參考”。 2. 利用外殼程序的命令歷史諸如c s h、tcsh 和bash 這樣的外殼程序會在一個歷史列表中記下您的命令,并允許重復(fù)該列表中的命令。如果采用的是這樣的外殼程序,其歷史列表可幫助免除完整命令的鍵入。例如,如果最近調(diào)用了m y s q l,可按如下命令再次執(zhí)行它:% !my 其中“~”告訴外殼程序搜索整個命令歷史找到最近以“ m y”開頭的命令,并像您打入的一樣發(fā)布它。有的外殼程序還允許利用上箭頭和下箭頭鍵(或許是Ctrl-P 和C t r l - N)在歷史列表中上下移動??捎眠@種方法選擇想要的命令,然后按Enter 執(zhí)行它。tcsh 和bash 有這種功能,而其他外殼程序也可能有。可參閱相應(yīng)的外殼程序以找到更多使用歷史列表的內(nèi)容。 3. 利用外殼程序的別名或腳本如果使用的外殼程序提供別名功能,那么可以設(shè)置允許通過鍵入簡短名調(diào)用長命令的命令快捷鍵。例如,在csh 或tcsh 中,可利用alias 命令設(shè)置名為samp_db 的別名,如下所示: 而bash 中的語法稍有不同: 可以定義一個別名使這兩個命令等價: 顯然,第一個比第二個更好鍵入。為了使這些別名在每次登錄時都起作用,可將在外殼程序設(shè)置文件中放入一個alias 命令(如,csh 放入. c s h r c,而bash 放入. b a s h _ p r o f i l e)??旖萱I的其他形式是建立利用適當?shù)倪x項執(zhí)行mysql 的外殼程序腳本。在UNIX 中,等價于samp_db 別名的腳本文件如下所示: 如果筆者命名此腳本為samp_db 并使其可執(zhí)行(用chmod +x samp_db),那么可以鍵入samp_db 運行mysql 并連接到筆者的數(shù)據(jù)庫中。在Windows 下,可用批命令文件來完成相同的工作。命名文件s a m p _ d b . b a t,并在其中放入如下的行: 此批命令文件可通過在DOS 控制臺提示符下鍵入samp_db 來執(zhí)行,也可以雙擊它的Windows 圖標來執(zhí)行。如果訪問多個數(shù)據(jù)庫或連接到多個主機,則可以定義幾個別名或腳本,每一個都用不同的選 項調(diào)用m y s q l。 1.5.2 以較少的鍵入發(fā)布查詢 mysql 是一個與數(shù)據(jù)庫進行交互的極為有用的程序,但是其界面最適合于簡短的、單行的查詢。當然, mysql 自身并不關(guān)心某個查詢是否分成多行,但是長的查詢很不好鍵入。輸入一條查詢也不是很有趣的事,即使是一條較短的查詢也是如此,除非發(fā)現(xiàn)有錯誤才愿意重新鍵入它。 有幾種可用來避免不必要的鍵入或重新鍵入的技巧: 利用mysql 的輸入行編輯功能。 利用拷貝和粘貼。 以批方式運行m y s q l。 利用現(xiàn)有數(shù)據(jù)來創(chuàng)建新記錄以避免鍵入I N S E RT 語句。 1. 利用mysql 的輸入行編輯器 mysql 具有內(nèi)建的GNU Readline 庫,允許對輸入行進行編輯??梢詫Ξ斍颁浫氲男羞M行處理,或調(diào)出以前輸入的行并重新執(zhí)行它們(原樣執(zhí)行或做進一步的修改后執(zhí)行)。在錄入一行并發(fā)現(xiàn)錯誤時,這是非常方便的;您可以在按Enter 鍵前,在行內(nèi)退格并進行修正。如果錄入了一個有錯的查詢,那么可以調(diào)用該查詢并對其進行編輯以解決問題,然后再重新提交它。(如果您在一行上鍵入了整個查詢,這是最容易的方法。)表1-4 中列出了一些非常有用的編輯序列,除了此表中給出的以外,還有許多輸入編輯命令。利用因特網(wǎng)搜索引擎,應(yīng)該能夠找到R e a d l i n e手冊的聯(lián)機版本。此手冊也包含在Readline 分發(fā)包中,可在h t t p : / / w w w.gnu. org/ 的GN U Web 站點得到。 下面的例子描述了輸入編輯的一個簡單的使用。假定用mysql 輸入了下列查詢: 如果在按Enter 前,已經(jīng)注意到將“ p r e s i d e n t”錯拼成了“ p e r s i d e n t”,則可按左箭頭或Ctrl-B 多次移動光標到“s”的左邊。然后按Delete 兩次刪除“ e r”,鍵入“r e”改正錯誤,并按Enter 發(fā)布此查詢。 如果沒注意到錯拼就按了E n t e r,也不會有問題。在mysql 顯示了錯誤消息后,按上箭頭或Ctrl-P 調(diào)出該行,然后對其進行編輯。 輸入行編輯在mysql 的Windows 版中不起作用,但是可從MySQL Web 站點取得免費的cygwin_32 客戶機分發(fā)包。在該分發(fā)包中的mysqlc 程序與mysql 一樣,但它支持輸入行編輯命令。 2. 利用拷貝和粘貼發(fā)布查詢 如果是在窗口環(huán)境下工作,可將認為有用的查詢文本保存在一個文件中并利用拷貝和粘貼操作很容易地發(fā)布這些命令。其工作過程如下: 1) 在Te l n e t窗口或DOS 控制窗口中激活m y s q l。 2) 在一個文檔窗口打開包含查詢的文件。(如筆者在Mac OS 下使用B B E d i t,在U N I X中使用X Window System 下的xterm 窗口中的v i。) 3) 為了執(zhí)行存放在文件中的某個查詢,選擇并拷貝它。然后切換到Telnet 窗口或DOS 控制臺,并將該查詢粘貼到m y s q l。這個過程寫起來似乎有點令人討厭,但它是一個快速錄入查詢的很容易的方法,實際使用時不用鍵入查詢。這個方法也允許在文檔窗口中對查詢進行編輯,而且它允許拷貝和粘貼現(xiàn)有查詢來構(gòu)造一個新的查詢。例如,如果您經(jīng)常從某個特定的表中選擇行,但是喜歡查看以不同方式存放的輸出結(jié)果,則可以在文檔窗口中保存一個不同的ORDER BY 子句的列表,然后為任意的特定查詢拷貝和粘貼想使用的那個子句。也可按其他方向拷貝和粘貼(從Telnet 到查詢文件)。在mysql 中錄入行時,它們被保存在您的主目錄中的名為.mysql_history 的文件中。如果您手工錄入了一個希望保存起來今后使用的查詢,可退出m y s q l,在某個編輯器中打開. m y s q l _ h i s t o r y,然后從.mysql_history 拷貝和粘貼此查詢到您的查詢文件。 3. 以批方式運行mysql不一定必須交互式地運行m y s q l。mysql 能夠以非交互式(批)方式從某個文件中讀取輸入。這對于定期運行的查詢是很有用的,因為您一定不希望每次運行此查詢時都要重新鍵入它。只要一次性地將其放入一個文件,然后讓mysql 在需要時執(zhí)行該文件的內(nèi)容即可。假定有一個查詢查找member 表的interests 列,以找出那些對美國歷史的某個方面感興趣的歷史同盟會員。如查找對大蕭條期有興趣的會員,可編寫此查詢?nèi)缦?注意結(jié)尾處有一個分號,從而mysql 能夠知道查詢語句在何處結(jié)束): 為了使用此查詢來找出對Thomas Jefferson 感興趣的會員,可以編輯此查詢文件將depression 更改為Je fferson 并再次運行m y s q l。只要不很經(jīng)常使用此查詢,它工作得很好。如果經(jīng)常使用,則需要更好的方法。使用此查詢更為靈活的一種方法是將其放入一個外殼程序腳本中,此腳本從腳本命令行取一個參數(shù)并利用它來更改查詢的文本。這樣確定查詢的參數(shù),使得能夠在運行腳本時指定令人感興趣 的關(guān)鍵字。為了了解這如何起作用,按如下編寫一個較小的外殼程序腳本i n t e r e s t s . s h: 其中第二行保證在命令行上有一個關(guān)鍵字;它顯示一條簡短的消息,或者退出。在< < Q U E RY_INPUT 和最后的Q U E RY_INPUT 之間的所有內(nèi)容成為mysql 的輸入。在查詢文本中,外殼程序用來自命令行的關(guān)鍵字替換$ 1。(在外殼程序腳本中, $ 1、$ 2 . . .為命令參數(shù)。)這使相應(yīng)的查詢反映了執(zhí)行此腳本時在命令行上指定的關(guān)鍵字。在能夠運行此腳本前,必須使其可執(zhí)行: 現(xiàn)在不需要在每次運行腳本時對其進行編輯了。只要在命令行上告訴它需要查找什么就行了。如下所示: 4. 利用現(xiàn)有數(shù)據(jù)來創(chuàng)建新記錄 可以用I N S E RT 語句每次一行地將新記錄追加到表中,但是在通過手工鍵入I N S E RT 語句建立幾個新記錄后,多數(shù)人都會意識到應(yīng)該有更好的追加記錄的方法。一種選擇是利用僅含有數(shù)據(jù)值的文件,然后利用LOAD DATA 語句或mysqlimport 實用程序從該文件中裝入記錄。通常,可利用已經(jīng)以某種格式存在的數(shù)據(jù)來建立數(shù)據(jù)文件。這些數(shù)據(jù)信息可能包含在電子表中,或許在某個其他數(shù)據(jù)庫中,應(yīng)該將它們轉(zhuǎn)換到MySQL。為了介紹起來簡單,我們假定這些數(shù)據(jù)是在桌面微計算機的電子表中。要將電子表數(shù)據(jù)從桌面微計算機中轉(zhuǎn)換到您的UNIX 賬號下的某個文件中,可結(jié)合Telnet 利用拷貝和粘貼。具體工作如下所示:1) 打開UNIX 賬號的一個Telnet 連接。在Mac OS 下,可利用諸如Better Telnet 或N CSA Telnet 這樣的應(yīng)用程序。在Windows 下,可使用標準的Telnet 程序。 2) 打開電子表,選擇想轉(zhuǎn)換的數(shù)據(jù)塊,拷貝它。 3) 在Telnet 窗中,鍵入下列命令開始獲取數(shù)據(jù)到文件data.txt。 cat 命令等待輸入。 4) 將從電子表拷貝來的數(shù)據(jù)粘貼到Telnet 窗口。cat 認為您正在鍵入信息并忠實地將它寫入到data.txt 文件。 5) 在所有粘貼數(shù)據(jù)已經(jīng)寫入該文件后,如果光標停止在數(shù)據(jù)行的結(jié)尾處而不是停止在新行的開始,按Enter。然后,按Ctrl-D 以指示“文件結(jié)束”。cat 停止輸入等待并關(guān)閉data.txt文件?,F(xiàn)在已經(jīng)得到了包含有電子表中選擇的數(shù)據(jù)塊的data.txt 文件,此文件已作好由LOAD DATA mysqlimport 加載到數(shù)據(jù)庫的準備。 拷貝和粘貼是一種將數(shù)據(jù)傳入UNIX 文件的快速且簡易的方法,但它最適合較小的數(shù)據(jù)集。量較大的數(shù)據(jù)可能會超出系統(tǒng)拷貝緩沖區(qū)。在這樣的情況下,最好是以無格式文本(制表符分隔)的形式保存電子表。然后可利用FTP 將相應(yīng)文件從微機上傳送到UNIX 賬號。轉(zhuǎn)換文本模式(非二進制或影像模式)的文件以便行結(jié)束符轉(zhuǎn)換為UNIX 的行結(jié)束符。(U N I X利用換行符、Mac OS 利用回車換行符、Windows 利用回車換行符/換行符對作為行結(jié)束符。)可告訴LOAD DATA 或mysqlimport 尋找什么換行符,但是在UNIX 下,對含換行符的文件處理要更容易一些。 在轉(zhuǎn)換了文件之后,應(yīng)該檢查一下在結(jié)尾處是否具空白行。如果有,應(yīng)該將它們刪除,否則在將該文件裝載到數(shù)據(jù)庫時,這些空白行將會轉(zhuǎn)換為空白或畸形的記錄。來自電子表格以無格式文本保存的文件,或具有能括住包含空格的值的括號。為了在將該文件裝入數(shù)據(jù)庫時去掉這些括號,可利用LOAD DATA 的FIELDS ENCLOSED BY 子句,或利用mysqlimport 的--fields - enclosed - by 選項。更詳細的信息請參看附錄D 中LOAD DATA 的相應(yīng)項。 1.6 向何處去 現(xiàn)在我們已經(jīng)介紹了許多使用MySQL 的知識。您已經(jīng)知道了怎樣設(shè)置數(shù)據(jù)庫并創(chuàng)建表。能夠?qū)⒂涗浄湃脒@些表中,并以各種方式對其進行檢索,更改或刪除。但是要掌握M y S Q L仍然有許多知識要學(xué),本章中的教程僅僅給出了一些淺顯的東西。通過考察我們的樣例數(shù)據(jù)庫就會明白這一點。我們創(chuàng)建了樣例數(shù)據(jù)庫及其表,并用一些初始的數(shù)據(jù)對其進行了填充。在這個工作過程中,我們明白了怎樣編寫查詢,回答關(guān)于數(shù)據(jù)庫中信息的某些問題,但是還有許多工作要做。 例如,我們沒有方便的交互方式來輸入學(xué)分保存方案的新學(xué)分記錄,或輸入歷史同盟地址名錄的會員條目。還沒有方便的方法來編輯現(xiàn)有記錄,而且我們?nèi)匀徊荒苌捎∷⒒蚵?lián)機形式的同盟地址名錄。這些任務(wù)以及一些其他的任務(wù)將在以后的各章中陸續(xù)地進行介紹,特別是在第7章“Perl DBI API”和第8 章“PHP API”中將要進行詳細地介紹。 下一步將閱讀本書中哪部分取決于您對什么內(nèi)容感興趣。如果希望了解怎樣完成已經(jīng)以 歷史同盟和學(xué)分保存方案開始的工作,可看第一部分有關(guān)MySQL 程序設(shè)計的內(nèi)容。如果打算成為某個站點的MySQL 管理員,本書的第三部分將對管理工作做較多的介紹。但是,筆者建議通過閱讀第一部分中的其余各章,首先獲得使用MySQL 的一般背景知識。 這些章節(jié)討論了MySQL 怎樣處理數(shù)據(jù),進一步提供有關(guān)語法和查詢語句的用途,并且說明了怎樣使查詢運行得更快。不管您在什么環(huán)境中使用M y S Q L,不管是運行mysql 還是編寫自己的程序, 還是作為數(shù)據(jù)庫管理員,用這些內(nèi)容打下一個良好的基礎(chǔ)將有助于您站在一個較高的起點上。
信息發(fā)布:廣州名易軟件有限公司 http://www.jetlc.com
|