數(shù)據(jù)庫(kù)中的每個(gè)表都是由一個(gè)或多個(gè)列構(gòu)成的。在用C R E ATE TABLE 語(yǔ)句創(chuàng)建一個(gè)表時(shí),要為每列指定一個(gè)類型。列的類型比數(shù)據(jù)類型更為特殊,它僅僅是如“數(shù)”或“串”這樣的通用類型。列的類型精確地描述了給定表列可能包含的值的種類,如SMALLINT 或VA R C H A R ( 3 2 )。
MySQL 的列類型是一種手段,通過(guò)這種手段可以描述一個(gè)表列包含什么類型的值,這又決定了MySQL 怎樣處理這些值。例如,數(shù)值值既可用數(shù)值也可用串的列類型來(lái)存放,但是根據(jù)存放這些值的類型, MySQL 對(duì)它們的處理將會(huì)有些不同。每種列類型都有幾個(gè)特性如下:
■ 其中可以存放什么類型的值。
■ 值要占據(jù)多少空間,以及該值是否是定長(zhǎng)的(所有值占相同數(shù)量的空間)或可變長(zhǎng)的(所占空間量依賴于所存儲(chǔ)的值)。
■ 該類型的值怎樣比較和存儲(chǔ)。
■ 此類型是否允許NULL 值。
■ 此類型是否可以索引。
我們將簡(jiǎn)要地考察一下M y S Q L列類型以獲得一個(gè)總的概念,然后更詳細(xì)地討論描述每種列類型的屬性。
2.2.1 列類型概述
MySQL 為除NULL 值以外的所有通用數(shù)據(jù)類型的值都提供了列類型。在列是否能夠包含NULL 值被視為一種類型屬性的意義上,可認(rèn)為所有類型都包含N U L L屬性。M y S Q L有整數(shù)和浮點(diǎn)數(shù)值的列類型,如表2 - 2所示。整數(shù)列類型可以有符號(hào)也可無(wú)符號(hào)。有一種特殊的屬性允許整數(shù)列值自動(dòng)生成,這對(duì)需要唯一序列或標(biāo)識(shí)號(hào)的應(yīng)用系統(tǒng)來(lái)說(shuō)是非常有用的。

MySQL 串列類型如表2 - 3所示。串可以存放任何內(nèi)容,即使是像圖像或聲音這樣的絕對(duì)二進(jìn)制數(shù)據(jù)也可以存放。串在進(jìn)行比較時(shí)可以設(shè)定是否區(qū)分大小寫。此外,可對(duì)串進(jìn)行模式匹配(實(shí)際上,在MySQL 中可以在任意列類型上進(jìn)行模式匹配,但最經(jīng)常進(jìn)行模式匹配還是在串類型上)

日期與時(shí)間列類型在表2 - 4中示出。對(duì)于臨時(shí)值, MySQL 提供了日期(有或沒(méi)有時(shí)間)、時(shí)間和時(shí)間戳(一種允許跟蹤對(duì)記錄何時(shí)進(jìn)行最后更改的特殊類型)的類型。而且還提供了一種在不需要完整的日期時(shí)有效地表示年份的類型。

要?jiǎng)?chuàng)建一個(gè)表,應(yīng)使用C R E ATE TABLE 語(yǔ)句并指定構(gòu)成表列的列表。每個(gè)列都有一個(gè)名字和類型,以及與每個(gè)類型相關(guān)的各種屬性。下面是創(chuàng)建具有三個(gè)分別名為f、c 和i 的列的表my_table 的例子:

定義一個(gè)列的語(yǔ)法如下:

其中列名由col_name 給出。列名可最多包含64 個(gè)字符,字符包括字母、數(shù)字、下劃線及美元符號(hào)。列名可以名字中合法的任何符號(hào)(包括數(shù)字)開頭。但列名不能完全由數(shù)字組成,因?yàn)槟菢涌赡苁蛊渑c數(shù)據(jù)分不開。M y S Q L保留諸如S E L E C T、DELETE 和C R E ATE 這樣的詞,這些詞不能用做列名。但是函數(shù)名(如POS 和M I N)是可以使用的。
列類型col_type 表示列可存儲(chǔ)的特定值。列類型說(shuō)明符還能表示存放在列中的值的最大長(zhǎng)度。對(duì)于某些類型,可用一個(gè)數(shù)值明確地說(shuō)明其長(zhǎng)度。而另外一些值,其長(zhǎng)度由類型名蘊(yùn)含。例如,CHAR(10) 明確指定了10 個(gè)字符的長(zhǎng)度。而TINYBLOB 值隱含最大長(zhǎng)度為2 5 5個(gè)字符。有的類型說(shuō)明符允許指定最大的顯示寬度(即顯示值時(shí)使用多少個(gè)字符)。浮點(diǎn)類型允許指定小數(shù)位數(shù),所以能控制浮點(diǎn)數(shù)的精度值為多少。
可以在列類型之后指定可選的類型說(shuō)明屬性,以及指定更多的常見屬性。屬性起修飾類型的作用,并更改其處理列值的方式,屬性有以下類型:
■ 專用屬性用于指定列。例如,UNSIGNED 屬性只針對(duì)整型,而B I N A RY 屬性只用于CHAR 和VA R C H A R。
■ 通用屬性除少數(shù)列之外可用于任意列??梢灾付∟ULL 或NOT NULL 以表示某個(gè)列是否能夠存放N U L L。還可以用D E FA U LT def_value 來(lái)表示在創(chuàng)建一個(gè)新行但未明確給出該列的值時(shí),該列可賦予值d e f _ v a l u e。def_value 必須為一個(gè)常量;它不能是表達(dá)式,也不能引用其他列。不能對(duì)BLOB 或TEXT 列指定缺省值。
如果想給出多個(gè)列的專用屬性,可按任意順序指定它們,只要它們跟在列類型之后、通用屬性之前即可。類似地,如果需要給出多個(gè)通用屬性,也可按任意順序給出它們,只要將它們放在列類型和可能給出的列專用屬性之后即可。本節(jié)其余部分討論每個(gè)MySQL 的列類型,給出定義類型和描述它們的屬性的語(yǔ)法,諸如取值范圍和存儲(chǔ)需求等。類型說(shuō)明如在C R E ATE TABLE 語(yǔ)句中那樣給出??蛇x的信息由方括號(hào)([ ])給出。如,語(yǔ)MEDIUMINT[(M)] 表示最大顯示寬度(指定為M)是可選的。另一方面,對(duì)于C H A R ( M ),無(wú)方括號(hào)表示的(M) 是必須的。
2.2.2 數(shù)值列類型
MySQL 的數(shù)值列類型有兩種:
■ 整型。用于無(wú)小數(shù)部分的數(shù),如1、4 3、- 3、0 或- 7 9 8 4 3 2??蓪?duì)正數(shù)表示的數(shù)據(jù)使用整數(shù)列,如磅的近似數(shù)、英寸的近似數(shù),銀河系行星的數(shù)目、家族人數(shù)或一個(gè)盤子里的細(xì)菌數(shù)等。
■ 浮點(diǎn)數(shù)。用于可能具有小數(shù)部分的數(shù),如3 . 1 4 1 5 9、- . 0 0 2 7 3、- 4 . 7 8、或3 9 . 3 E + 4??蓪⒏↑c(diǎn)數(shù)列類型用于有小數(shù)點(diǎn)部分或極大、極小的數(shù)。可能會(huì)表示為浮點(diǎn)數(shù)的值有農(nóng)作物平均產(chǎn)量、距離、錢數(shù)(如物品價(jià)格或工資)、失業(yè)率或股票價(jià)格等等。整型值也可
以賦予浮點(diǎn)列,這時(shí)將它們表示為小數(shù)部分為零的浮點(diǎn)值。每種數(shù)值類型的名稱和取值范圍如表2 - 5所示。各種類型值所需的存儲(chǔ)量如表2-6 所示。
CREATE TABLE 語(yǔ)句
本章中例子中大量使用了C R E ATE TABLE 語(yǔ)句。您應(yīng)該對(duì)此語(yǔ)句相當(dāng)熟悉,因?yàn)槲覀冊(cè)诘?章中的教程部分使用過(guò)它。關(guān)于C R E ATE TABLE 語(yǔ)句也可參閱附錄D。


MySQL 提供了五種整型: T I N Y I N T、S M A L L I N T、M E D I U M I N T、INT 和B I G I N T。I N T 為I N T E G E R的縮寫。這些類型在可表示的取值范圍上是不同的。整數(shù)列可定義為UNSIGNED 從而禁用負(fù)值;這使列的取值范圍為0 以上。各種類型的存儲(chǔ)量需求也是不同的。
取值范圍較大的類型所需的存儲(chǔ)量較大。
MySQL 提供三種浮點(diǎn)類型: F L O AT、DOUBLE 和D E C I M A L。與整型不同,浮點(diǎn)類型不能是UNSIGNED 的,其取值范圍也與整型不同,這種不同不僅在于這些類型有最大值,而且還有最小非零值。最小值提供了相應(yīng)類型精度的一種度量,這對(duì)于記錄科學(xué)數(shù)據(jù)來(lái)說(shuō)是非常重要的(當(dāng)然,也有負(fù)的最大和最小值)。
DOUBLE PRECISION[(M, D)] 和REAL[(M, D)] 為DOUBLE[(M, D)] 的同義詞。而NUMERIC(M, D) 為DECIMAL(M, D) 的同義詞。F L O AT(4) 和F L O AT(8) 是為了與ODBC 兼容而提供的。在MySQL 3.23 以前,它們?yōu)镕 L O AT(10, 2) 和DOUBLE(16, 4) 的同義詞。自MySQL 3.23 以來(lái),F(xiàn) L O AT(4) 和F L O AT(8) 各不相同,下面還要介紹。
在選擇了某種數(shù)值類型時(shí),應(yīng)該考慮所要表示的值的范圍,只需選擇能覆蓋要取值的范圍的最小類型即可。選擇較大類型會(huì)對(duì)空間造成浪費(fèi),使表不必要地增大,處理起來(lái)沒(méi)有選擇較小類型那樣有效。對(duì)于整型值,如果數(shù)據(jù)取值范圍較小,如人員年齡或兄弟姐妹數(shù),則TINYINT 最合適。MEDIUMINT 能夠表示數(shù)百萬(wàn)的值并且可用于更多類型的值,但存儲(chǔ)代價(jià)較大。BIGINT 在全部整型中取值范圍最大,而且需要的存儲(chǔ)空間是表示范圍次大的整型I N T類型的兩倍,因此只在確實(shí)需要時(shí)才用。對(duì)于浮點(diǎn)值, D O U B L E占用F L O AT 的兩倍空間。除非特別需要高精度或范圍極大的值,一般應(yīng)使用只用一半存儲(chǔ)代價(jià)的F L O AT 型來(lái)表示數(shù)據(jù)。
在定義整型列時(shí),可以指定可選的顯示尺寸M。如果這樣,M 應(yīng)該是一個(gè)1 到255 的整數(shù)。它表示用來(lái)顯示列中值的字符數(shù)。例如, MEDIUMINT(4) 指定了一個(gè)具有4 個(gè)字符顯示寬度的MEDIUMINT 列。如果定義了一個(gè)沒(méi)有明確寬度的整數(shù)列,將會(huì)自動(dòng)分配給它一個(gè)缺省的寬度。缺省值為每種類型的“最長(zhǎng)”值的長(zhǎng)度。如果某個(gè)特定值的可打印表示需要不止M 個(gè)字符,則顯示完全的值;不會(huì)將值截?cái)嘁赃m合M 個(gè)字符。對(duì)每種浮點(diǎn)類型,可指定一個(gè)最大的顯示尺寸M 和小數(shù)位數(shù)D。M 的值應(yīng)該取1 到2 5 5。D 的值可為0 到3 0,但是不應(yīng)大于M - 2。(如果熟悉ODBC 術(shù)語(yǔ),就會(huì)知道M 和D 對(duì)應(yīng)于
ODBC 概念的“精度”和“小數(shù)點(diǎn)位數(shù)”)M 和D 對(duì)F L O AT 和DOUBLE 都是可選的,但對(duì)于DECIMAL 是必須的。在選項(xiàng)M 和D時(shí),如果省略了它們,則使用缺省值。下面的語(yǔ)句創(chuàng)建了一個(gè)表,它說(shuō)明了數(shù)值列類型的M 和D 的缺省值(其中不包括D E C I M A L,因?yàn)镸 和D 對(duì)這種類型不是可選的):

如果在創(chuàng)建表之后使用DESCRIBE my_table 語(yǔ)句,則輸出的Field 和Type 列如下所示(注意,如果用MySQL 的3.23 以前的版本運(yùn)行這個(gè)查詢,則有一個(gè)小故障, 即BIGINT 的顯示寬度將是21 而不是2 0。):

每一個(gè)數(shù)字列都具有一個(gè)由列類型所決定的取值范圍。如果打算插入一個(gè)不在列范圍內(nèi)的值,將會(huì)進(jìn)行截?。篗ySQL 將剪裁該值為取值范圍的邊界值并使用這個(gè)結(jié)果。在檢索時(shí)不進(jìn)行值的剪裁。
值的剪裁根據(jù)列類型的范圍而不是顯示寬度進(jìn)行。例如,一個(gè)SMALLINT(3) 列顯示寬度為3 而取值范圍為-32768 到3 2 7 6 7。值12345 比顯示寬度大,但在該列的取值范圍內(nèi),因此它可以插入而不用剪裁并且作為12345 檢索。值99999 超出了取值范圍,因此在插入時(shí)被剪裁為3 2 7 6 7。以后在檢索中將以值3 2 7 6 7檢索該值。
一般賦予浮點(diǎn)列的值被四舍五入到這個(gè)列所指定的十進(jìn)制數(shù)。如果在一個(gè)F L O AT(8, 1)的列中存儲(chǔ)1 . 2 3 4 5 6,則結(jié)果為1 . 2。如果將相同的值存入F L O AT(8, 4) 的列中,則結(jié)果為1 . 2 3 4 6。這表示應(yīng)該定義具有足夠位數(shù)的浮點(diǎn)列以便得到盡可能精確的值。如果想精確到千分之一,那就不要定義使該類型僅有兩位小數(shù)。
浮點(diǎn)值的這種處理在MySQL 3.23 中有例外,F(xiàn) L O AT(4) 和F L O AT(8) 的性能有所變化。這兩種類型現(xiàn)在為單精度( 4 字節(jié))和雙精度( 8 字節(jié))的類型,在其值按給出的形式存放(只受硬件的限制)這一點(diǎn)上說(shuō),這兩種類型是真浮點(diǎn)類型。
DECIMAL 類型不同于F L O AT 和D E C I M A L,其中DECIMAL 實(shí)際是以串存放的。DECIMAL 可能的最大取值范圍與DOUBLE 一樣,但是其有效的取值范圍由M 和D 的值決定。如果改變M 而固定D,則其取值范圍將隨M 的變大而變大。表2 - 7的前三行說(shuō)明了這一點(diǎn)。如果固定M 而改變D,則其取值范圍將隨D 的變大而變?。ǖ仍黾樱?。表2 - 7的后三行說(shuō)明了這一點(diǎn)。

給定的DECIMAL 類型的取值范圍取決于MySQL 的版本。對(duì)于MySQL 3.23 以前的版本,DECIMAL(M, D) 列的每個(gè)值占用M 字節(jié),而符號(hào)(如果需要)和小數(shù)點(diǎn)包括在M 字節(jié)中。因此,類型為DECIMAL(5, 2) 的列,其取值范圍為-9.99 到9 9 . 9 9,因?yàn)樗鼈兏采w了所有可能的5 個(gè)字符的值。
正如MySQL 3.23 一樣,DECIMAL 值是根據(jù)ANSI 規(guī)范進(jìn)行處理的, ANSI 規(guī)范規(guī)定DECIMAL(M, D) 必須能夠表示M 位數(shù)字及D 位小數(shù)的任何值。例如, DECIMAL(5, 2) 必須能夠表示從-999.99 到999.99 的所有值。而且必須存儲(chǔ)符號(hào)和小數(shù)點(diǎn),因此自MySQL 3.23以來(lái)DECIMAL 值占M + 2 個(gè)字節(jié)。對(duì)于DECIMAL(5, 2),“最長(zhǎng)”的值(- 9 9 9 . 9 9)需要7個(gè)字節(jié)。在正取值范圍的一端,不需要正號(hào),因此MySQL 利用它擴(kuò)充了取值范圍,使其超
過(guò)了ANSI 所規(guī)范所要求的取值范圍。如DECIMAL(5, 2) 的最大值為9 9 9 9 . 9 9,因?yàn)橛? 個(gè)字節(jié)可用。
簡(jiǎn)而言之,在MySQL 3.23 及以后的版本中,DECIMAL(M, D) 的取值范圍等于更早版本中的DECIMAL(M + 2, D) 的取值范圍。在MySQL 的所有版本中,如果某個(gè)DECIMAL 列的D 為0,則不存儲(chǔ)小數(shù)點(diǎn)。這樣做的結(jié)果是擴(kuò)充了列的取值范圍,因?yàn)檫^(guò)去用來(lái)存儲(chǔ)小數(shù)點(diǎn)的字節(jié)現(xiàn)在可用來(lái)存放其他數(shù)字了。
1. 數(shù)值列的類型屬性
可對(duì)所有數(shù)值類型指定ZEROFILL 屬性。它使相應(yīng)列的顯示值用前導(dǎo)零來(lái)填充,以達(dá)到顯示寬度。在希望確定列值總是以給定的數(shù)字位數(shù)顯示時(shí)可利用Z E R O F I L L。實(shí)際上,更準(zhǔn)確地說(shuō)是“一個(gè)給定的最小數(shù)目的數(shù)字位數(shù)”,因?yàn)楸蕊@示寬度更寬的值可完全顯示而未被剪裁。使用下列語(yǔ)句可看到這一點(diǎn):

其中SELECT 語(yǔ)句的輸出結(jié)果如下。請(qǐng)注意最后一行值,它比列的顯示寬度更寬,但仍然完全顯示出來(lái):

如下所示兩個(gè)屬性只用于整數(shù)列:
■ AUTO_INCREMENT。在需要產(chǎn)生唯一標(biāo)識(shí)符或順序值時(shí),可利用AUTO_ INCREMENT屬性。A U TO_INCREMENT 值一般從1開始,每行增加1。在插入NULL 到一個(gè)A U TO _INCREMENT 列時(shí),MySQL 插入一個(gè)比該列中當(dāng)前最大值大1 的值。一個(gè)表中最多只能有一個(gè)A U TO_INCREMENT 列。對(duì)于任何想要使用A U TO_INCREMENT 的列,應(yīng)該定義為NOT NULL,并定義為P R I M A RY KEY 或定義為UNIQUE 鍵。例如, 可按下列任何一種方式定義AUTO_INCREMENT 列:

A U TO_INCREMENT 的性能將在下一小節(jié)“使用序列”中作進(jìn)一步的介紹。
■ U N S I G N E D。此屬性禁用負(fù)值。將列定義為UNSIGNED 并不改變其基本數(shù)據(jù)類型的取值范圍;它只是前移了取值的范圍??紤]下列的表說(shuō)明:

itiny 和itiny_u 兩列都是T I N Y I N T列,并且都可取2 5 6個(gè)值,但是i t i n y的取值范圍為-1 2 8 到1 2 7,而itiny_u 的取值范圍為0 到2 5 5。UNSIGNED 對(duì)不取負(fù)值的列是非常有用的,如存入人口統(tǒng)計(jì)或出席人數(shù)的列。如果用常規(guī)的有符號(hào)列來(lái)存儲(chǔ)這樣的值,那么就只利用了該列類型取值范圍的一半。通過(guò)使列為U N S I G N E D,能有效地成倍增加其取值范圍。如果將列用于序列號(hào),且將它設(shè)為U N S I G N E D,則可取原雙倍的值。在指定以上屬性之后(它們是專門用于數(shù)值列的),可以指定通用屬性NULL 或N O TN U L L。如果未指定NULL 或NOT NULL,則缺省為N U L L。也可以用D E FA U LT 屬性來(lái)指定一個(gè)缺省值。如果不指定缺省值,則會(huì)自動(dòng)選擇一個(gè)。對(duì)于所有數(shù)值列類型,那些可以包含NULL 的列的缺省將為N U L L,不能包含NULL 的列其缺省為0。下面的樣例創(chuàng)建三個(gè)INT 列,它們分別具有缺省值-1、1 和N U L L:

2. 使用序列
許多應(yīng)用程序出于標(biāo)識(shí)的目的需要使用唯一的號(hào)碼。需要唯一值的這種要求在許多場(chǎng)合都會(huì)出現(xiàn),如:會(huì)員號(hào)、試驗(yàn)樣品編號(hào)、顧客I D、錯(cuò)誤報(bào)告或故障標(biāo)簽等等。A U TO_INCREMENT 列可提供唯一編號(hào)。這些列可自動(dòng)生成順序編號(hào)。本節(jié)描述A U TO_INCREMENT 列是怎樣起作用的,從而使您能夠有效地利用它們而不至于出錯(cuò)。另外,還介紹了怎樣不用A U TO_INCREMENT 列來(lái)產(chǎn)生序列的方法。
(1) MySQL 3.23 以前的版本中的A U TO _ I N C R E M E N TMySQL 3.23 版以前的A U TO_INCREMENT 列的性能如下:
■ 插入NULL 到A U TO_INCREMENT 列,使MySQL 自動(dòng)地產(chǎn)生下一個(gè)序列號(hào)并將此序列號(hào)自動(dòng)地插入列中。A U TO_INCREMENT 序列從1 開始,因此插入表中的第一個(gè)記錄得到為1 的序列值,而后繼插入的記錄分別得到序列值2、3 等等。一般,每個(gè)自動(dòng)生成的值都比存儲(chǔ)在該列中的當(dāng)前最大值大1。
■ 插入0 到A U TO_INCREMENT 與插入NULL 到列中的效果一樣。插入一行而不指定A U TO_INCREMENT 列的值也與插入NULL 的效果一樣。
■ 如果插入一個(gè)記錄并明確指定A U TO_INCREMENT 列的一個(gè)值,將會(huì)發(fā)生兩件事之一。如果已經(jīng)存在具有該值的某個(gè)記錄,則出錯(cuò),因?yàn)锳 U TO_INCREMENT 列中的值必須是惟一的。如果不存在具有該值的記錄,那么新記錄將被插入,并且如果新記錄的A U TO_INCREMENT 列中的值是新的最大值,那么后續(xù)行將用該值的下一個(gè)值。換句話說(shuō),也就是可以通過(guò)插入一個(gè)具有比當(dāng)前值大的序列值的記錄,來(lái)增大序列的計(jì)數(shù)器。增大計(jì)數(shù)器會(huì)使序列出現(xiàn)空白,但這個(gè)特性也有用。例如創(chuàng)建一個(gè)具有A U TO _INCREMENT 列的表,但希望序列從1000 而不是1 開始。則可以用后述的兩種辦法之一達(dá)到此目的。一個(gè)辦法是插入具有明確序列值1000 的第一個(gè)記錄,然后通過(guò)插入NULL 到A U TO_INCREMENT 列來(lái)插入后續(xù)的記錄。另一個(gè)辦法是插入
A U TO_INCREMENT 列值為999 的假記錄。然后第一個(gè)實(shí)際插入的記錄將得到一個(gè)序列號(hào)1 0 0 0,這時(shí)再將假記錄刪除。
■ 如果將一個(gè)不合規(guī)定的值插入A U TO_INCREMENT 列,將會(huì)出現(xiàn)難以預(yù)料的結(jié)果。
■ 如果刪除了在A U TO_INCREMENT 列中含有最大值的記錄,則此值在下一次產(chǎn)生新值時(shí)會(huì)再次使用。如果刪除了表中的所有記錄,則所有值都可以重用;相應(yīng)的序列重新從1開始。
■ REPLACE 語(yǔ)句正常起作用。
■ U P D ATE 語(yǔ)句按類似插入新記錄的規(guī)則起作用。如果更新一個(gè)A U TO _ I N C R E M E N T列為NULL 或0,則會(huì)自動(dòng)將其更新為下一個(gè)序列號(hào)。如果試圖更新該列為一個(gè)已經(jīng)存在的值,將出錯(cuò)(除非碰巧設(shè)置此列的值為它所具有的值,才不會(huì)出錯(cuò),但這沒(méi)有任何意義)。如果更新該列的值為一個(gè)比當(dāng)前任何列值都大的值,則以后序列將從下一個(gè)值繼續(xù)進(jìn)行編號(hào)。
■ 最近自動(dòng)產(chǎn)生的序列編號(hào)值可調(diào)用L A S T _ I N S E RT_ID( ) 函數(shù)得到。它使得能在其他不知道此值的語(yǔ)句中引用A U TO_INCREMENT 值。L A S T _ I N S E RT_ID( ) 依賴于當(dāng)前服務(wù)器會(huì)話中生成的A U TO_INCREMENT 值; 它不受與其他客戶機(jī)相關(guān)的A U TO_INCREMENT 活動(dòng)的影響。如果當(dāng)前會(huì)話中沒(méi)有生成A U TO_INCREMENT 值,則L A S T _ I N S E RT_ID( ) 返回0。能夠自動(dòng)生成順序編號(hào)這個(gè)功能特別有用。但是剛才介紹的A U TO_INCREMENT 性能有兩個(gè)缺陷。首先,序列中頂上的記錄被刪除時(shí),序列值的重用使得難于生成可能刪除和插入記錄的應(yīng)用的一系列單調(diào)(嚴(yán)格遞增)值。其次,利用從大于1的值開始生成序列的方法是很笨的。
(2) MySQL 3.23 版以后的A U TO_INCREMENTMySQL 3.23 對(duì)A U TO_INCREMENT 的性能進(jìn)行了下列變動(dòng)以便能夠處理上述問(wèn)題:
■ 自動(dòng)順序生成的值嚴(yán)格遞增且不重用。如果最大的值為143 并刪除了包含這個(gè)值的記錄,MySQL 繼續(xù)生成下一個(gè)值1 4 4。
■ 在創(chuàng)建表時(shí),可以明確指定初始的序列編號(hào)。下面的例子創(chuàng)建一個(gè)A U TO _ I N C R E -MENT 列seq 從1,000,000 開始的表:

在一個(gè)表具有多個(gè)列時(shí)(正如多數(shù)表那樣),最后的A U TO_INCREMENT = 1000000子句應(yīng)用到哪一列是不會(huì)混淆的,因?yàn)槊總€(gè)表只能有一個(gè)A U TO_INCREMENT 列。
(3) 使用A U TO_INCREMENT 應(yīng)該考慮的問(wèn)題在使用A U TO_INCREMENT 列時(shí),應(yīng)該記住下列要點(diǎn):
■ A U TO_INCREMENT 不是一種列類型,它只是一種列類型屬性。此外, A U TO _INCREMENT 是一種只能用于整數(shù)類型的屬性。MySQL 早于3.23 的版本并不嚴(yán)格服從這個(gè)約束,允許定義諸如CHAR 這樣的列類型具有A U TO_INCREMENT 屬性。但是只有整數(shù)類型作為A U TO_INCREMENT 列正常起作用。
■ A U TO_INCREMENT 機(jī)制的主要目的是生成一個(gè)正整數(shù)序列,并且如果以這種方式使用,則A U TO_INCREMENT 列效果最好。所以應(yīng)該定義A U TO_INCREMENT 列為U N S I G N E D。這樣做的優(yōu)點(diǎn)是在到達(dá)列類型的取值范圍上限前可以進(jìn)行兩倍的序列編號(hào)。在某些環(huán)境下,也有可能利用A U TO_INCREMENT 列來(lái)生成負(fù)值的序列,但是我們不建議這樣做。如果您決定要試一下,應(yīng)該保證進(jìn)行充分的試驗(yàn),并且在升級(jí)到不同的MySQL 版本時(shí)需要重新測(cè)試。筆者的經(jīng)驗(yàn)表明,不同的版本中,負(fù)序列的性能并不完全一致。
■ 不要認(rèn)為對(duì)某個(gè)列定義增加A U TO_INCREMENT 是一個(gè)得到無(wú)限的編號(hào)序列的奇妙方法。事實(shí)并非這樣; A U TO_INCREMENT 序列受基礎(chǔ)列類型的取值范圍所限制。例如,如果使用TINYINT UNSIGNED 列,則最大的序列號(hào)為2 5 5。在達(dá)到這個(gè)界限時(shí),應(yīng)用程序?qū)㈤_始出現(xiàn)“重復(fù)鍵”錯(cuò)誤。
■ MySQL 3.23 引入了不重用序列編號(hào)的新A U TO_INCREMENT 性能,并且允許在C R E ATE TABLE 語(yǔ)句中指定一個(gè)初始的序列編號(hào)。這些性能在使用下列形式的DELETE 語(yǔ)句刪除了表中所有記錄后可以撤消:

在此情形下,序列重新從1開始而不按嚴(yán)格的增量順序繼續(xù)增加。即使在C R E AT ETABLE 語(yǔ)句中明確指定了一個(gè)初始的序列編號(hào),相應(yīng)的序列也會(huì)從頭開始。出現(xiàn)這種情形的原因在于MySQL 優(yōu)化完全刪空一個(gè)表的DELETE 語(yǔ)句的方法上;它從頭開始重新創(chuàng)建數(shù)據(jù)文件和索引文件而不是去刪除每個(gè)記錄,這樣就丟失了所有的序列號(hào)信息。如果要?jiǎng)h除所有記錄,但希望保留序列信息,可以取消優(yōu)化并強(qiáng)制MySQL 執(zhí)行逐行的刪除操作,如下所示:

如果使用的是3 . 2 3以上的版本,怎樣保持嚴(yán)格的增量序列?方法之一是保持一個(gè)只用來(lái)生成A U TO_INCREMENT 值的獨(dú)立的表,永遠(yuǎn)不從這個(gè)表中刪除記錄。在這種情況下,獨(dú)立表中的值永遠(yuǎn)不會(huì)重用。在主表中需要生成一個(gè)新記錄時(shí),首先在序列編號(hào)表中插入一個(gè)N U L L。然后對(duì)希望包含序列編號(hào)的列使用L A S T _ I N S E RT_ID( ) 的值將該記錄插入主表,如下所示:

如果想要編寫一個(gè)生成A U TO_INCREMENT 值的應(yīng)用程序,但希望序列從100 而不是1開始。再假定希望這個(gè)程序可移植到所有MySQL 版本。怎樣來(lái)完成它呢?如果可移植是一個(gè)目標(biāo),那么不能依賴MySQL 3.23 所提供的在C R E ATE TABLE 語(yǔ)句中指定初始序列編號(hào)的功能。而是在想要插入一個(gè)記錄時(shí),首先用下列語(yǔ)句檢查表是否是空的:

這個(gè)步驟雖然是附加的,但不會(huì)花費(fèi)太多的時(shí)間,因?yàn)闆](méi)有WHERE 子句的SELECT COUNT(*) 是優(yōu)化的,返回很快。如果表是空的,則插入記錄并明確地對(duì)序列編號(hào)列指定值1 0 0。如果表不空,則對(duì)序列編號(hào)列值指定NULL 使MySQL 自動(dòng)生成下一個(gè)編號(hào)。此方法允許插入序列編號(hào)為1 0 0、101 等的記錄,它不管MySQL 是否允許指定初始序列值都能正常工作。如果要求序列編號(hào)即使是從表中刪除了記錄后也要嚴(yán)格遞增,則此方法不起作用。在這樣的情形下,可將此方法與前面描述的什么也不做只是用來(lái)產(chǎn)生用于主表的序列編號(hào)的輔助表技術(shù)結(jié)合使用。為什么會(huì)希望從一個(gè)大于1 的序列編號(hào)開始呢?一個(gè)原因是想使所有序列編號(hào)全都具有相同的數(shù)字位數(shù)。如果需要生成顧客ID 號(hào),并且希望不要多于一百萬(wàn)個(gè)顧客,則可以從1 000 000
開始編號(hào)。在對(duì)顧客ID 值計(jì)數(shù)的數(shù)字位數(shù)改變之前,可以追加一百萬(wàn)個(gè)顧客。當(dāng)然,強(qiáng)制序列編號(hào)為一個(gè)固定寬度的另一個(gè)方法是采用ZEROFILL 列。對(duì)于有的情形,這樣做有可能會(huì)出問(wèn)題。例如,如果在Perl 或PHP 腳本中處理具有前導(dǎo)零的序列編號(hào),則必須仔細(xì)地將它們只作為串使用;如果將它們轉(zhuǎn)換成數(shù)字,前導(dǎo)零將會(huì)丟失。下面的短Perl 腳本說(shuō)明了處理編號(hào)時(shí)可能會(huì)出的問(wèn)題:

打印時(shí),此腳本給出下列輸出:

P e r l’s‘+ +’自動(dòng)增量操作是很靈巧的而且可以利用串或數(shù)值建立序列值,但‘+ =’操作只應(yīng)用于數(shù)值。在所顯示的輸出中,可看到‘ + =’引起串到數(shù)值的轉(zhuǎn)換并且丟失了$s 值中的前導(dǎo)零。
序列不從1開始的另一個(gè)原因從技術(shù)的角度來(lái)說(shuō)可能不值一提。例如,在分配會(huì)員號(hào)時(shí),序列號(hào)不要從1開始,以免出現(xiàn)關(guān)于誰(shuí)是第一號(hào)的政治爭(zhēng)論。
(4) 不用A U TO_INCREMENT 生成序列生成序列號(hào)的另一個(gè)方法根本就不需要使用A U TO_INCREMENT 列。它利用取一個(gè)參數(shù)的L A S T _ I N S E RT_ID( ) 函數(shù)的變量來(lái)生成序列號(hào)。(這種形式在MySQL 3.22.9. 中引入)如果利用L A S T _ I N S E RT_ID(expr) 來(lái)插入或更新一個(gè)列, 則下一次不用參數(shù)調(diào)用L A S T _ I N S E RT_ID( ) 時(shí),將返回expr 的值。換句話說(shuō),就像由A U TO_INCREMENT 機(jī)制生成的那樣對(duì)expr 進(jìn)行處理。這樣使得能生成一個(gè)序列號(hào),然后可在以后的客戶會(huì)話中利用它,用不著取受其他客戶機(jī)影響的值。利用這種策略的一種方法是創(chuàng)建一個(gè)包含一個(gè)值的單行表,該值在想得到序列中下一個(gè)值時(shí)進(jìn)行更新。例如,可創(chuàng)建如下的表:

上面的語(yǔ)句創(chuàng)建了表seq_table 并用包含seq 值0 的行對(duì)其進(jìn)行初始化??衫眠@個(gè)表產(chǎn)生下一個(gè)序列號(hào),如下所示:

該語(yǔ)句取出seq 列的當(dāng)前值并對(duì)其加1,產(chǎn)生序列中的下一個(gè)值。利用L A S T _ I N S E RT _ID(seq + 1) 生成新值使它就像一個(gè)AUTO_INCREMENT 值一樣,而且此值可在以后的語(yǔ)句中通過(guò)調(diào)用無(wú)參數(shù)的L A S T _ I N S E RT_ID( ) 來(lái)取出。即使某個(gè)其他客戶機(jī)同時(shí)生成了另一個(gè)序列號(hào),上述作用也不會(huì)改變,因?yàn)長(zhǎng) A S T _ I N S E RT_ID( ) 是客戶機(jī)專用的。如果希望生成增量不是1 的編號(hào)序列或負(fù)增量的編號(hào)序列,也可以利用這個(gè)方法。例如,下面兩個(gè)語(yǔ)句可以用來(lái)分別生成一個(gè)增量為100 的編號(hào)序列和一個(gè)負(fù)的編號(hào)序列:

通過(guò)將seq 列設(shè)置為相應(yīng)的初始值,可利用這個(gè)方法生成以任意值開始的序列。關(guān)于將此序列生成方法用于多個(gè)計(jì)數(shù)器的應(yīng)用,可參閱第3章。
2.2.3 串列類型
MySQL 提供了幾種存放字符數(shù)據(jù)的串類型。串常常用于如下這樣的值:

在某種意義上,串實(shí)際是一種“通用”類型,因?yàn)榭捎盟鼈儊?lái)表示任意值。例如,可用串類型來(lái)存儲(chǔ)二進(jìn)制數(shù)據(jù),如影像或聲音,或者存儲(chǔ)gzip 的輸出結(jié)果,即存儲(chǔ)壓縮數(shù)據(jù)。對(duì)于所有串類型,都要剪裁過(guò)長(zhǎng)的值使其適合于相應(yīng)的串類型。但是串類型的取值范圍很不同,有的取值范圍很小,有的則很大。取值大的串類型能夠存儲(chǔ)近4GB 的數(shù)據(jù)。因此,應(yīng)該使串足夠長(zhǎng)以免您的信息被切斷(由于受客戶機(jī)/服務(wù)器通信協(xié)議的最大塊尺寸限制,列
值的最大限額為2 4 M B)。
表2 - 8給出了MySQL 定義串值列的類型,以及每種類型的最大尺寸和存儲(chǔ)需求。對(duì)于可變長(zhǎng)的列類型,各行的值所占的存儲(chǔ)量是不同的,這取決于實(shí)際存放在列中的值的長(zhǎng)度。這個(gè)長(zhǎng)度在表中用L 表示。

L 以外所需的額外字節(jié)為存放該值的長(zhǎng)度所需的字節(jié)數(shù)。MySQL 通過(guò)存儲(chǔ)值的內(nèi)容及其長(zhǎng)度來(lái)處理可變長(zhǎng)度的值。這些額外的字節(jié)是無(wú)符號(hào)整數(shù)。請(qǐng)注意,可變長(zhǎng)類型的最大長(zhǎng)度、此類型所需的額外字節(jié)數(shù)以及占用相同字節(jié)數(shù)的無(wú)符號(hào)整數(shù)之間的對(duì)應(yīng)關(guān)系。例如,
MEDIUMBLOB 值可能最多22 4 - 1字節(jié)長(zhǎng)并需要3 個(gè)字節(jié)記錄其結(jié)果。3 個(gè)字節(jié)的整數(shù)類型MEDIUMINT 的最大無(wú)符號(hào)值為22 4 - 1。這并非偶然。
1. CHAR 和VARCHAR 列類型
CHAR 和VARCHAR 是最常使用的串類型。它們是有差異的, CHAR 是定長(zhǎng)類型而VARCHAR 是可變長(zhǎng)類型。CHAR(M) 列中的每個(gè)值占M 個(gè)字節(jié);短于M 個(gè)字節(jié)的值存儲(chǔ)時(shí)在右邊加空格(但右邊的空格在檢索時(shí)去掉)。VARCHAR(M) 列的值只用所必需的字節(jié)數(shù)來(lái)存放(結(jié)尾的空格在存儲(chǔ)時(shí)去掉,這與ANSI SQL 的VARCHAR 值的標(biāo)準(zhǔn)不同),然后再加一個(gè)字節(jié)記錄其長(zhǎng)度。如果所需的值在長(zhǎng)度上變化不大,則CHAR 是一種比VARCHAR 好的選擇,因?yàn)樘幚硇虚L(zhǎng)度固定的表比處理行長(zhǎng)度可變的表的效率更高。如果所有的值長(zhǎng)度相同,由于需要額外的字節(jié)來(lái)記錄值的長(zhǎng)度,VARCHAR 實(shí)際占用了更多的空間。在MySQL 3.23 以前,CHAR 和VARCHAR 列用最大長(zhǎng)度為1 到255 的M 來(lái)定義。從MySQL 3.23 開始,CHAR(0) 也是合法的了。在希望定義一個(gè)列,但由于尚不知道其長(zhǎng)度,所以不想給其分配空間的情況下, CHAR(0) 列作為占位符很有用處。以后可以用A LT E RTABLE 來(lái)加寬這個(gè)列。如果允許其為N U L L,則CHAR(0) 列也可以用來(lái)表示o n / o ff 值。這樣的列可能取兩個(gè)值,NULL 和空串。CHAR(0) 列在表中所占的空間很小,只占一位。除少數(shù)情況外,在同一個(gè)表中不能混用CHAR 和VA R C H A R。MySQL 根據(jù)情況甚至?xí)⒘袕囊环N類型轉(zhuǎn)換為另一種類型。這樣做的原因如下:
■ 行定長(zhǎng)的表比行可變長(zhǎng)的表容易處理(其理由請(qǐng)參閱2 . 3節(jié)“選擇列的類型”)。
■ 表行只在表中所有行為定長(zhǎng)類型時(shí)是定長(zhǎng)的。即使表中只有一列是可變長(zhǎng)的,該表的行也是可變長(zhǎng)的。
■ 因?yàn)樵谛锌勺冮L(zhǎng)時(shí)定長(zhǎng)行的性能優(yōu)點(diǎn)完全失去。所以為了節(jié)省存儲(chǔ)空間,在這種情況下最好也將定長(zhǎng)列轉(zhuǎn)換為可變長(zhǎng)列。這表示,如果表中有VARCHAR 列,那么表中不可能同時(shí)有CHAR 列;MySQL 會(huì)自動(dòng)地將它們轉(zhuǎn)換為VARCHAR 列。例如創(chuàng)建如下一個(gè)表:

請(qǐng)注意,VARCHAR 列的出現(xiàn)使MySQL 將c1 也轉(zhuǎn)換成了VARCHAR 類型。如果試圖用A LTER TABLE 將c1 轉(zhuǎn)換為C H A R,將不起作用。將VARCHAR 列轉(zhuǎn)換為CHAR 的惟一辦法是同時(shí)轉(zhuǎn)換表中所有VARCHAR 列:

BLOB 和TEXT 列類型像VARCHAR 一樣是可變長(zhǎng)的,但是它們沒(méi)有定長(zhǎng)的等價(jià)類型,因此不能在同一表中與BLOB 或TEXT 列一起使用CHAR 列。這時(shí)任何CHAR 列都將被轉(zhuǎn)換為VARCHAR 列。定長(zhǎng)與可變長(zhǎng)列混用的情形是在CHAR 列短于4 個(gè)字符時(shí),可以不對(duì)其進(jìn)行轉(zhuǎn)換。例如,MySQL 不會(huì)將下面所創(chuàng)建的表中的CHAR 列轉(zhuǎn)換為VARCHAR 列:

短于4個(gè)字符的列不轉(zhuǎn)換的原因是,平均情況下,不存儲(chǔ)尾空格所節(jié)省的空間被VA R C H A R列中記錄每個(gè)值的長(zhǎng)度所需的額外字節(jié)所抵消了。實(shí)際上,如果所有列都短, MySQL 將會(huì)把所定義的所有列從VARCHAR 轉(zhuǎn)換為C H A R。MySQL 這樣做的原因是,這種轉(zhuǎn)換平均來(lái)說(shuō)不會(huì)增加存儲(chǔ)需求,而且使表行定長(zhǎng),從而改善了性能。如果按如下創(chuàng)建一個(gè)表,VARCHAR 列全都會(huì)轉(zhuǎn)換為CHAR 列:


2. BLOB 與TEXT 列類型
BLOB 是一個(gè)二進(jìn)制大對(duì)象,是一個(gè)可以存儲(chǔ)大量數(shù)據(jù)的容器,可以使其任意大。在MySQL 中,BLOB 類型實(shí)際是一個(gè)類型系列( T I N Y B L O B、B L O B、M E D I U M B L O B、L O N G B L O B),除了在可以存儲(chǔ)的最大信息量上不同外(請(qǐng)參閱表2 - 8),它們是等同的。
MySQL 還有一個(gè)TEXT 類型系列( T I N Y T E X T、T E X T、M E D I U M T E X T、L O N G T E X T)。除了用于比較和排序外,它們?cè)诟鱾€(gè)方面都與相應(yīng)的BLOB 類型等同,BLOB 值是區(qū)分大小寫的,而TEXT 值不區(qū)分大小寫。BLOB 和TEXT 列對(duì)于存儲(chǔ)可能有很大增長(zhǎng)的值或各行大小有很大變化的值很有用,例如,字處理文檔、圖像和聲音、混合數(shù)據(jù)以及新聞文章等等。BLOB 或TEXT 列在MySQL 3.23 以上版本中可以進(jìn)行索引,雖然在索引時(shí)必須指定一個(gè)用于索引的約束尺寸,以免建立出很大的索引項(xiàng)從而抵消索引所帶來(lái)的好處。除此之外,一般不通過(guò)查找BLOB 或TEXT 列來(lái)進(jìn)行搜索,因?yàn)檫@樣的列常常包含二進(jìn)制數(shù)據(jù)(如圖像)。常見的做法是用表中另外的列來(lái)記錄有關(guān)BLOB 或TEXT 值的某種標(biāo)識(shí)信息,并用這些信息來(lái)確定想要哪些行。使用BLOB 和TEXT 列需要特別注意以下幾點(diǎn):
■ 由于BLOB 和TEXT 值的大小變化很大,如果進(jìn)行的刪除和更新很多,則存儲(chǔ)它們的
表出現(xiàn)高碎片率會(huì)很高。應(yīng)該定期地運(yùn)行OPTIMIZE TABLE 減少碎片率以保持良好的
性能。要了解更詳細(xì)的信息請(qǐng)參閱第4章。
■ 如果使用非常大的值,可能會(huì)需要調(diào)整服務(wù)器增加max_allowed_packet 參數(shù)的值。詳細(xì)的信息請(qǐng)參閱第11章“常規(guī)的MySQL 管理”。如果需要增加希望使用非常大的值的客戶機(jī)的塊尺寸,可見附錄E“MySQL 程序參考”,該附錄介紹了怎樣對(duì)mysql 和mysqldump 客戶機(jī)進(jìn)行這種塊尺寸的增加。
3. ENUM 和SET 列類型
ENUM 和SET 是一種特殊的串類型,其列值必須從一個(gè)固定的串集中選擇。它們之間的主要差別是ENUM 列值必須確實(shí)是值集中的一個(gè)成員,而SET 列值可以包括集合中任意或所有的成員。換句話說(shuō), ENUM 用于互相排斥的值,而S E T列可以從一個(gè)值的列表中選擇多個(gè)值。
ENUM 列類型定義了一個(gè)枚舉??少x予ENUM 列一個(gè)在創(chuàng)建表時(shí)指定的值列表中選擇的成員。枚舉可具有最多65 536 個(gè)成員(其中之一為MySQL 保留)。枚舉通常用來(lái)表示類別值。例如,定義為E N U M (“N”, “Y”) 的列中的值可以是“N”或“Y”?;蛘呖蓪NUM 用于諸如調(diào)查或問(wèn)卷中的多項(xiàng)選擇問(wèn)題,或用于某個(gè)產(chǎn)品的可能尺寸或顏色等:

如果正在處理Web 頁(yè)中的選擇,那么可以利用ENUM 來(lái)表示站點(diǎn)訪問(wèn)者在某頁(yè)上的互相排斥的單選鈕集合中進(jìn)行的選擇。例如,如果運(yùn)行一個(gè)在線比薩餅訂購(gòu)服務(wù)系統(tǒng),可用ENUM 來(lái)表示顧客訂購(gòu)的比薩餅形狀:

如果枚舉類別表示計(jì)數(shù),在建立該枚舉時(shí)最重要的是選擇合適的類別。例如,在記錄實(shí)驗(yàn)室檢驗(yàn)中白血球的數(shù)目時(shí),可能會(huì)將計(jì)數(shù)分為如下的幾組:

在某個(gè)測(cè)試結(jié)果以精確的計(jì)數(shù)到達(dá)時(shí),要根據(jù)該值所屬的類別來(lái)記錄它。但如果想將列從基于類別的ENUM 轉(zhuǎn)換為基于精確計(jì)數(shù)的整數(shù)時(shí),不可能恢復(fù)原來(lái)的計(jì)數(shù)。在創(chuàng)建SET 列時(shí),要指定一個(gè)合法的集合成員列表。在這種意義上, SET 類型與E N U M是類似的。但是SET 與ENUM 不同,每個(gè)列值可由來(lái)自集合中任意數(shù)目的成員組成。集合中最多可有64 個(gè)成員。對(duì)于值之間互斥的固定集合,可使用SET 列類型。例如,可利用SET 來(lái)表示汽車的可用選件,如下所示:

然后,特定的SET 值將表示顧客實(shí)際訂購(gòu)哪些選件,如下所示:

空串表示顧客未訂購(gòu)任何選件。這是一個(gè)合法的SET 值。SET 列值為單個(gè)串。如果某個(gè)值由多個(gè)集合成員組成,那么這些成員在串中用逗號(hào)分隔。顯然,這表示不應(yīng)該用含有逗號(hào)的串作為SET 成員。SET 列的其他用途是表示諸如病人的診斷或來(lái)自Web 頁(yè)的選擇結(jié)果這樣的信息。對(duì)于診斷,可能會(huì)有一個(gè)向病人提問(wèn)的標(biāo)準(zhǔn)癥狀清單,而病人可能會(huì)表現(xiàn)出某些癥狀或所有的癥狀。對(duì)于在線比薩餅服務(wù)系統(tǒng),用于訂購(gòu)的Web 頁(yè)應(yīng)該具有一組復(fù)選框,用來(lái)表示顧客想在比薩餅上加的配料。對(duì)ENUM 或SET 列的合法值列表的定義很重要,例如:
■ 正如上面所介紹的,此列表決定了列的可能合法值。
■ 可按任意的大小寫字符插入ENUM 或SET 值,但是列定義中指定的串的大小寫字符決定了以后檢索它們時(shí)的大小寫。例如,如果有一個(gè)E N U M (“Y”, “N”) 列,但您在其中存儲(chǔ)了“ y”和“n”,當(dāng)您檢索出它們時(shí)顯示的是“ Y”和“N”。這并不影響比較或排序的狀態(tài),因?yàn)镋NUM 和SET 列是不區(qū)分大小寫的。
■ 在ENUM 定義中的值順序就是排序順序。SET 定義中的值順序也決定了排序順序,但是這個(gè)關(guān)系更為復(fù)雜,因?yàn)榱兄悼赡馨ǘ鄠€(gè)集合成員。
■ SET 定義中的值順序決定了在顯示由多個(gè)集合成員組成的SET 列值時(shí),子串出現(xiàn)的順序。
ENUM 和SET 被歸為串類型是由于在建立這些類型的列時(shí),枚舉和集合成員被指定為串。但是,這些成員在內(nèi)部存放時(shí)作為數(shù)值,而且同樣可作為數(shù)值來(lái)處理。這表示ENUM 和S E T類型比其他的串類型更為有效,因?yàn)橥ǔ?捎脭?shù)值運(yùn)算而不是串運(yùn)算來(lái)處理它們。而且這還表示ENUM 和SET 值可用在串或數(shù)值的環(huán)境中。
列定義中的ENUM 成員是從1 開始順序編號(hào)的。(0 被MySQL 用作錯(cuò)誤成員,如果以串的形式表示就是空串。)枚舉值的數(shù)目決定了ENUM 列的存儲(chǔ)大小。一個(gè)字節(jié)可表示256 個(gè)值,兩個(gè)字節(jié)可表示65 536 個(gè)值。(可將其與一字節(jié)和兩字節(jié)的整數(shù)類型T I N Y I N T、
UNSIGNED 和SMALLINT UNSIGNED 進(jìn)行對(duì)比。)因此,枚舉成員的最大數(shù)目為65 536(包括錯(cuò)誤成員),并且存儲(chǔ)大小依賴于成員數(shù)目是否多于256 個(gè)。在ENUM 定義中,可以最多指定65 535(而不是65 536)個(gè)成員,因?yàn)镸ySQL 保留了一個(gè)錯(cuò)誤成員,它是每個(gè)枚舉的隱含成員。在將一個(gè)非法值賦給ENUM 列時(shí),MySQL 自動(dòng)將其換成錯(cuò)誤成員。下面有一個(gè)例子,可用mysql 客戶機(jī)程序測(cè)試一下。它給出枚舉成員的數(shù)值順序,而且還說(shuō)明了NULL 值無(wú)順序編號(hào):

可對(duì)ENUM 成員按名或者按編號(hào)進(jìn)行運(yùn)算,例如:

可以定義空串為一個(gè)合法的枚舉成員。與列在定義中的其他成員一樣,它將被賦予一個(gè)非零的數(shù)值。但是使用空串可能會(huì)引起某些混淆,因?yàn)樵摯脖蛔鳛閿?shù)值為0 的錯(cuò)誤成員。在下面的例子中,將非法的枚舉值“ x”賦予ENUM 列引起了錯(cuò)誤成員的賦值。僅在以數(shù)值
形式進(jìn)行檢索時(shí),才能夠與空串區(qū)分開:

SET 列的數(shù)值表示與ENUM 列的表示有所不同,集合成員不是順序編號(hào)的。每個(gè)成員對(duì)應(yīng)SET 值中的一個(gè)二進(jìn)制位。第一個(gè)集合成員對(duì)應(yīng)于0 位,第二個(gè)成員對(duì)應(yīng)于1 位,如此等等。數(shù)值SET 值0 對(duì)應(yīng)于空串。SET 成員以位值保存。每個(gè)字節(jié)的8 個(gè)集合值可按此方式存
放,因此SET 列的存儲(chǔ)大小是由集合成員的數(shù)目決定的,最多64 個(gè)成員。對(duì)于大小為1 到8、9 到1 6、17 到2 4、25 到3 2、33 到64 個(gè)成員的集合,其SET 值分別占用1、2、3、4 或8個(gè)字節(jié)。
用一組二進(jìn)制位來(lái)表示SET 正是允許SET 值由多個(gè)集合成員組成的原因。值中二進(jìn)制位的任意組合都可以得到,因此,相應(yīng)的值可由對(duì)應(yīng)于這些二進(jìn)制位的SET 定義中的串組合構(gòu)成。下面給出一個(gè)說(shuō)明SET 列的串形式與數(shù)值形式之間關(guān)系的樣例;數(shù)值以十進(jìn)制形式和二
進(jìn)制形式分別給出:

如果給SET 列賦予一個(gè)含有未作為集合成員列出的子串的值,那么這些子串被刪除,并將包含其余子串的值賦予該列。在賦值給SET 列時(shí),子串不需要按定義該列時(shí)的順序給出。但是,在以后檢索該值時(shí),各成員將按定義時(shí)的順序列出。假如用下面的定義定義一個(gè)S E T列來(lái)表示家具:

如果給這個(gè)列賦予“ c h a i r, couch, table”值,那么,“c o u c h”被放棄,因?yàn)樗皇羌系某蓡T。其次,以后檢索這個(gè)值時(shí),顯示為“ table, chair”。之所以這樣是因?yàn)镸ySQL 針對(duì)所賦的值的每個(gè)子串決定各個(gè)二進(jìn)制位并在存儲(chǔ)值時(shí)將它們置為1。“c o u c h”不對(duì)應(yīng)二進(jìn)制位,則忽略。在檢索時(shí),MySQL 按順序掃描各二進(jìn)制位,通過(guò)數(shù)值值構(gòu)造出串值,它自動(dòng)地將子串排成定義列時(shí)給出的順序。這個(gè)舉動(dòng)還表示,如果在一個(gè)值中不止一次地指定某個(gè)成員,但在檢索時(shí)它也只會(huì)出現(xiàn)一次。如果將“ lamp, lamp,lamp”賦予某個(gè)SET 列,檢索時(shí)也只會(huì)得出“l(fā) a m p”。MySQL 重新對(duì)SET 值中的成員進(jìn)行排序這個(gè)事實(shí)表示,如果用一個(gè)串來(lái)搜索值,則必須以正確的順序列出各成員。如果插入“ c h a i r, table”,然后搜索“c h a i r, table”,那么將找不到相應(yīng)的記錄;必須查找“ table, chair”才能找到。ENUM 和SET 列的排序和索引是根據(jù)列值的內(nèi)部值(數(shù)值值)進(jìn)行的。下面的例子可能會(huì)顯示不正確,因?yàn)楦鱾€(gè)值并不是按字母順序存儲(chǔ)的:


NULL 值排在其他值前(如果是降序,將排在其他值之后)。如果有一個(gè)固定的值集,并且希望按特殊的次序進(jìn)行排序,可利用ENUM 的排序順序。在創(chuàng)建表時(shí)做一個(gè)ENUM 列,并在該列的定義中以所想要的次序給出各枚舉值即可。如果希望ENUM 按正常的字典順序排序,可使用C O N C AT( ) 和排序結(jié)果將列轉(zhuǎn)換成一個(gè)非ENUM 串,如下所示:

4. 串列類型屬性
可對(duì)CHAR 和VARCHAR 類型指定B I N A RY 屬性使列值作為二進(jìn)制串處理(即,在比較和排序操作區(qū)分大小寫)。
可對(duì)任何串類型指定通用屬性NULL 和NOT NULL。如果兩者都不指定,缺省值為N U L L。但是定義某個(gè)串列為NOT NULL 并不阻止其取空串??罩挡煌谶z漏的值,因此,不要錯(cuò)誤地認(rèn)為可以通過(guò)定義NOT NULL 來(lái)強(qiáng)制某個(gè)串列只包含非空的值。如果要求串值非
空,那么這是一個(gè)在應(yīng)用程序中必須強(qiáng)制實(shí)施的約束條件。
還可以對(duì)除BLOB 和TEXT 類型外的所有串列類型用D E FA U LT 屬性指定一個(gè)缺省值。如果不指定缺省值, MySQL 會(huì)自動(dòng)選擇一個(gè)。對(duì)于可以包含NULL 的列,其缺省值為N U L L。對(duì)于不能包含NULL 的列,除ENUM 列外都為空串,在ENUM 列中,缺省值為第一個(gè)枚舉成員(對(duì)于SET 類型,在相應(yīng)的列不能包含NULL 時(shí)其缺省值實(shí)際上是空集,不過(guò)這里空集等價(jià)于空串)。
2.2.4 日期和時(shí)間列類型
MySQL 提供了幾種時(shí)間值的列類型,它們分別是: DATE、DATE TIME、TIME、TIMES TAMP 和YEAR。表2-9 給出了MySQL 為定義存儲(chǔ)日期和時(shí)間值所提供的這些類型,并給出了每種類型的合法取值范圍。YEAR 類型是在MySQL 3.22版本中引入的。其他類型在所有MySQL 版本中都可用。每種時(shí)間類型的存儲(chǔ)需求見表2 - 1 0。每個(gè)日期和時(shí)間類型都有一個(gè)“零”值,在插入該類型的一個(gè)非法值時(shí)替換成此值,見表2 - 11。這個(gè)值也是定義為NOT NULL 的日期和時(shí)間列的缺省值。


MySQL 表示日期時(shí)根據(jù)ANSI 規(guī)范首先給出年份。例如,1999 年12 月3 日表示為“1 9 9 9 - 1 2 - 0 3”。MySQL 允許在輸入日期
時(shí)有某些活動(dòng)的余地。如能將兩個(gè)數(shù)字的年份轉(zhuǎn)換成四位數(shù)字的年份,而且在輸入小于10 的月份和日期時(shí)不用輸入前面的那位數(shù)字。但是必須首先給出年份。平常經(jīng)常使用的那些格式,如“ 1 2 / 3 / 9 9”或“3 / 1 2 / 9 9”,都是不正確的。MySQL 使用的日期表示規(guī)則請(qǐng)參閱“處理日期和時(shí)間列”小節(jié)。時(shí)間值按本地時(shí)區(qū)返回給服務(wù)器; MySQL 對(duì)返回給客戶機(jī)的值不作任何時(shí)區(qū)調(diào)整。
1. DATE、TIME 和DATETIME 列類型DATE、TIME 和DATETIME 類型存儲(chǔ)日期、時(shí)間以及日期和時(shí)間值的組合。其格式為“YYYY - MM - DD”、“h h : m m : s s”和“YYYY - MM - DD hh:mm:ss”。對(duì)于D ATETIME 類型,日期和時(shí)間部分都需要;如果將D ATE 值賦給DATETIME 列,MySQL 會(huì)自動(dòng)地追加一個(gè)為“0 0 : 0 0 : 0 0”的時(shí)間部分。MySQL 對(duì)D ATETIME 和TIME 表示的時(shí)間在處理上稍有不同。對(duì)于D ATETIME ,時(shí)間部分表示某天的時(shí)間。而TIME 值表示占用的時(shí)間(這也就是為什么其取值范圍如此之大而且允許取負(fù)值的原因)。用TIME 值的最右邊部分表示秒,因此,如果插入一個(gè)“短”(不完全)的時(shí)間值,如“1 2 : 3 0”到TIME 列,則存儲(chǔ)的值為“ 0 0 : 1 2 : 3 0”,即被認(rèn)為是“12 分30 秒”。如果愿意,也可用TIME 列來(lái)表示天的時(shí)間,但是要記住這個(gè)轉(zhuǎn)換規(guī)則以免出問(wèn)題。為了插入一個(gè)“12 小時(shí)30 分鐘”的值,必須將其表示為“ 1 2 : 3 0 : 0 0”。
2. TIMESTAMP 列類型
TIMES TAMP 列以YYYYMMDDhhmmss 的格式表示值,其取值范圍從19700101000000到2037 年的某個(gè)時(shí)間。此取值范圍與UNIX 的時(shí)間相聯(lián)系,在UNIX 的時(shí)間中,1970 年的第一天為“零天”,也就是所謂的“新紀(jì)元”。因此1970 年的開始決定了T I M E S TAMP 取值范圍的低端。其取值范圍的上端對(duì)應(yīng)于UNIX 時(shí)間上的四字節(jié)界限,它可以表示到2037年的值。(TIMES TAMP 值的上限將會(huì)隨著操作系統(tǒng)為擴(kuò)充UNIX 的時(shí)間值所進(jìn)行的修改而增加。這是在系統(tǒng)庫(kù)一級(jí)必須提及的。MySQL 也將利用這些更改。)TIMES TAMP 類型之所以得到這樣的名稱是因?yàn)樗趧?chuàng)建或修改某個(gè)記錄時(shí),有特殊的記錄作用。如果在一個(gè)TIMES TAMP 列中插入NULL,則該列值將自動(dòng)設(shè)置為當(dāng)前的日期和時(shí)間。在建立或更新一行但不明確給TIMES TAMP 列賦值時(shí)也會(huì)自動(dòng)設(shè)置該列的值為當(dāng)前的日期和時(shí)間。但是,僅行中的第一個(gè)TIMES TAMP 列按此方式處理,即使是行中第一個(gè)TIMESTAMP列,也可以通過(guò)插入一個(gè)明確的日期和時(shí)間值到該列(而不是NULL)使該處理失效。

TIMES TAMP 列的定義可包含對(duì)最大顯示寬度M 的說(shuō)明。表2 - 1 2給出了所允許的M 值的顯示格式。如果TIMES TAMP 定義中省略了M 或者其值為0或大于1 4,則該列按TIMES TAMP(14) 處理。取值范圍從1到1 3的M 奇數(shù)值作為下一個(gè)更大的偶數(shù)值處理。T I M E S TAMP 列的顯示寬度與存儲(chǔ)大小或存儲(chǔ)在內(nèi)部的值無(wú)關(guān)。TIMES TAMP 值總是以4 字節(jié)存放并按14 位精度進(jìn)行計(jì)算,與顯示寬度無(wú)關(guān)。為了明白這一點(diǎn),按如下定義一個(gè)表,然后插入一些行,進(jìn)行檢索:

從表面上看,出現(xiàn)的行排序有誤,第一列中的值全都相同,所以似乎排序是根據(jù)第二列中的值進(jìn)行的。這個(gè)表面反常的結(jié)果是由于事實(shí)上, MySQL 是根據(jù)插入T I M E S TAMP 列的全部14 位值進(jìn)行排序的。MySQL 沒(méi)有可在記錄建立時(shí)設(shè)置為當(dāng)前日期和時(shí)間、并從此以后保持不變的列類型。如果要實(shí)現(xiàn)這一點(diǎn),可用兩種方法來(lái)完成:
■ 使用T I M E S TAMP 列。在最初建立一個(gè)記錄時(shí),設(shè)置該列為N U L L,將其初始化為當(dāng)前日期和時(shí)間:

在以后無(wú)論何時(shí)更改此記錄,都要明確地設(shè)置此列為其原有的值。賦予一個(gè)明確的值使時(shí)間戳機(jī)制失效,因?yàn)樗柚沽嗽摿械闹底詣?dòng)更新:

■ 使用D ATETIME 列。在建立記錄時(shí),將該列的值初始化為NOW( ):

無(wú)論以后何時(shí)更新此記錄,都不能動(dòng)該列:U P D ATE tbl_name SET /* angthing BUT dt_col here */ WHERE ...如果想利用T I M E S TAMP 列既保存建立的時(shí)間值又保存最后修改的時(shí)間值,那么可用一個(gè)T I M E S TAMP 列來(lái)保存修改時(shí)間值,用另一個(gè)T I M E S TAMP 列保存建立時(shí)間值。要保證保存修改時(shí)間值的列為第一個(gè)T I M E S TA M P,從而在記錄建立或更改時(shí)自動(dòng)對(duì)其進(jìn)行設(shè)置。使保存建立時(shí)間值的列為第二個(gè)T I M E S TA M P,并在建立新記錄時(shí)將其初始化為NOW( )。這樣第二個(gè)T I M E S TAMP 的值將反映記錄建立時(shí)間,而且以后將不再更改。
3. YEAR 列類型
YEAR 是一個(gè)用來(lái)有效地表示年份值的1個(gè)字節(jié)的列類型。其取值范圍為從1901 到2 1 5 5。在想保存日期信息但又只需要日期的年份時(shí)可使用YEAR 類型,如出生年份、政府機(jī)關(guān)選舉年份等等。在不需要完全的日期值時(shí), YEAR 比其他日期類型在空間利用上更為有效。
YEAR 列的定義可包括顯示寬度M 的說(shuō)明,顯示寬度應(yīng)該為4 或2。如果YEAR 定義中省略了M,其缺省值為4。TINYINT 與YEAR 具有相同的存儲(chǔ)大?。ㄒ粋€(gè)字節(jié)),但取值范圍不同。要使用一個(gè)整數(shù)類型且覆蓋與YEAR 相同的取值范圍,可能需要SMALLINT 類型,此類型要占兩倍的空間。在所要表示的年份取值范圍與YEAR 類型的取值范圍相同的情況下, YEAR 的空間利用率比SMALLINT 更為有效。YEAR 相對(duì)整數(shù)列的另一個(gè)優(yōu)點(diǎn)是MySQL 將會(huì)利用MySQL 的年份推測(cè)規(guī)則把2 位值轉(zhuǎn)換為4 位值。例如,97 與14 將轉(zhuǎn)換為1997 和2 0 1 4。但要認(rèn)識(shí)到,插入數(shù)值00 將得到0000 而不是2 0 0 0。如果希望零值轉(zhuǎn)換為2 0 0 0,必須指定其為串“0 0”。

4. 日期和時(shí)間列類型的屬性沒(méi)有專門針對(duì)日期和時(shí)間列類型的屬性。通用屬性NULL 和NOT NULL 可用于任意日期和時(shí)間類型。如果NULL 和NOT NULL 兩者都不指定,則缺省值為N U L L。也可以用DEFA ULT 屬性指定一個(gè)缺省值。如果不指定缺省值,將自動(dòng)選擇一個(gè)缺
省值。含有NULL 的列的缺省值為NULL 。否則,缺省值為該類型的“零”值。
5. 處理日期和時(shí)間列MySQL 可以理解各種格式的日期和時(shí)間值。D ATE 值可按后面的任何一種格式指定,其中包括串和數(shù)值形式。表2 - 1 3為每種日期和時(shí)間類型所允許的格式。兩位數(shù)字的年度值的格式用“歧義年份值的解釋”中所描述的規(guī)則來(lái)解釋。對(duì)于有分隔
符的串格式,不一定非要用日期的“ -”符號(hào)和時(shí)間的“ :”符號(hào)來(lái)分隔,任何標(biāo)點(diǎn)符號(hào)都可用作分隔符,因?yàn)橹档慕忉屓Q于上下文,而不是取決于分隔符。例如,雖然時(shí)間一般是用分隔符“:”指定的,但MySQL 并不會(huì)在一個(gè)需要日期的上下文中將含有“ :”號(hào)的值理解成時(shí)間。此外,對(duì)于有分隔符的串格式,不需要為小于10 的月、日、小時(shí)、分鐘或秒值指定兩個(gè)數(shù)值。下列值是完全等同的:

請(qǐng)注意,有前導(dǎo)零的值根據(jù)它們被指定為串或數(shù)有不同的解釋。串“ 0 0 1 2 3 1”將視為一個(gè)六位數(shù)字的值并解釋為D ATE 的“2 0 0 0 - 1 2 - 3 1”和D ATETIME 的“2000-12-31 00:00:00”。而數(shù)0 0 1 2 3 1被認(rèn)為1 2 3 1,這樣的解釋就有問(wèn)題了。這種情形最好使用串值,或者如果要使用數(shù)值的話,應(yīng)該用完全限定的值(即, D ATE 用2 0 0 0 1 2 3 1,D ATETIME 用2 0 0 0 1 2 3 1 0 0 0 0)。通常,在D AT E、D ATETIME 和T I M E S TAMP 類型之間可以自由地賦值,但是應(yīng)該記住以下一些限制:
■ 如果將D ATETIME 或T I M E S TAMP 值賦給D AT E,則時(shí)間部分被刪除。
■ 如果將D ATE 值賦給D ATETIME 或T I M E S TA M P,結(jié)果值的時(shí)間部分被設(shè)置為零。
■ 各種類型具有不同的取值范圍。T I M E S TAMP 的取值范圍更受限制( 1970 到2 0 3 7),因此,比方說(shuō),不能將1970 年以前的DATETIME 值賦給T I M E S TAMP 并得到合理的結(jié)果。也不能將2037 以后的值賦給TIMES TAMP。MySQL 提供了許多處理日期和時(shí)間值的函數(shù)。要了解更詳細(xì)的信息請(qǐng)參閱附錄C。
6. 歧義年份值的理解
對(duì)于所有包括年份部分的日期和時(shí)間類型( DATE、DATE TIME、TIME STAMP、YEAR),MySQL將兩位數(shù)字的年份轉(zhuǎn)換為四位數(shù)字的年份。這個(gè)轉(zhuǎn)換根據(jù)下列規(guī)則進(jìn)行(在MySQL4.0 中,這些規(guī)則稍有改動(dòng),其中69 將轉(zhuǎn)換為1969 而不是2069。這是根據(jù)X/Open UNIX 標(biāo)準(zhǔn)規(guī)定的規(guī)則作出的改動(dòng)):
■ 00 到69 的年份值轉(zhuǎn)換為2000 到2069。
■ 70 到99 的年份值轉(zhuǎn)換為1970 到1999。
通過(guò)將不同的兩位數(shù)字值賦給一個(gè)YEAR 列然后進(jìn)行檢索,可很容易地看到這些規(guī)則的效果。下面是檢索程序:

請(qǐng)注意,00 轉(zhuǎn)換為0000 而不是2 0 0 0。這是因?yàn)? 是YEAR 類型的一個(gè)完全合法的值;如果插入一個(gè)數(shù)值,得到的就是這個(gè)結(jié)果。要得到2 0 0 0,應(yīng)該插入串“ 0”或“0 0”??赏ㄟ^(guò)C O N C AT( ) 插入YEAR 值來(lái)保證MySQL 得到一個(gè)串而不是數(shù)。C O N C AT( ) 函數(shù)不管其參數(shù)是串或數(shù)值,都返回一個(gè)串結(jié)果。請(qǐng)記住,將兩位數(shù)字的年份值轉(zhuǎn)換為四位數(shù)字的年份值的規(guī)則只產(chǎn)生一種結(jié)果。在未給
定世紀(jì)的情況下,MySQL 沒(méi)有辦法肯定兩位數(shù)字的年份的含義。如果MySQL 的轉(zhuǎn)換規(guī)則不能得出您所希望的值,解決的方法很簡(jiǎn)單:即用四位數(shù)字輸入年份值。MySQL 有千年蟲問(wèn)題嗎?MySQL 自身是沒(méi)有2000 年問(wèn)題的,因?yàn)樗趦?nèi)部是按四位數(shù)年份存儲(chǔ)日期值的,并且由用戶負(fù)責(zé)提供恰當(dāng)?shù)娜掌谥?。兩位?shù)字年份解釋的實(shí)際問(wèn)題不是MySQL 帶來(lái)的,而是由于有的人想省事,輸入歧義數(shù)據(jù)所引起的問(wèn)題。如果您愿意冒險(xiǎn),可以繼續(xù)這樣做。在您冒險(xiǎn)的時(shí)候,MySQL 的猜測(cè)規(guī)則是可以使用的。但要意識(shí)到,很多時(shí)候您確實(shí)需要輸入四位數(shù)字的年份。例如, p r e s i d e n t表列出了1700 年以來(lái)的美國(guó)總統(tǒng),所以在此表中錄入出生與死亡日期需要四位的年份值。這些列中的年份值跨了好幾個(gè)世紀(jì),因此,讓MySQL 從兩位數(shù)字的年份去猜測(cè)是哪個(gè)世紀(jì)是不可能的。