MySQL的複合數據類型:ENUM和SET

MySQL的經常使用數據類型包括:Number/Date/String,而String類型中又包含了Char/Varchar/Binary/blob/text等長度不一樣的簡單數據類型,有時咱們須要對數據作更細緻的管理,好比枚舉和集合,就須要複合類型ENUMSET了。html

ENUM 枚舉類型

ENUM適合於只能在一組固定值中選一個的場景,好比性別只能爲男或者女。
ENUM的優點在於:mysql

  • 只能在固定值中選擇,能夠在數據庫層面限制非法值。
  • 數據的存儲用數字來存儲,佔用空間少。

可是它的使用有不少須要咱們注意的地方,一不當心你就會獲得錯誤的結果。sql

使用ENUM枚舉類型

mysql> create table test (name varchar(40), sex enum('male', 'female') );
mysql> insert into test (name, sex) values('a', 'male'), ('b', 'female'), ('c', 'male');
mysql> select * from test;
+------+--------+
| name | sex    |
+------+--------+
| a    | male   |
| b    | female |
| c    | male   |
+------+--------+
3 rows in set (0.00 sec)

建立枚舉類型時,咱們使用關鍵字enum,同時跟着一組可枚舉值列表,這些可枚舉值必須使用字符串的格式,不然會報錯。若是插入值的大小寫不匹配,會自動轉換成枚舉值。數據庫

ENUM類型數據存儲的實際值是索引值

咱們全部枚舉值都是按照枚舉值列表中的索引值進行存儲的,如上面的ENUM('male', 'female')sex字段全部值爲:函數

字面值 存儲值
NULL NULL
'' 0
'male' 1
'female' 2

所以若是有1000條記錄都存儲爲male,咱們可能認爲數據庫存儲了4000個字符,其實只存儲了10001字符。而在查詢的時候又會將這個編碼過的數字轉爲實際的值。測試

咱們能夠用兩個例子測試下:ui

mysql> select * from test where sex=1;
+------+------+
| name | sex  |
+------+------+
| a    | male |
| c    | male |
+------+------+
2 rows in set (0.00 sec)

mysql> select name, sex+0 from test;
+------+-------+
| name | sex+0 |
+------+-------+
| a    |     1 |
| b    |     2 |
| c    |     1 |
+------+-------+
3 rows in set (0.00 sec)

這種存儲和查詢的方式會致使一些處理數字的函數,也會使用存儲的值來進行計算,如SUM()AVG()編碼

mysql> select name, avg(sex) from test;
+------+--------------------+
| name | avg(sex)           |
+------+--------------------+
| a    | 1.3333333333333333 |
+------+--------------------+
1 row in set (0.00 sec)

讀寫時不要使用數字

因爲上面介紹的用索引值存儲的特性,咱們不要用枚舉類型來存儲數字格式的列,不然會引發很大的混淆,如:code

mysql> create table test2 (numbers enum('0', '1', '2'));
Query OK, 0 rows affected (0.04 sec)

# 此時2被當作索引值,所以是'1';'2'就是'2';'3'由於不是合法值,會用索引值嘗試,所以是'2'
mysql> insert into test2 (numbers) values (2), ('2'), ('3');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from test2;
+---------+
| numbers |
+---------+
| 1       |
| 2       |
| 2       |
+---------+
3 rows in set (0.00 sec)

枚舉類型的默認值

即使一列被設定爲枚舉類型,但依然有額外兩種值爲合法值:NULL''server

當咱們插入一個非法值時,在寬鬆模式下,會插入一個普通的空字符'',其值爲0。而在嚴格模式下會報錯。

當該字段設定爲容許爲空時,NULL字段能夠被正常插入。當不容許爲空時,若是你不填值,會使用默認值:枚舉值的第一個,如上面的male

除了設置爲嚴格模式,不然沒有合適的辦法讓一列數據必須插入合法枚舉值。使用默認值不少狀況下不能知足需求。

枚舉類型的排序

常規使用order by進行排序時,會按照字母的文本順序。但枚舉類型因爲存儲爲索引值,所以會按照索引值進行排序:NULL < '' = 0 < 1 < 2

若是但願按照文本類型進行排序,可使用:

order by cast(col as char)
或者
order by concat(col)

枚舉值聲明的限制

建立數據類型時,枚舉值不容許爲表達式,如:

mysql> create table test (name varchar(40), sex enum('male', concat('fem', 'ale') );
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'concat('fem', 'ale') )' at line 1

枚舉值數量的限制

枚舉值用1-2個字節來存儲,所以上限值爲2^16-1=65535

SET集合類型

SETENUM類型很是類似,它適合於只能在一組固定值中選零個或多個的場景,好比一我的喜歡的顏色能夠爲紅、黃、藍等顏色中的一個或多個,也能夠都不喜歡。

SET的優點和ENUM也類似,在於:

  • 只能在固定值中選擇,能夠在數據庫層面限制非法值。
  • 數據的存儲用數字來存儲,佔用空間少。但在枚舉值數量不少,而枚舉值字符數少時這一可能不成立。

使用SET枚舉類型

mysql> create table test2 (name varchar(40), color set('red', 'green', 'blue', 'yellow'));
Query OK, 0 rows affected (0.04 sec)

mysql> insert into test2(name,color) values ('a', 'red'), ('b', 'red,green'), ('c', 'green,blue,yellow');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from test2;
+------+-------------------+
| name | color             |
+------+-------------------+
| a    | red               |
| b    | red,green         |
| c    | green,blue,yellow |
+------+-------------------+
3 rows in set (0.00 sec)

建立時,咱們使用關鍵字set,同時跟着一組可枚舉值列表,這些可枚舉值必須使用字符串的格式,不然會報錯。

SET類型數據存儲的實際值是索引值的和

咱們全部枚舉值都是按照列表中的索引值進行存儲的,不一樣的是經過設置二進制數爲1的位置,即2的冪次方。如上面的SET('red', 'blue', 'green', 'yellow')color字段全部值爲:

枚舉值 二進制值 十進制數字
red 0001 1
green 0010 2
blue 0100 4
yellow 1000 8

而當有多個值時,經過全部值的求和獲得存儲的值。所以存儲的數據量變少了,當取出的時候編碼過的數字又會被轉義成實際的字符串。

咱們能夠用兩個例子測試下:

mysql> select name,color+0 from test2;
+------+---------+
| name | color+0 |
+------+---------+
| a    |       1 |
| b    |       3 |
| c    |      14 |
+------+---------+
3 rows in set (0.00 sec)

mysql> select name,color from test2 where color=14;
+------+-------------------+
| name | color             |
+------+-------------------+
| c    | green,blue,yellow |
+------+-------------------+
1 row in set (0.00 sec)

這種存儲和查詢的方式會致使一些處理數字的函數,也會使用存儲的值來進行計算,如SUM()AVG()

mysql> select avg(color) from test2;
+------------+
| avg(color) |
+------------+
|          6 |
+------------+
1 row in set (0.00 sec)

插入時的順序和次數

當插入值時,set類型不關注你插入的順序和一個枚舉值的插入次數,它會自動去重並進行求和獲得值,等到取出時,會按照聲明的順序返回:

mysql> insert into test2(name,color) values ('d', 'yellow,green,red,yellow');
Query OK, 1 row affected (0.00 sec)

mysql> select name,color from test2;
+------+-------------------+
| name | color             |
+------+-------------------+
| d    | red,green,yellow  |
+------+-------------------+
4 rows in set (0.00 sec)

查找集合值

因爲set類型的特殊性,所以有專用的查找函數:

mysql> select * from test2 where find_in_set('red', color);
+------+------------------+
| name | color            |
+------+------------------+
| a    | red              |
| b    | red,green        |
| d    | red,green,yellow |
+------+------------------+
3 rows in set (0.00 sec)

# 這一種方法當出現lightred顏色的時候就沒法正確工做了
mysql> select * from test2 where color like '%red%';
+------+------------------+
| name | color            |
+------+------------------+
| a    | red              |
| b    | red,green        |
| d    | red,green,yellow |
+------+------------------+
3 rows in set (0.00 sec)

集合值的計算方式是位運算

前面說是對枚舉值去重並自動求和只是爲了方便理解,其實是進行位運算,獲得最終的值,如0001 + 0100 = 0101

所以咱們也能夠用相似的方法來查找值:

mysql> select name,color from test2 where color & 10;
+------+-------------------+
| name | color             |
+------+-------------------+
| b    | red,green         |
| c    | green,blue,yellow |
| d    | red,green,yellow  |
+------+-------------------+
3 rows in set (0.00 sec)

mysql> select name,color from test2 where color & 12;
+------+-------------------+
| name | color             |
+------+-------------------+
| c    | green,blue,yellow |
| d    | red,green,yellow  |
+------+-------------------+
2 rows in set (0.00 sec)

上面的這個&符號什麼含義我沒查到,但我猜想這個&符號的含義就是位運算,當兩個數在一個位置都爲1時返回true,若是沒有一個位置二者都爲1則爲false

具體的能夠計算下:a,b,c,d分別爲0001,0011,1110,1011,此時101100121010,能夠計算的到上面的結果。其餘數字的結果也都符合,因此應該符合個人猜想。

枚舉類型的排序

常規使用order by進行排序時,會按照字母的文本順序。但枚舉類型因爲存儲爲索引值,所以會按照索引值進行排序:NULL < 0 < 1 < 2

若是但願按照文本類型進行排序,可使用:

order by cast(col as char)
或者
order by concat(col)

枚舉值數量的限制

枚舉值用1-8個字節來存儲,所以上限值爲8*8=64個。

參考資料

  1. 11.4.4 The ENUM Type:https://dev.mysql.com/doc/ref...
  2. 11.8 Data Type Storage Requirements:https://dev.mysql.com/doc/ref...
  3. 11.4.5 The SET Type:https://dev.mysql.com/doc/ref...
相關文章
相關標籤/搜索