MySQL 中的字符串類型

原文: MySQL 中的字符串類型

字符類型包括:html

CHAR 與 VARCHAR

CHAR(m) m 取值範圍 0~255。列寬固定,存儲時字符串右邊會補空格,取出時自動去掉空格,除非開啓了 PAD_CHAR_TO_FULL_LENGTHmysql

對於值未超出的狀況,VARCHAR 在存儲及查詢時皆會完整保留末尾的空格字符。sql

VARCHAR(m) m 取值範圍 0~65535,不定長度,根據存儲的值而定,但上限爲 m。存儲時,會在值的前面預留一到兩位用來存儲字符串長度。0~255時預留一位,超過的狀況使用兩位來標識所存儲的字符串的長度shell

在非嚴格模式下,超過的非空格值會自動截斷來存儲,同時生成警告信息。建議開啓嚴格模式以免這種不完整數據的產生。性能

對於超出的空格值,VARCHAR 會在插入錢截斷併產生警告信息,而對於 CHAR 則沒有警告信息,直接靜默截斷存儲。ui

如下表格以 m爲4的狀況展現了 非嚴格模式下 CHAR,VARCHAR 在存儲不一樣長度字符串時的表現。spa

Value CHAR(4) Storage Required VARCHAR(4) Storage Required
'' '    ' 4 bytes '' 1 byte
'ab' 'ab  ' 4 bytes 'ab' 3 bytes
'abcd' 'abcd' 4 bytes 'abcd' 5 bytes
'abcdefgh' 'abcd' 4 bytes 'abcd' 5 byte

如下代碼展現了二者在查詢時對於末尾空格的處理狀況。code

mysql> CREATE TABLE vc (v VARCHAR(4), c CHAR(4));
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO vc VALUES ('ab ', 'ab ');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT CONCAT('(', v, ')'), CONCAT('(', c, ')') FROM vc;
+---------------------+---------------------+
| CONCAT('(', v, ')') | CONCAT('(', c, ')') |
+---------------------+---------------------+
| (ab  )              | (ab)                |
+---------------------+---------------------+
1 row in set (0.06 sec)

在進行排序和字符串比較時,具體表現和使用的字符集(character set collation )有關。server

Pad 屬性

Pad 即字符串長度不夠時的補齊操做,通常是添加空格。大部分字符集都有一個 PAD SPACE 的補齊屬性(pad attribute)。基於 UCA 9.0.0 的 Unicode 字符集其補齊屬性爲 NO PADhtm

可經過查詢 INFORMATION_SCHEMA COLLATIONS 表中的 PAD_ATTRIBUTE 列獲取到和字符集的補齊屬性。

補齊屬性直接影響非二進制字符串(CHAR, VARCHAR, and TEXT)進行比較時的末尾的空白會如何處理。

對於 NO PAD 字符集,末尾的空白會參與字符串比較,PAD SPACE 末尾的空白則不影響比較結果,但在使用 LIKE 關鍵字時空白是會參與進來的。

mysql> CREATE TABLE names (myname CHAR(10));
Query OK, 0 rows affected (0.03 sec)

mysql> INSERT INTO names VALUES ('Monty');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT myname = 'Monty', myname = 'Monty ' FROM names;
+------------------+--------------------+
| myname = 'Monty' | myname = 'Monty ' |
+------------------+--------------------+
|                1 |                  1 |
+------------------+--------------------+
1 row in set (0.00 sec)

mysql> SELECT myname LIKE 'Monty', myname LIKE 'Monty ' FROM names;
+---------------------+-----------------------+
| myname LIKE 'Monty' | myname LIKE 'Monty ' |
+---------------------+-----------------------+
|                   1 |                     0 |
+---------------------+-----------------------+
1 row in set (0.00 sec)

對於要求列值要求在記錄中惟一(unique values)的表格,在插入時須要格外注意,若是插入的值只包含空格的區別,在空格不影響比較結果的字符集中會報 duplicate-key error。好比已經有記錄的值爲 a 嘗試插入 a 時會報錯。

BINARY 與 VARBINARY

基本與 CHAR 和 VARCHAR 相似,不一樣點在於存儲的是二進制字符值。存儲長度的取值範圍也同樣,只是單位是字節而不是字符。

寫入時對於超出限制的值,會自動截斷,除非開啓 MySQL 的嚴格模式,此時會報錯而非截斷後成功寫入。

對於 BINARY 類型,存入的值長度不夠時會自動補上二進制中的零 0x00。查詢時也會返回補零後的值。在進行比較操做時,全部字節都參與計算,注意二進制的零值 0x00 與空格是不一樣的值,0x00 < 空格。

如下代碼展現了自動補零在存取時的表現,

mysql> CREATE TABLE t (c BINARY(3));
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO t SET c = 'a';
Query OK, 1 row affected (0.01 sec)

mysql> SELECT HEX(c), c = 'a', c = 'a\0\0' from t;
+--------+---------+-------------+
| HEX(c) | c = 'a' | c = 'a\0\0' |
+--------+---------+-------------+
| 610000 |       0 |           1 |
+--------+---------+-------------+
1 row in set (0.09 sec)

因此若是你指望查詢時獲得與存儲時一致的值,最好使用 VARBINARYBLOB 類型。

BLOB 與 TEXT 類型

BLOB (binary large object) 即大型二進制形式的對象,用於數據量大的狀況。BLOB 大類中具體包含:TINYBLOB, BLOB, MEDIUMBLOB 以及 LONGBLOB 四種數據類型,他們只存在存儲上限的區別。

文本類型 TEXT 下面一樣包含不一樣上限的具體類型:TINYTEXT, TEXT, MEDIUMTEXT 及 LONGTEXT 。這四種類型功能及上限與 BLOB 中的四種類型一一對應,只是該類型用於字符而非二進制值。

BLOB 類型內部處理時以二進制字符串的形式處理,在進行比較操做或排序時,按其表示的二進制數字來進行。而 TEXT 類型表示非二進制字符,在進行比較或排序時與字符集有關。

非嚴格模式下寫入一個超出長度的值時,會自動截斷後存入,同時生成警告信息,這一行爲可經過開啓嚴格模式來避免。

其中對於 TEXT 類型,截取末尾的空格字符時始終生成警告信息,與 MySQL 當前設置的模式無關。

通常來講你可把 BLOB 看做是 VARBLOB,存儲任意大的數據。TEXT 同理,可當作 VARCHAR。但仍是有一些區別的:

  • 創建索引時 BLOB 和 TEXT 須要指定長度(index prefix length),而 CHAR 和 VARCHAR 不須要。
  • BLOB 和 TEXT 類型沒有默認值,不能設置 DEFAULT 屬性。

由於 BLOB 和 TEXT 所存的值有可能很大,實際使用時會遇到些限制,好比

  • 排序時只取 max_sort_length 的長度。該值默認爲 1024,但可隨時配置:
mysql> SET max_sort_length = 2000;
mysql> SELECT id, comment FROM t
    -> ORDER BY comment;
  • 查詢 BLOB 和 TEXT 類型時其結果會處理到一個臨時表中,而不是像其餘類型同樣放到內存裏,由於內存中是不支持這兩種類型的(關於緣由詳見 Section 8.4.4, 「Internal Temporary Table Use in MySQL)。這裏臨時表的操做涉及到額外的文件 I/O,在查詢時會影響性能,因此應該儘可能避免這樣的操做。
  • 儘管 BLOB 和 TEXT 容許存儲的值很大,實際使用時,其上限受內存及傳輸 buffer 大小的影響。

ENUM 類型

枚舉值類型經過在建立表時指定該類型可接受的合法值列表,相比通常的字符類型有如下優點:

  • 節省存儲空間,枚舉值實際存儲時會轉成數字。
  • 語義化。相比 直接使用數字類型,枚舉類型在查詢時會返回其對應的字符串。

枚舉的建立與使用

枚舉中的項經過單引號指定,如下是一個建立和使用枚舉類型的示例:

CREATE TABLE shirts (
    name VARCHAR(40),
    size ENUM('x-small', 'small', 'medium', 'large', 'x-large')
);
INSERT INTO shirts (name, size) VALUES ('dress shirt','large'), ('t-shirt','medium'),
  ('polo shirt','small');
SELECT name, size FROM shirts WHERE size = 'medium';
+---------+--------+
| name    | size   |
+---------+--------+
| t-shirt | medium |
+---------+--------+
UPDATE shirts SET size = 'small' WHERE size = 'large';
COMMIT;

不支持在建立時使用表達式,好比下面這樣是不行的:

# 🚨
CREATE TABLE sizes (
    size ENUM('small', CONCAT('med','ium'), 'large')
);

# 🚨
SET @mysize = 'medium';

CREATE TABLE sizes (
    size ENUM('small', @mysize, 'large')
);

枚舉項的數字值

枚舉列表中每項都對應一個數字值,是一個從 1 開始的索引值。枚舉類型最多可包含 65,535 個候選項。

對於寫入的非法值會存儲成 0,因此可經過查詢爲 0 的列找出全部這些非法的記錄。

mysql> SELECT * FROM tbl_name WHERE enum_col=0;

NULL 的索引值是 NULL。

ENUM('Mercury', 'Venus', 'Earth') 爲例,如下表格展現了不一樣值其對應的索引值:

Value Index
NULL NULL
'' 0
'Mercury' 1
'Venus' 2
'Earth' 3

在須要數字的上下文中,枚舉類型返回時會自動處理成數字,譬如:

mysql> SELECT enum_col+0 FROM tbl_name;

插入時,數字類型的值會自動與枚舉中的索引對應,若是插入的數字帶引號,即嘗試以字符串形式存入,MySQL 會先從枚舉列表中找,若是找不到相應的匹配項,會將這個插入值當索引處理。因此儘可能不要設置數字類型的枚舉,這致使存取時一些不可預測的狀況發生。好比:

numbers ENUM('0','1','2')

當你存入數字 2 時,會解析對應到枚舉中索引爲 2 的值即 '1'。存入字符 '2' 時,與枚舉中最後一項匹配。嘗試存入 '3' 時,枚舉值中沒有相應的匹配,此時 MySQL 嘗試將 3 當成索引存入,對應的值是枚舉中的 '2'

mysql> INSERT INTO t (numbers) VALUES(2),('2'),('3');
mysql> SELECT * FROM t;
+---------+
| numbers |
+---------+
| 1       |
| 2       |
| 2       |
+---------+

查看枚舉值

經過 SHOW COLUMNS FROM tbl_name LIKE 'enum_col' 可在結果中的 Type 列查看定義好的枚舉其可選值有哪些。

mysql> SHOW COLUMNS FROM enum_test LIKE 'n';
+-------+-------------------+------+-----+---------+-------+
| Field | Type              | Null | Key | Default | Extra |
+-------+-------------------+------+-----+---------+-------+
| n     | enum('0','1','2') | YES  |     | NULL    |       |
+-------+-------------------+------+-----+---------+-------+
1 row in set (0.00 sec)

排序規則

排序時根據其在枚舉列表中的索引來定。好比 ENUM('b', 'a') 中 'b' 會排在 a 前面,而不是按常理那樣以爲 a 是在 b 前面的。爲了不這種不太直觀的狀況,在定義枚舉時最好按字母順序定義,也能夠在使用 ORDER BY 語句時經過指定排序類型來修正,即便用 ORDER BY CAST(col as CHAR)ORDER BY CONCAT(col)

空字符串會排在非空值前面,NULL 值在全部值前面。

SET 類型

SET 類型是一組字符串值,它的值相似於枚舉,必需是定義表時指定的候選值中所列出的值,不一樣點在於它可同時支持設置多個候選值,用逗號分隔(同時意味着候選值自身不能包含逗號)。

好比類型爲定義爲 SET('one', 'two') NOT NULL 的列,可有如下合法的值:

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

SET 最多可定義 64 個候選值。嚴格模式下,定義時若是指定了重複的候選值會拋錯。候選值末尾的空白會自動截取掉。

內部存儲時其實存的是一個字節表。每一個候選值都對應一個二進制值中的一位。好比定義以下的表:

CREATE TABLE set_test(
    rowid INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    myset SET('Travel','Sports','Dancing','Fine Dining')
  );

每一個值對應一個二進制位數值,因此每一個候選值也對應一個計算後的數字。

Element SET Value Decimal Value
Travel 00000001 1
Sports 00000010 2
Dancing 00000100 4
Fine Dining 00001000 8

這樣在設置時,若是設置的是數字,會解析成對應的二進制形式,好比 9=8+1,對應 TravelFine Dining。因此實際設置成了 Travel,Fine Dining

同時 9 的二進制表示是 00001001,經過上面表格對比可知相應位(二進制中第0位和第三位)對應的值也是 Travel/00000001Fine Dining/00001000

在設置時不關心候選值的順序及次數,屢次指定同一個候選值最終只會出現一次。

mysql> CREATE TABLE myset (col SET('a', 'b', 'c', 'd'));

mysql> INSERT INTO myset (col) VALUES 
-> ('a,d'), ('d,a'), ('a,d,a'), ('a,d,d'), ('d,a,d');
Query OK, 5 rows affected (0.01 sec)
Records: 5  Duplicates: 0  Warnings: 0


mysql> SELECT col FROM myset;
+------+
| col  |
+------+
| a,d  |
| a,d  |
| a,d  |
| a,d  |
| a,d  |
+------+
5 rows in set (0.04 sec)

經過 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%';

如下用法也是可行的:

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

相關資源

相關文章
相關標籤/搜索