很多時候, int char bit set enum這幾種類型都可以替換着用,下面對這幾種類型的優劣稍作淺析
以在數據庫中建立用戶表的性別(gender)字段為例,分別用以下幾種類型各有什么優缺點
1. int: gender tinyint unsigned not null
可以這樣定義gender列,好處是儲存空間小( 1 Byte ),可擴展性好(range 0~255)
壞處是沒有強約束(可以在代碼里控制),可讀性不好,無從知道 0 1 2 到底什么意思
2. char(1): gender char(1) not null
優劣與int類似
3. bit(1): gender bit(1) not null
實際上bit在存儲空間上沒有什么優勢,MyISAM會把bit做一個round,例如bit(17)會變成3 Bytes存儲。而InnoDB和Memory則是會選擇最小的能容下這個列的int型來存儲他。
4. set: 此處沒用
5. enum: gender enum("MALE", "FEMALE") not null
enum好處很明顯,就是存儲空間小而且有對應的字符串映射關系,他的數據存儲的是2 Bytes大小的整數index,真正的字符串值存放在.frm文件里,每次查詢和插入的時候做轉換。
他的壞處就是擴展性不好,每次如果要擴充enum的值都要用到 ALTER TABLE
就gender這個問題而言,我認為最好的方法其實是建立代碼表,將gender列獨立成一個表,如下
create table gender (
id int unsigned not null primary key,
text varchar(8) not null
)
然后可以在user表里引用gender的id作為外鍵
將對gender表的查詢做一個cache,例如
select * from gender;
select * from gender where id = ?;
...
等等,這樣既能夠很方便的查詢出gender的信息(例如在生成頁面的gender select option選項時)而不影響效率,擴展添加表項只需要insert或者update gender表即可,這相對於ALTER TABLE來說是好很多的,而且完全不會影響到程序代碼(例如生成頁面里的option的代碼),達到代碼和數據庫解耦,只要在更新表項后flush一次cache就好。一般來講ORM框架還會有延遲加載之類的,運用這些技術可以進一步提高效率。
如果說不考慮這些項目需要,我覺得最好還是用enum吧,但是如果在項目里需要去獲取enum的值,程序代碼確實是比較麻煩的一件事情,此處可以考慮在項目啟動的時候將enum的值加載到項目里的靜態變量去,每次更新值重啟一下項目,但是這些動態生成表項的代碼需要自己去編寫,而且擴展enum較麻煩(ALTER TABLE)。
第二個例子是user表里的privilege列,這時候有如下兩種選擇:
1. set: privilege set("read", "write", "exe") not null
這種用法和enum類似,不同點在於set可以同時存在多個值,例如 read, write 或者 read, exe等等組合
缺點也和enum一樣,擴展的時候需要用到ALTER TABLE,
2. int: privilege tinyint unsigned not null
這樣的方法和linux里的權限控制是一樣的,你可以將權限定義成二進制數,例如
read = 1 << 0;
write = 1 << 1;
exe = 1 << 2;
在判斷權限的時候就很簡單了,例如要有一個權限 p1 = 3,要知道他有哪些權限,只需要做 & 運算即可
if (p1 & read != 0) { ... }
即可知道p1有read權限。
結合在項目里來說,同樣可以像gender那樣,為privilege建表:
create table privilege (
id tinyint unsigned not null primary key,
text varchar(6) not null
)
使用起來和gender是一樣的。