設計目的: 減少各種狀態值欄位; 減少資料庫冗餘和存儲空間; 增加狀態值時可靈活調整,無需增加額外欄位 ...
設計目的
- 減少各種狀態值欄位
- 減少資料庫冗餘和存儲空間
- 增加狀態值時可靈活調整,無需增加額外欄位
運用場景
例子1:管理用戶的支付方式
比如針對不同用戶組設置了不同的支付方式支持,假設支付方式有支付寶
、微信
、銀聯
、借條
等。A用戶支持支付寶、微信;B用戶支持支付寶、微信、借條。一般用戶支付方式資料庫設計為:
ID | name | alipay | weixin | union | iou |
---|---|---|---|---|---|
1 | A | 1 | 1 | 0 | 0 |
2 | B | 1 | 1 | 0 | 1 |
這時如果後續多了其它支付方式後,就需要調整表結構增加欄位,如快錢、貨到付款等。這種設計方式明顯不符合資料庫設計第一範式,增加了很多冗餘欄位和存儲空間。
例子2:設置用戶的操作許可權
比如有一組許可權列表,查看
,編輯
,發佈
,刪除
,資料庫可能會是這樣:
ID | name | is_visible | is_editble | is_publishable | is_deleteable |
---|---|---|---|---|---|
1 | A | 1 | 1 | 0 | 0 |
2 | B | 1 | 1 | 1 | 1 |
上面只是舉些例子來說明一個問題,當一張表的欄位里包含很多這些狀態值01時,我們可以使用二進位位
的方式來表示,而且只需要一個欄位就好了。
設計思路
比如例1中的支付方式,假設我們最多可設計有10種支付方式。
欄位仍設為int整形,A支持支付寶、微信,則值為12(1100);B支持支付寶、微信、借條,則值為13(1101),表結構如下:
ID | name | pay_flag |
---|---|---|
1 | A | 12 |
2 | B | 13 |
如果增加了貨到付款,可再賦值給二進位的第五位,其它位還是保持不變。
這時候會涉及到資料庫查詢問題,比如上面的值12、13都支持支付寶、微信,還有14(1110)、15(1111)也支持,如果增加了二進位第五位,那麼會有更多匹配值,如30(11110)、28(11100)等...
如果要查詢支持支付寶、微信的數據怎麼辦?這時只需要通過“位”的與運算,就能簡單的查詢出想要數據:
select * from user_pay where pay_flag & b'1100';
# 或者:
select * from user_pay where pay_flag & 12;
php簡單實現
class PayFlag {
const ALIPAY = 8; //01000
const WEIXIN = 4; //00100
const UNION = 2; //00010
const IOU = 1; //00001
function addFlag($old_flag, $flag) {
return $old_flag | $flag;
}
function delFlag($old_flag, $flag) {
return $old_flag ^ $flag;
}
}
$old_flag = 6; //00110
$PayFlag = new PayFlag;
//原有值 - 輸出 6:110
echo($old_flag . ":" . decbin($old_flag) . PHP_EOL);
//增加ALIPAY - 輸出 14:1110
$new_flag = $PayFlag->addFlag($old_flag, PayFlag::ALIPAY);
echo($new_flag . ":" . decbin($new_flag) . PHP_EOL);
//移除ALIPAY - 輸出 6:110
$new_flag = $PayFlag->delFlag($new_flag, PayFlag::ALIPAY);
echo($new_flag . ":" . decbin($new_flag) . PHP_EOL);
//移除UNION - 輸出 4:100
$new_flag = $PayFlag->delFlag($new_flag, PayFlag::UNION);
echo($new_flag . ":" . decbin($new_flag) . PHP_EOL);
//增加IOU - 輸出 5:101
$new_flag = $PayFlag->delFlag($new_flag, PayFlag::IOU);
echo($new_flag . ":" . decbin($new_flag) . PHP_EOL);