数据库中用一个值来保存多种情况:二进制和按位异或

你们是否遇到过一张表中有很多个标识是否的字段

我们现在打个比方,我们有四种饮料,分别是 可乐,雪碧,咖啡,茶.. 我们有个人,我们想知道这个人是否有这几种饮料的其中一种或者多种,我们通常会设计如下表(以下均是0否1是)

id has_cola has_sprite has_coffee has_tea
用户id 是否有可乐 是否有雪碧 是否有咖啡 是否有茶

这样一来极大的浪费了数据库的字段,字段一多..还容易懵逼...而且这些字段.还经常被用到..且加索引基本没什么用的...

疑问

那么我们是否可以用一个字段来记录多种状态呢... 答案是可以得..请大家去看下参考文章.我这里只提供encode和decode代码

en/de code

所有状态列表

$statusArray = [
    1  => 1,//可乐
    2  => 2,//雪碧
    4  => 4,//咖啡
    8  => 8//茶
];

encode

这一步其实就是列出,一个状态是否包含某一种,或者某几种状态值

//你要包含的状态
$nums = [1];
$statusArray = [
    1  => 1,//可乐
    2  => 2,//雪碧
    4  => 4,//咖啡
    8  => 8//茶
];
$header = [];
$footer = [];
foreach ($statusArray as $item) {
    if (in_array($item, $nums)) {
        $header[] = $item;
    } else {
        $footer[] = $item;
    }
}
$combination = [array_sum($nums)];
$step = 0;
while (count($footer) > 1) {
    if ($step > 0) {
        $head = array_shift($footer);
        $header[] = $head;
    }
    $count = count($footer);
    for ($i = 0; $i < $count; $i++) {
        $combination[] = array_sum($header) + $footer[ $i ];
    }
    ++$step;
}
var_dump($combination);

decode

这个其实就是根据一个最终值,反解出你又多少个状态

/**
 * 根据一个状态值反解出有的状态值
 *
 * @param array $statusArray
 * @param int   $number
 * @param int   $base
 *
 * @return array
 */
function decodeStatus (array $statusArray, int $number, int $base = 2)
{
    if ($number < 0) throw new \Exception('被解必须大于0');
    if (!$statusArray) throw new \Exception('基本数据为空');
    if ($base <= 0) throw new \Exception('基数必须大于0');
    $maxCount = count($statusArray) - 1;
    $list = [];
    for ($i = $maxCount; $i >= 0; $i--) {
        $index = pow($base, $i);
        $temp = $number - $index;
        if ($temp >= 0) {
            $list[] = $index;
            $number = $temp;
        }
    }

    return $list;
}

缺点

  • 排序很困难, 比如我想让有雪碧的人排在前面.. :) 我现在还没想到怎么排序

参考文章