選擇MYSQL列的類型

      在實際建立一個表時怎樣決定用哪些類型呢?本節討論在作出決定前應考慮的各類因素。最「經常使用」的列類型是串類型。可將任何數據存儲爲串,由於數和日期均可以串的形式表示。可是爲何不將全部列都定義爲串從而結束這裏的討論呢?讓咱們來看一個簡單的例子。假定有一些看起來像數的值。可將它們表示爲串,但應該這樣作嗎?這樣作會發生什麼事?

  有一樁事不可避免,那就是可能要使用更多的空間,由於較串來講,數的存儲更爲有效。咱們可能已經注意到,因爲數和串處理方式的不一樣,查詢結果也有所不一樣。例如,數的排序與串的排序就有所不一樣。數2
小於數11,但串「2」按字典順序大於「 11」。可用以下數值內容的列來搞清這個問題mysql

  Mysql入門系列:選擇MYSQL列的類型

  將零加到該列強制得出一個數值,可是這樣合理嗎?通常可能不合理。將該列做爲數而不是串具備幾個重要的含義。它對每一個列值實施串到數的轉換,這是低效的。並且將該列的值轉換爲計算結果妨礙MySQL使用該列上的索引,下降了之後的查詢速度。若是這些值一開始就是做爲數值存儲的,那麼這些性能上的下降都不會出現。採用一種表示而不用另外一種的簡單選擇實際上並不簡單,它在存儲需求、查詢效率以及處理性能等方面都會產生重要的影響。 sql

  前面的例子說明,在選擇列類型時,有如下幾個問題須要考慮: 數據庫

  ■ 列中存儲何種類型的值?這是一個顯而易見的問題,但必須肯定。可將任何類型的值表示爲串,尤爲當對數值使用更爲合適的類型可能獲得更好的性能時(日期和時間值也是這樣)。可見,對要處理的值的類型進行評估不必定是件微不足道的事,特別在數據是別人的數據時更是如此。若是正在爲其餘人創建一個表,搞清列中要存儲的值的類型極爲重要,必須提足夠多的問題以便獲得做出決定的充足的信息。 數據庫設計

  ■ 列值有特定的取值範圍嗎?若是它們是整數,它們老是非負值嗎?若是這樣,可採用UNSIGNED類型。若是它們是串,總能從定長值集中選出它們嗎?若是這樣,ENUM或SET是很合適的類型。在類型的取值範圍與所用的存儲量之間存在折衷。需有一個多「大」的類型?對於數,若是其取值範圍有限,能夠選擇較小的類型,對取值範圍幾乎無限的數,應該選擇較大的類型。對於串,可使它們短也可使它們長,但若是但願存儲的值只含不到10個字符,就不該該選用CHAR(255)。 函數

      ■ 性能與效率 問題是什麼?有些類型比另一些類型的處理效率高。數值運算通常比串的運算快。短串比長串運行更快,並且磁盤消耗更小。定長類型比可變長類型的性能更好。

  ■ 但願對值進行什麼樣的比較?對於串,其比較能夠是區分大小寫的,也能夠不區分大小寫。其選擇也會影響排序,由於它是基於比較的。 性能

  ■ 計劃對列進行索引嗎?若是計劃對列進行索引,那麼將會影響您對列類型的選擇,由於有的MySQL版本不容許對某些類型進行索引,例如不能對BLOB 和TEXT類型進行索引。並且有的MySQL版本要求定義索引列爲NOT NULL 的,這使您不能使用NULL 值。 測試

  如今讓咱們來更詳細地考慮這些問題。這裏要指出的是:在建立表時,但願做出儘量好的列類型選擇,但若是所做的選擇其實際並非最佳的,這也不會帶來多大的問題。可用ALTER TABLE 將原來選擇的類型轉換爲更好的類型。在發現數據所含的值比原設想的大時,可像將SMALLINT 更換成MEDIUMINT那樣簡單地對類型進行更換。有時這種更換也可能很複雜,例如將CHAR 類型更換成具備特定值集的ENUM 類型。在MySQL3.23 及之後的版本中,可以使用PROCEDURE ANALYSE( ) 來得到表列的信息,諸如最小值和最大值以及推薦的覆蓋列中值的取值範圍的最佳類型。這有助於肯定使用更小的類型,從而改進涉及該表的查詢的性能,並減小存儲該表所需的空間量。 編碼

  2.3.1列中存儲何種類型的值 spa

  在決定列的類型時,首先應該考慮該列的值類型,由於這對於所選擇的類型來講具備最爲明顯的意義。一般,在數值列中存儲數,在串列中存儲串,在日期和時間列中存儲日期和時間。若是數值有小數部分,那麼應該用浮點列類型而不是整數類型,如此等等。有時也存在例外,不可一律而論。主要是爲了有意義地選擇類型,應該理解所用數據的特性。若是您打算存儲本身的數據,大概對如何存儲它們會有本身很好的想法。可是,若是其餘人請您爲他們建一個表,決定列類型有時會很困難。這不像處理本身的數據那麼容易。應該充分地提問,搞清表實際應該包含何種類型的值。 設計

  若是有人告訴您,某列須要記錄「降雨量」。那是一個數嗎?或者它「主要」是一個數值,即通常是但不老是編碼成一個數嗎?例如,在看電視新聞時,氣象預報通常包括降雨量。有時是一個數(如「0.25」英寸的雨量),可是有時是「微量(trace)」降雨,意思是「雨根本就不大」。這對氣象預報很合適,但在數據庫中怎樣存儲?有可能須要將「微量」量化爲一個數,以便能用數值列類型來記錄降雨量,或許須要使用串,以即可以記錄「微量」這個詞。或者能夠提出某種更爲複雜的安排,使用一個數值列和一個串列,若是填充一個列就讓另外一個列爲NULL。很明顯,可能的話,應該避免最後這種選擇;最後這種選擇使表難於理解,使查詢更爲困難。咱們通常儘可能以數值形式存儲全部的行,並且只爲了顯示的須要纔對它們進行轉換。例如,若是小於0.01英寸的非零降雨量被視爲微量,那麼能夠以下選擇列值:

  Mysql入門系列:選擇MYSQL列的類型

  對於金錢的計算,須要處理元和分部分。這彷佛像浮點值,但FLOAT和DOUBLE 容易出現舍入錯誤,除了只須要大體精確的記錄外,這些類型可能不適合。由於人們對本身的錢都是很敏感的,最好是用一種能提供完善的精確性的類型,例如:

  ■ 將錢表示爲DECIMAL(M, 2) 類型,選擇M爲適合於所需取值範圍的最大寬度。這給出具備兩位小數精度的浮點值。DECIMAL的優勢是將值表示爲一個串,並且不容易出現舍入錯誤。不利之處是串運算比內部存儲爲數的值上的運算效率差。

  ■ 可在內部用整數類型來表示全部的錢值。其優勢是內部用整數來計算,這樣會很是快。不利之處是在輸入或輸出時須要利用乘或除100對值進行轉換。有些數據顯然是數值的,但必須決定是使用浮點類型仍是使用整數類型。應該搞清楚所用的單位是什麼以及須要什麼樣的精度。整個單元的精度都夠嗎?或者須要表示小數的單元嗎?這將有助於您在整數列和浮點數列之間進行區分。例如,若是您正表示權重,那麼若是記錄的值爲英磅,可使用一個整形列。若是但願記錄小數部分,就應該使用浮點列。在有的狀況下,甚至會使用多個字段,例如:若是但願根據磅和盎司記錄權重,則可使用多個列。

  高度(height)是另一種數值類型,有以下幾種表示方法:

  ■ 諸如「6 英尺2 英寸」可表示爲「 6-2」這樣一個串。這種形式具備容易察看和理解的優勢(固然比「74
英寸更好理解」),可是這種值很難用於數學運算,如求和或取平均值。

  ■ 一個數值字段表示英尺,另外一個數值字段表示英寸。這樣的表示進行數值運算相對容易,但兩個字段比一個字段難於使用。

  ■ 只用一個表示英寸的數值段。這是數據庫最容易處理的方式,可是這種方式意義最不明確。不過要記住,不必定要用與您慣常使用的那種格式來表示值。能夠用MySQL的函數將值轉換爲看上去意義明顯的值。所以,最後這種表示方法多是表示高度的最好方法。

  若是須要存儲日期信息,須要包括時間嗎?即,它們永遠都須要包括時間嗎? MySQL不提供具備可選時間部分的日期類型: DATE 可不包含時間,而DATETIME 必須包含時間。若是時間確實是可選的,那麼可用一個DATE 列記錄日期,一個TIME 列記錄時間。容許TIME 列爲NULL 並解釋爲「無時間」:

  Mysql入門系列:選擇MYSQL列的類型

      在用基於日期信息的主-細目關係鏈接兩個表時,決定是否須要時間值特別重要。假如您正在進行一項研究,包括一些對進入您的 辦公室的人進行測試的題目。在一個標準的初步測試集以後,您可能會在同一天進行幾個額外的測試,測試的選擇視初步測試結果而定。您可能會利用一個主-細目關係來表示這些信息,其中題目的標識信息和標準的初步測試存儲在一個主記錄中,而其餘測試保存爲輔助細目表的行。而後基於題目ID 與進行測試的日期將這兩個錶鏈接到一塊兒。

  在這種狀況下必須回答的問題是,是否能夠只用日期,或者是否須要既使用日期又使用時間。這個問題依賴於一個題目是否能夠在同一天投入測試過程不止一次。若是是這樣,那麼應該記錄時間(比方說,記錄測試過程開始的時間),或者用DATETIME 列,或者分別用DATE 和TIME 列(二者都必須填寫)。若是一個題目一天測試了兩次,沒有時間值就不能將該題目的細目記錄與適當的主記錄進行關聯。

  我曾經聽過有人聲稱「我不須要時間;我從不在同一天把一道題測試兩次」。有時他們是對的,可是我也看到過這些人後來在錄入同一天測試屢次的題目的數據後,反過來考慮怎樣防止細目記錄與錯誤的主記錄相混。很抱歉,這時已經太遲了!有時能夠在表中增長TIME 列來處理這個問題,不幸的是,除非有某些獨立的數據源,如原書面記錄,不然很難整理現有記錄。此外,沒辦法消除細目記錄的歧義,以便將它們關聯到合適的主記錄上。即便有獨立的信息源,這樣作也是很是亂的,極可能使已經編寫來利用表的應用程序出問題。最好是向表的擁有者說明問題並保證在建立他們的表以前進行很好的描述。

  有時具備一些不完整的數據,這會干擾列類型的選擇。若是進行家譜研究,須要記錄出生日期和死亡日期,有時會發現所能蒐集到的數據中只是某人出生或死亡的年份,但沒有確切的日期。若是使用DATE 列,除非有完整的日期值,不然不能輸入日期。若是但願可以記錄所具備的任何信息,即便不完整也保存,那麼可能必須保存獨立的年、月、日字段。這樣就能夠輸入所具備的日期成員並將沒有的部分設爲NULL。在MySQL3.23 及之後的版本中,還容許DATE 的日爲0 或者月和日部分爲0。這樣「模糊」的日期可用來表示不完整的日期值。

  2.3.2 列值有特定的取值範圍嗎

  若是已經決定從通用類別上選擇一種列類型,那麼考慮想要表示的值的取值範圍會有助於將您的選擇縮減到該類別中特定的類型上。假如但願存儲整數值。這些整數值的取值範圍爲0 到1000,那麼可使用從SMALLINT 到BIGINT 的全部類型。若是這些整數值的取值範圍最多爲2 000 000,則不能使用SMALLINT,其選擇範圍從MEDIUMINT 到BIGINT。須要從這個可能的選擇範圍中選取一種類型。固然,能夠簡單地爲想要存儲的值選擇最大的類型(如上述例子中選擇BIGINT)。可是,通常應該爲所要存儲的值選擇足以存儲它的最小的類型。這樣作,能夠最小化表佔用的存儲量,獲得最好的性能,由於一般較小列的處理比較大列的快。若是不知道所要表示的值的取值範圍,那麼必須進行猜想或使用BIGINT以應付最壞的狀況。(請注意,若是進行猜想時使用了一個過小的類型,工做不會白作;之後能夠利用ALTER TABLE 來將此列改成更大一些的類型。)

  在第1章中,咱們爲學分保存方案建立了一個score 表,它有一個記錄測驗和測試學分的score 列。爲了討論簡單起見,建立該表時使用了INT類型,但如今能夠看出,若是學分在0到100 的取值範圍內,更好的選擇應該是TINYINT UNSIGNED,由於所用的存儲空間較小。數據的取值範圍還影響列類型的屬性。若是該數據從不爲負,可以使用UNSIGNED 屬性;不然就不能用它。

  串類型沒有數值列那樣的「取值範圍」,但它們有長度,須要知道該串可以使用的列最大長度。若是串短於2 56個字符,可以使用CHAR、VARCHAR、TINYTEXT 或TINYBLOB 等類型。若是想要更長的串,可以使用TEXT 或BLOB 類型,而CHAR 和VARCHAR 再也不是選項。對於用來表示某個固定值集合的串列,能夠考慮使用ENUM 或SET 列類型。它們多是很好的選項,由於它們在內部是用數來表示的。這兩個類型上的運算是數值化的,所以,比其餘的串類型效率更高。它們還比其餘串類型緊湊、節省空間。在描述必須處理的值的範圍時,最好的術語是「老是」和「決不」(如「老是小於1000」或「決不爲負」),由於它們能更準確地約束列類型的選擇。但在未確證以前,要慎用這兩個術語。特別是與其餘人談他們的數據,而他們開始亂用這兩個術語時要注意。在有人說「老是」或「決不」時,必定要搞清他們說的確實是這個含義。有時人們說本身的數據老是有某種特定的性質,而其真正的含義是「幾乎老是」。

  例如,假如您爲某些人設計一個表,而他們告訴您,「咱們的測試學分老是0 到100」。根據這個描述,您選擇了TINYINT 類型並使它爲UNSIGNED的,由於值老是非負的。然而,您發現編碼錄入數據庫的人有時用- 1來表示「學生因病缺席」。呀,他們沒告訴您這事。可能能夠用NULL 來表示-1,但若是不能,必須記錄-
1,這樣就不能用UNSIGNED 列了(只好用ALTER TABLE 來補救!)。有時關於這些情形的討論可經過提一些簡單的問題來簡化,如問:曾經有過例外嗎?若是曾經有過例外狀況,即便是隻有一次,也必須考慮。您會發現,和您討論數據庫設計的人老是認爲,若是例外不常常發生,那麼就沒什麼關係。然而在建立數據庫時,就不能這樣想了。須要提的問題並非例外出現有多頻繁,而是有沒有例外?若是有,必須考慮進去。

      2.3.3 性能與效率問題

  列類型的選擇會在幾個方面影響查詢性能。若是記住下幾節討論的通常準則,將可以選出有助於MySQL有效處理表的列類型。

  1. 數值與串的運算

  數值運算通常比串運算更快。例如比較運算,可在單一運算中對數進行比較。而串運算涉及幾個逐字節的比較,若是串更長的話,這種比較還要多。若是串列的值數目有限,應該利用ENUM或SET類型來得到數值運算的優越性。這兩種類型在內部是用數表示的,可更爲有效地進行處理。例如替換串的表示。有時可用數來表示串值以改進其性能。例如,爲了用點分四位數(dotted-quad)表示法來表示IP 號,如192.168.0.4,可使用串。可是也能夠經過用四字節的UNSIGNED類型的每一個字節存儲四位數的每一個部分,將IP 號轉換爲整數形式。這便可以節省空間又可加快查找速度。但另外一方面,將IP 號表示爲INT值會使諸如查找某個子網的號碼這樣的模式匹配難於完成。所以,不能只考慮空間問題;必須根據利用這些值作什麼來決定哪一種表示更適合。

  2. 更小的類型與更大的類型

  更小的類型比更大的類型處理要快得多。首先,它們佔用的空間較小,且涉及的磁盤活動開銷也少。對於串,其處理時間與串長度直接相關。通常狀況下,較小的表處理更快,由於查詢處理須要的磁盤I/O少。對於定長類型的列,應該選擇最小的類型,只要能存儲所需範圍的值便可。例如,若是MEDIUMINT 夠用,就不要選擇BIGINT。若是隻須要FLOAT精度,就不該該選擇DOUBLE。對於可變長類型,也仍然可以節省空間。一個BLOB 類型的值用2字節記錄值的長度,而一個LONGBLOB 則用4 字節記錄其值的長度。若是存儲的值長度永遠不會超過64KB,使用BLOB 將使每一個值節省2字節(固然,對於TEXT 類型也能夠作相似的考慮)。

  3. 定長與可變長類型

  定長類型通常比可變長類型處理得更快:

  ■ 對於可變長列,因爲記錄大小不一樣,在其上進行許多刪除和更改將會使表中的碎片更多。須要按期運行OPTIMIZE TABLE以保持性能。而定長列就沒有這個問題。

  ■ 在出現表崩潰時,定長列的表易於從新構造,由於每一個記錄的開始位置是肯定的。可變長列就沒有這種便利。這不是一個與查詢處理有關的性能問題,但它一定能加快表的修復過程。若是表中有可變長的列,將它們轉換爲定長列可以改進性能,由於定長記錄易於處理。在試圖這樣作以前,應該考慮下列問題:

  ■ 使用定長列涉及某種折衷。它們更快,但佔用的空間更多。CHAR(n) 類型列的每一個值總要佔用n個字節(即便空串也是如此),由於在表中存儲時,值的長度不夠將在右邊補空格。而VARCHAR(N)類型的列所佔空間較少,由於只給它們分配存儲每一個值所須要的空間,每一個值再加一個字節用於記錄其長度。所以,若是在CHAR和VARCHAR列之間進行選擇,須要對時間與空間做出折衷。若是速度是主要關心的因素,則利用CHAR
列來取得定長列的性能優點。若是空間是關鍵,應該使用VARCHAR 列。

       ■ 不能只轉換一個可變長列;必須對它們所有進行轉換。並且必須使用一個ALTE RTABLE 語句同時所有轉換,不然轉換將不起做用。

  ■ 有時不能使用定長類型,即便想這樣作也不行。例如對於比255 字符長的串,沒有定長類型。

  4. 可索引類型

  索引能加快查詢速度,所以,應該選擇可索引的類型。

  5. NULL 與NOT NULL 類型

  若是定義一列爲NOT NULL,其處理更快,由於MySQL在查詢處理中沒必要檢查該列的值弄清它是否爲NULL,表中每行還能節省一位。避免列中有NULL可使查詢更簡單,由於不須要將NULL做爲一種特殊情形來考慮。一般,查詢越簡單,處理就越快。所給出的性能準則有時是互相矛盾的。例如,根據MySQL能對行定位這一方面來講,包含CHAR列的定長行比包含VARCHAR 列的可變長行處理快。但另外一方面,它也將佔用更多的空間,所以,會致使更多的磁盤活動。從這個觀點來看, VARCHAR可能會更快。做爲一個經驗規則,可假定定長列能改善性能,即便它佔用更多的空間也如此。對於某個特殊的關鍵應用,可能會但願以定長和可變長兩種方式實現一個表,並進行某些測試以決定哪一種方式對您的特定應用來講更快。

  2.3.4 但願對值進行什麼樣的比較

  根據定義串的方式,可使串類型以區分大小寫或不區分大小寫的方式進行比較和排序。表2-14示出不區分大小寫的每一個類型及其等價的區分大小寫類型。根據列定義中給不給出關鍵字BINARY,有的類型(CHAR、VARCHAR)是二進制編碼或非二進制編碼的。其餘類型(BLOB、TEXT)的「二進制化」隱含在類型名中。

  Mysql入門系列:選擇MYSQL列的類型

  請注意,二進制(區分大小寫)類型僅在比較和排序行爲上不一樣於相應的非二進制(不區分大小寫)類型。任意串類型均可以包含任意種類的數據。特別是, TEXT類型儘管在列類型名中稱爲「TEXT(文本)」,但它能夠很好地存儲二進制數據。若是但願使用一個在比較時既區分大小寫,又可不區分大小寫的列。可在但願進行區分大小寫的比較時,利用BINARY關鍵字強制串做爲二進制串值。例如,若是my_col 爲一個CHAR 列,可按不一樣的方式對其進行比較:

  my_col = 「A B C」 不區分大小寫

  BINARY my_col =「A B C」 區分大小寫

  my_col = BINARY「A B C」 區分大小寫

  若是有一個但願以非字典順序存儲的串值,可考慮使用ENUM 列。ENUM值的排序是根據列定義中所列出枚舉值的順序進行的,所以可使這些值以任意想要的次序排序。

      2.3.5 計劃對列進行索引嗎

  使用索引可更有效地處理查詢。索引的選擇是第4 章中的一個主題,但通常原則是將WHERE子句中用來選擇行的列用於索引。若是您要對某列進行索引或將該列包含在多列索引中,則在類型的選擇上可能會有限定。在早於3.23.2 版的MySQL發行版中,索引列必須定義爲NOT NULL,而且不能對BLOB或TEXT類型進行索引。這些限制在MySQL3.23.2版中都撤消了,但若是您正使用一個更早的版本,不能或不肯升級,那麼必須聽從這些約束。不過在下列情形中能夠繞過它們:

  ■ 若是能夠指定某個值做爲專用的值,那麼可以將其做爲與NULL 相同的東西對待。對於DATE 列,能夠指定「0000-00-00」表示「無日期」。在串列中,能夠指定空串表明「缺值」。在數值列中,若是該列通常只存儲非負值,則可以使用- 1。

  ■ 不能對BLOB或TEXT類型進行索引,但若是串不超過255它符,可以使用等價的VARCHAR 列類型並對其進行索引。可VARCHAR(255) BINARY 用於BLOB 值,將VARCHAR(255) 用於TEXT 值。

  2.3.6 列類型選擇問題的相互關聯程度

  不要覺得列類型的選擇是相互獨立的。例如,數值的取值範圍與存儲大小有關;在增大取值的範圍時,須要更多的存儲空間,這會影響性能。另外,考慮選擇使用AUTO_INCREMENT 來建立一個存放惟一序列號的列有何含義。這個選擇有幾個結果,它們涉及列的類型、索引和NULL 的使用,現列出以下:

  ■ AUTO_INCREMENT 是一個應該只用於整數類型的列屬性。它將您的選擇限定在TINYINT 到BIGINT 之上。

  ■ AUTO_INCREMENT 列應該進行索引,從而當前最大的序列號能夠很快就肯定,不用對錶進行所有掃描。此外,爲了防止序列號被重用,索引號必須是惟一的。這表示必須將列定義爲PRIMARY KEY 或定義爲UNIQUE 索引。

  ■ 若是所用的MySQL版本早於3 . 2 3 . 2,則索引列不能包含NULL 值,所以,必須定義列爲NOT NULL。全部這一切表示,不能像以下這樣只定義一個AUTO_INCREMENT 列:

  Mysql入門系列:選擇MYSQL列的類型

  使用AUTO_INCREMENT 獲得的另外一個結果是,因爲它是用來生成一個正值序列的,所以,最好將AUTO_INCREMENT 列定義爲UNSIGNED:

  Mysql入門系列:選擇MYSQL列的類型

相關文章
相關標籤/搜索