1. 程式人生 > >MySQL 中的資料型別介紹

MySQL 中的資料型別介紹

據我統計,MySQL支援39種(按可使用的型別欄位統計,即同義詞也作多個)資料型別。下面的介紹可能在非常古老MySQL版本中不適用。

1、概述

    要了解一個資料庫,我們也必須瞭解其支援的資料型別。

    mysql支援所有標準的SQL資料型別,主要分3類:

  •     數值型別
  •     字串型別
  •     時間日期型別
    另一類是幾何資料型別,用的不多,也沒多介紹。    下面大、小標題後括號內的陣列表示其含有的型別個數。下面所有結論都經過本人使用MySql Workbench編寫SQL驗證過或來自官網。

2、數值型別(12)

    2.1、整數型別(6)

    一張圖就能解釋清楚了:

    01

    INTEGER同INT。

    2.2、定點數(2)

    DECIMAL和NUMERIC型別在MySQL中視為相同的型別。它們用於儲存必須為確切精度的值。

    使用方式如下:

  1. salary DECIMAL(5,2)

    下面的介紹將基於上面這個例子。

    我們看到其中有兩個引數,即DECIMAL(M,D),其中M表示十進位制數字總的個數,D表示小數點後面數字的位數,上例中的取值範圍為-999.99~999.99。

    如果儲存時,整數部分超出了範圍(如上面的例子中,新增數值為1000.01),MySql就會報錯,不允許存這樣的值。

    如果儲存時,小數點部分若超出範圍,就分以下情況:

  •     若四捨五入後,整數部分沒有超出範圍,則只警告,但能成功操作並四捨五入刪除多餘的小數位後儲存。如999.994實際被儲存為999.99。
  •     若四捨五入後,整數部分超出範圍,則MySql報錯,並拒絕處理。如999.995和-999.995都會報錯。

    M的預設取值為10,D預設取值為0。如果建立表時,某欄位定義為decimal型別不帶任何引數,等同於decimal(10,0)。帶一個引數時,D取預設值。

  •     M的取值範圍為1~65,取0時會被設為預設值,超出範圍會報錯。
  •     D的取值範圍為0~30,而且必須<=M,超出範圍會報錯。

    所以,很顯然,當M=65,D=0時,可以取得最大和最小值。

    已經解釋很詳細了,如還不清楚,請回復。

    2.3、浮點數(3)

    浮點數是用來表示實數的一種方法,它用 M(尾數) * B( 基數)的E(指數)次方來表示實數,相對於定點數來說,在長度一定的情況下,具有表示資料範圍大的特點。但同時也存在誤差問題。

    如果希望保證值比較準確,推薦使用定點數資料型別。

    MySql中的浮點型別有float,double和real。他們定義方式為:FLOAT(M,D) 、 REAL(M,D) 、 DOUBLE PRECISION(M,D)。

    REAL就是DOUBLE ,如果SQL伺服器模式包括REAL_AS_FLOAT選項,REAL是FLOAT的同義詞而不是DOUBLE的同義詞。

    “(M,D)”表示該值一共顯示M位整數,其中D位位於小數點後面。例如,定義為FLOAT(7,4)的一個列可以顯示為-999.9999。MySQL儲存值時進行四捨五入,因此如果在FLOAT(7,4)列內插入999.00009,近似結果是999.0001。

    FLOAT和DOUBLE中的M和D的取值預設都為0,即除了最大最小值,不限制位數。允許的值理論上是-1.7976931348623157E+308~-2.2250738585072014E-308、0和2.2250738585072014E-308~1.7976931348623157E+308。M、D範圍如下(MySql5.7實測,與IEEE標準計算的實際是不同的,下面介紹):

  •     M取值範圍為0~255。FLOAT只保證6位有效數字的準確性,所以FLOAT(M,D)中,M<=6時,數字通常是準確的。如果M和D都有明確定義,其超出範圍後的處理同decimal。
  •     D取值範圍為0~30,同時必須<=M。double只保證16位有效數字的準確性,所以DOUBLE(M,D)中,M<=16時,數字通常是準確的。如果M和D都有明確定義,其超出範圍後的處理同decimal。

    FLOAT和DOUBLE中,若M的定義分別超出7和17,則多出的有效數字部分,取值是不定的,通常數值上會發生錯誤。因為浮點數是不準確的,所以我們要避免使用“=”來判斷兩個數是否相等。

    MySql中的浮點數遵循IEEE 754標準。

    記憶體中,FLOAT佔4-byte(1位符號位 8位表示指數 23位表示尾數),DOUBLE佔8-byte(1位符號位 11位表示指數 52位表示尾數)。IEEE754標準還對尾數的格式做了規範:d.dddddd...,小數點左面只有1位且不能為零,計算機內部是二進位制,因此,尾數小數點左面部分總是1。顯然,這個1可以省去,以提高尾數的精度。由上可知,單精度浮點數的尾數是用24bit表示的,雙精度浮點數的尾數是用53bit表示的。所以就能算出取值範圍和準確的有效位數了,但MySql中其實略有不同。

    2.4、BIT(1)

    BIT資料型別可用來儲存位欄位值。BIT(M)型別允許儲存M位值。M範圍為1~64,預設為1。

    BIT其實就是存入二進位制的值,類似010110。

    如果存入一個BIT型別的值,位數少於M值,則左補0.

    如果存入一個BIT型別的值,位數多於M值,MySQL的操作取決於此時有效的SQL模式:

  •     如果模式未設定,MySQL將值裁剪到範圍的相應端點,並儲存裁減好的值。
  •     如果模式設定為traditional(“嚴格模式”),超出範圍的值將被拒絕並提示錯誤,並且根據SQL標準插入會失敗。

    下面是官方示例:

  1. mysql> CREATE TABLE t (b BIT(8));
  2. mysql> INSERT INTO t SET b = b'11111111';
  3. mysql> INSERT INTO t SET b = b'1010';
  4. mysql> INSERT INTO t SET b = b'0101';
  1. mysql> SELECT b+0, BIN(b+0), OCT(b+0), HEX(b+0) FROM t;
  2. +------+----------+----------+----------+
  3. | b+0| BIN(b+0)| OCT(b+0)| HEX(b+0)|
  4. +------+----------+----------+----------+
  5. |255|11111111|377| FF |
  6. |10|1010|12| A |
  7. |5|101|5|5|
  8. +------+----------+----------+----------+

3、字串型別(14)

    字串型別指CHAR、VARCHAR、BINARY、VARBINARY、BLOB、TEXT、ENUM和SET。

    3.1、CHAR和VARCHAR型別(2)

    CHAR和VARCHAR型別宣告的長度表示你想要儲存的最大字元數。例如,CHAR(30)可以佔用30個字元。預設長度都為255。

    CHAR列的長度固定為建立表時宣告的長度。長度可以為從0到255的任何值。當儲存CHAR值時,在它們的右邊填充空格以達到指定的長度。當檢索到CHAR值時,尾部的空格被刪除掉,所以,我們在儲存時字串右邊不能有空格,即使有,查詢出來後也會被刪除。在儲存或檢索過程中不進行大小寫轉換。

    所以當char型別的欄位為唯一值時,新增的值是否已經存在以不包含末尾空格(可能有多個空格)的值確定,比較時會在末尾補滿空格後與現已存在的值比較。

    VARCHAR列中的值為可變長字串。長度可以指定為0到65,535之間的值(實際可指定的最大長度與編碼和其他欄位有關,比如,本人MySql使用utf-8編碼格式,大小為標準格式大小的2倍,僅有一個varchar欄位時實測最大值僅21844,如果新增一個char(3),則最大取值減少3。整體最大長度是65,532位元組)。

    同CHAR對比,VARCHAR值儲存時只儲存需要的字元數,另加一個位元組來記錄長度(如果列宣告的長度超過255,則使用兩個位元組)。

    VARCHAR值儲存時不進行填充。當值儲存和檢索時尾部的空格仍保留,符合標準SQL。

    如果分配給CHAR或VARCHAR列的值超過列的最大長度,則對值進行裁剪以使其適合。如果被裁掉的字元不是空格,則會產生一條警告。如果裁剪非空格字元,則會造成錯誤(而不是警告)並通過使用嚴格SQL模式禁用值的插入。

    下面顯示了將各種字串值儲存到CHAR(4)和VARCHAR(4)列後的結果:

    02

    表中最後一行的值只適用在不使用嚴格模式時;如果MySQL執行使用嚴格模式,超過列長度的值不儲存,並且會出現錯誤。

    因為空格的原因,相同的值存入到長度都足夠的varvhar和char中,取出可能會不同,比如"a"和"a  "。

    3.1、BINARY和VARBINARY型別(2)

    BINARY和VARBINARY型別類似於CHAR和VARCHAR型別,但是不同的是,它們儲存的不是字元字串,而是二進位制串。所以它們沒有字符集,並且排序和比較基於列值位元組的數值值。

    當儲存BINARY值時,在它們右邊填充0x00(零位元組)值以達到指定長度。取值時不刪除尾部的位元組。比較時所有位元組很重要(因為空格和0x00是不同的,0x00<空格),包括ORDER BY和DISTINCT操作。比如插入'a '會變成'a \0'。

    對於VARBINARY,插入時不填充字元,選擇時不裁剪位元組。比較時所有位元組很重要。

    當型別為BINARY的欄位為主鍵時,應考慮上面介紹的儲存方式。

    3.2、BLOB和TEXT型別(8)

    BLOB是一個二進位制大物件,可以容納可變數量的資料。有4種BLOB型別:TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB。它們只是可容納值的最大長度不同。

    有4種TEXT型別:TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT。這些對應4種BLOB型別,有相同的最大長度和儲存需求。

    BLOB列被視為二進位制字串。TEXT列被視為字元字串,類似CHAR和BINARY。

    在TEXT或BLOB列的儲存或檢索過程中,不存在大小寫轉換。

    未執行在嚴格模式時,如果你為BLOB或TEXT列分配一個超過該列型別的最大長度的值,值被擷取以保證適合。如果截掉的字元不是空格,將會產生一條警告。使用嚴格SQL模式,會產生錯誤,並且值將被拒絕而不是擷取並給出警告。

    在大多數方面,可以將BLOB列視為能夠足夠大的VARBINARY列。同樣,可以將TEXT列視為VARCHAR列。

    BLOB和TEXT在以下幾個方面不同於VARBINARY和VARCHAR:

  •     當儲存或檢索BLOB和TEXT列的值時不刪除尾部空格。(這與VARBINARY和VARCHAR列相同)。 
  •     比較時將用空格對TEXT進行擴充以適合比較的物件,正如CHAR和VARCHAR。
  •     對於BLOB和TEXT列的索引,必須指定索引字首的長度。對於CHAR和VARCHAR,字首長度是可選的。
  •     BLOB和TEXT列不能有預設值。

    MySQL Connector/ODBC將BLOB值定義為LONGVARBINARY,將TEXT值定義為LONGVARCHAR。

    BLOB或TEXT物件的最大大小由其型別確定,但在客戶端和伺服器之間實際可以傳遞的最大值由可用記憶體數量和通訊快取區大小確定。你可以通過更改max_allowed_packet變數的值更改訊息快取區的大小,但必須同時修改伺服器和客戶端程式。

    每個BLOB或TEXT值分別由內部分配的物件表示。

    它們(TEXT和BLOB同)的長度:

  •     Tiny:最大長度255個字元(2^8-1)
  •     BLOB或TEXT:最大長度65535個字元(2^16-1)
  •     Medium:最大長度16777215個字元(2^24-1)
  •     LongText 最大長度4294967295個字元(2^32-1)

    實際長度與編碼有關,比如utf-8的會減半。

    3.3、ENUM(1)

    MySql中的ENUM是一個字串物件,其值來自表建立時在列規定中顯式列舉的一列值。

    可以插入空字串""和NULL:

  •     如果你將一個非法值插入ENUM(也就是說,允許的值列之外的字串),將插入空字串以作為特殊錯誤值。該字串與“普通”空字串不同,該字串有數值值0。
  •     如果將ENUM列宣告為允許NULL,NULL值則為該列的一個有效值,並且預設值為NULL。如果ENUM列被宣告為NOT NULL,其預設值為允許的值列的第1個元素。

    值的索引規則如下:

  •     來自列規定的允許的值列中的值從1開始編號。
  •     空字串錯誤值的索引值是0。所以,可以使用下面的SELECT語句來找出分配了非法ENUM值的行:mysql> SELECT * FROM tbl_name WHERE enum_col=0;
  •     NULL值的索引是NULL。

    如下例:

    03

    ENUM最多可以有65,535個元素。當建立表時,ENUM成員值的尾部空格將自動被刪除。

    使用方式:

  1. CREATE TABLE shirts (
  2. name VARCHAR(40),
  3. size ENUM('x-small','small','medium','large','x-large')
  4. );
  1. INSERT INTO shirts (name, size) VALUES ('dress shirt','large'),('t-shirt','medium'),('polo shirt','small');
  1. SELECT name, size FROM shirts WHERE size ='medium';
  1. UPDATE shirts SET size ='small' WHERE size ='large';

    如果將返回值設為數值,將返回索引值,比如講上面的查詢語句改為:

  1. SELECT name, size+0 FROM shirts WHERE size ='medium';

    如果將一個數字儲存到ENUM列,數字被視為索引,並且儲存的值是該索引對應的列舉成員(這不適合LOAD DATA,它將所有輸入視為字串)。不建議使用類似數字的列舉值來定義一個ENUM列,因為這很容易引起混淆。

    ENUM值根據索引編號進行排序)。例如,對於ENUM('a','b'),'a'排在'b'前面,但對於ENUM('b','a'),'b'排在'a'前面。空字串排在非空字串前面,並且NULL值排在所有其它列舉值前面。要想防止意想不到的結果,按字母順序規定ENUM列。還可以使用GROUP BY CAST(col AS CHAR)或GROUP BY CONCAT(col)來確保按照詞彙對列進行排序而不是用索引數字。

    3.4、SET型別(1)

    SET是一個字串物件,可以有零或多個值,其值來自表建立時規定的允許的一列值。指定包括多個SET成員的SET列值時各成員之間用逗號(‘,’)間隔開。例如,指定為SET('one', 'two') NOT NULL的列可以有下面的任何值:

  •     ''
  •     'one'
  •     'two'
  •     'one,two'

    SET最多可以設定64個值。建立表時,SET成員值的尾部空格將自動被刪除。檢索時,儲存在SET列的值使用列定義中所使用的大小寫來顯示。

    MySQL用數字儲存SET值,所儲存值的低階位對應第1個SET成員。如果在數值上下文中檢索一個SET值,檢索的值的位設定對應組成列值的SET成員。

    例如,可以這樣從一個SET列檢索數值值:

  1. mysql> SELECT set_col+0 FROM tbl_name;

    如果將一個數字儲存到SET列中,數字的二進位制的1的位置確定了列值中的SET成員。對於指定為SET('a','b','c','d')的列,成員有下面的十進位制和二進位制值:

    04

    如果你為該列分配一個值9,其二進位制形式為1001,因此第1個和第4個SET值成員'a'和'd'被選擇,結果值為 'a,d'。

    對於包含多個SET元素的值,當插入值時元素所列的順序並不重要。在值中一個給定的元素列了多少次也不重要。當以後檢索該值時,值中的每個元素出現一次,根據表建立時指定的順序列出元素。例如,假定某個列指定為SET('a','b','c','d'):

  1. CREATE TABLE myset (col SET('a','b','c','d'));
  2. INSERT INTO myset (col) VALUES ('a,d'),('d,a'),('a,d,a'),('a,d,d'),('d,a,d');
  3. SELECT *,col+0 FROM myset;
  4. SELECT *,col+0 FROM myset where col='a,b';

    結果:

  1. a,d 9
  2. a,d 9
  3. a,d 9
  4. a,d 9
  5. a,d 9

    SET值按數字順序排序。NULL值排在非NULL SET值的前面。

    通常情況,可以使用FIND_IN_SET()函式或LIKE操作符搜尋SET值:

    mysql> SELECT * FROM tbl_name WHERE FIND_IN_SET('value',set_col)>0;

    mysql> SELECT * FROM tbl_name WHERE set_col LIKE '%value%';

    第1個語句找出SET_col包含value set成員的行。第2個類似,但有所不同:它在其它地方找出set_col包含value的行,甚至是在另一個SET成員的子字串中。

    下面的語句也是合法的:

    mysql> SELECT * FROM tbl_name WHERE set_col & 1;

    mysql> SELECT * FROM tbl_name WHERE set_col = 'val1,val2';

    第1個語句尋找包含第1個set成員的值。第2個語句尋找一個確切匹配的值。應注意第2類的比較。將set值與'val1,val2'比較返回的結果與同'val2,val1'比較返回的結果不同。指定值時的順序應與在列定義中所列的順序相同。

    如果想要為SET列確定所有可能的值,使用SHOW COLUMNS FROM tbl_name LIKE set_col並解析輸出中第2列的SET定義。

    有什麼實際應用呢?

    比如我們設定使用者的許可權控制,一個使用者可能會有多種許可權,我們使用所有許可權建立一個SET型別的欄位,我們不需要用一系列int來定義各種許可權了,直接使用一個SET欄位即可:

  1. /*
  2. 使用者許可權permission表
  3. */
  4. create table user_permission(
  5. id int UNSIGNED notnull auto_increment,
  6. user_id intnotnull,
  7. permission set('閱讀','評論','發帖')notnull,
  8. primary key(id),
  9. unique (user_id)
  10. );
  11. desc user_permission;
  12. insert into user_permission values (0,1,