数据库存储多选值,你可以用这招

1,260 阅读3分钟

背景

工作中经常遇到多选值存储问题,例如:发送消息时,用户可选 短信,app,pc系统,微信,邮箱等。

方案

方案1:多字段存储

一个值对应一个字段。0表示未拥有,1表示拥有。
这种方式等缺点:每增加一种认证方式都需要添加一个表字段需要修改数据结构,扩展性差。

方案2:单字段拼接

单字段存储数据,多个值用分隔符区分。比如以如下方式存储:【短信,app】。
这种方式等缺点:不利于查询,需要使用模糊查询,搞不好会影响性能。

方案3:使用附表形式

使用附表存储数据,需要和主表数据关联起来。
这种方式等缺点:表设计复杂,查询需要关联查询数据

方案4:使用int进行位运算

比如 1代表短信,2代表app。则3可以表示短信和app。
这种方式很好的弥补了上诉方式的缺点。缺点是代码相对复杂,不易理解。

方案4案例

该方案需要注意一下几点:

  1. 枚举值定义时需要相加不重复。采用机器二进制数[1,2,4,8,16,32,64...]就能很好的表达该数据结构。
  2. 根据数据结构,需提供以下方法:
    1. 判断某个数据值是否存在于存储值中(having),用于判断数据库是否存储某个值时。
    2. 将存储值进行数据拆分,拆分成单项(mapping),用于将数据库保存数据进行还原。
    3. 将多个数据值进行合并相加,整理成存储值(merging),用于进行数据库保存时。
    4. 返回多个数据值可能存在的存储值组合(being)相当于in查询。

实现细节

having

表示某个数值集合中是否包含某个数值。比如 数字集合3是否包含数字1, 输入(3,1),输出 true,即代表3中包含1。 用于数据查询后进行逻辑判断等操作。

代码示例:

public static boolean having(int number, int memberCode) {
    return (number & memberCode) == memberCode;
}

mapping

表示返回当前数据可拆分的数据的集合。 比如:输入 7 ,输出 (1、2、4) 。 多用于查询后返回界面数据展示时。

代码示例:

public static int[] mapping(int number) {
    return Arrays.stream(CODE).filter(v -> (number & v) == v).toArray();
}

merging

将散列的值进行合并,这步操作比较简单。 比如输入(1、2、4),输出 7 。 用于将所选的数值进行储存时调用。

代码示例:

public static int merging(int[] memberCode) {
    return Arrays.stream(memberCode).sum();
}

being

用于返回某个数值集合存在于某个数值集合的可能性。举个列子:比如你现在想知道 (1、2)这个可能存在哪些合集中就可以调用该函数。 输入(1、2)输出(3、7 )。 这种在数据查询时多用到,比如用户想筛选我包含使用了短信发送信息的数据。 那么就需要调用该函数 being(1)获取所有包含1的可能性数值集合然后再去数据库作in查询。

代码示例:

public static Integer[] being(int... memberCodes) {
    if (!inCode(memberCodes)) {
        throw new IllegalArgumentException("参数错误");
    }
    int sum = Arrays.stream(memberCodes).sum();
    Integer[] excludedCodes = excluded(memberCodes);
    List<List<Integer>> objects = new ArrayList<>();

    backtrack(objects, new ArrayList<>(), excludedCodes, 0);

    List<Integer> allSum = new ArrayList<>();
    allSum.add(sum);

    objects.forEach(o -> {
        int i = 0;
        for (Integer integer : o) {
            i += integer;
        }
        allSum.add(i + sum);
    });

    return allSum.toArray(new Integer[0]);

}

一起进步

你好,我是啊Q,是一个爱技术爱生活的的程序员。

写程序几年了,现在想记录一下自己的程序员生活和故事,来掘金和大家交朋友。写的不好请多多包涵,支持一下。

我是啊Q,可掘金关注或私信我,我们一起进步。

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天 ,点击查看活动详情

ps:由于规则限制,想需要详细的代码的小伙伴可私信我。