当前位置: 首页 > news >正文

一种多选项的高效存取(存储、查询)解决方案

本文主要内容

  • 对于多选项的值,如何保存?本文提供了一种非常规的方案。
  • 对于记录在数据库中的多选项的值,如何快速查询那些记录是包含了某个(某些)选项?本文使用了“与位运算”解决查询问题。
  • 源码地址:https://github.com/HackyleShawe/JavaBackEndDemos/tree/master/BusinessCommonSolution/multi-options-storage-query-demo

文章前置知识:SpringBoot、JDBCTemplate、位运算(与运算)、jQuery

内容导览 

  • 背景
  • 设计思想
    • 编码:将多选项转换为数字
      • 例1
      • 例2
    • 查询原理
  • 项目启动
    • 新增数据演示
    • 查询数据演示
  • 查询示例
    • 构造数据
    • 插入到数据库
    • 查询选择了"1-编程"这个选项的记录
    • 查询选择了"2-听音乐唱歌,5-看电影"这些选项的记录

背景

在项目开发中,如何保存多选项的值呢?例如下图中的职业发展和兴趣爱好

  • 最容易想到的就是,选择了哪些选项,就把该选项值存储起来。

  • 在数据库层面设置一个VARCHAR,例如选择了"收入无上线、培训与发展、职业价值感",前端就传递"收入无上线、培训与发展、职业价值感",数据库就保存为"收入无上线、培训与发展、职业价值感"。

可是这样存在一个问题,我要查询那些人选择了其中的某个、某些选项,就很难实现。例如,查询那些人的兴趣爱好是"编程、篮球",查询兴趣爱好是"看书、写作"的人数有多少。

为了解决这种多选项的高效查询问题,本人设计了一种方法,可以实现快速、高效地查询多选项中有某个、某些选项的所有记录。

设计思想

核心思路:

  1. 编码:将每个选项与特定的比特位上对应起来,选中为1,未选中为0

  2. 存储:将比特位转换为Long型(64bit,最大支持64个多选项),数据库表字段为BIGINT

  3. 查询:与(&)位运算

主要流程

  • 编码:将多选项进行编号

    • 例如:对职业发展的多选项进行编号为:1-收入无上限,2-培训与发展,3-职业价值感,4-行业稳定性,5-社交与人脉,6-塑造个人品牌,7-团队综合素质,8-终身学习;

    • 对兴趣爱好的多选项进行编号为:1-编程,2-听音乐唱歌,3-篮球,4-玩游戏,5-看电影,6-享美食,7-健身,8-旅游,9-看书,10-写作。

  • 存储

    • 前端:选择了哪些选项,就传递哪些选项的编号例如:选择了"编程、篮球",则传递的编号串为:"1,3"

    • 后端:将编号串转换为二进制串,再转换为数字,落库

  • 查询:前端还是只传选项编号;后端将其转换为数字;在数据库层面使用位运算中的与运算,匹配包含了参数选项的记录

编码:将多选项转换为数字

现以兴趣爱好为例,为其多选项定义编号:1-编程,2-听音乐唱歌,3-篮球,4-玩游戏,5-看电影,6-享美食,7-健身,8-旅游,9-看书,10-写作

根据二进制位下标与十进制数的互转:

例1

用户A勾选的兴趣爱好为:"1-编程,2-听音乐唱歌,5-看电影,6-享美食"
前端传递的串为:1,2,5,6
转换为二进制串:0011 0011
转换规则:在有选项编号出现的位下标的位置上填充1,其他位置填充0
转换为十进制后落库:51

例2

用户B勾选的兴趣爱好为:"3-篮球,4-玩游戏,6-享美食,7-健身,10-写作"
前端传递的串为:3,4,6,7,10
转换为二进制串:0010 0110 1100
转换为十进制后落库:620

查询原理

查询的核心思想:与(&)位运算

  • X & Y = X,则说明X的所有值为"1"的二进制位、在Y中对应的二进制位也为"1"。

  • 也就是说,Y为"1"的二进制位包含了X的所有为"1"的二进制位。从而实现了,查询条件X,在Y中存在(包含)

项目启动

Step 1:在application.yml中修改数据库连接参数

Step 2:执行resources/sql.sql下的SQL文件,初始化数据

Step 3:从启动类App.java启动

Step 4:启动成功后进入前端页面:http://localhost:9898/person.html

新增数据演示

必要数据,多选项进行勾选:

去数据库查看刚刚新增的记录:

查询数据演示

选择查询条件,点击“Query”进行查询:

查看运行日志,显示执行的SQL:

查询示例

以多选项”兴趣爱好“为例,展示查询的工作原理

构造数据

AA选择了"1-编程,2-听音乐唱歌,5-看电影,6-享美食,7-健身,8-旅游"
前端传递的编号串:1,2,5,6,7,8
转换为二进制串:0000 1111 0011
转换为数字:243

BB选择了"3-篮球,4-玩游戏,8-旅游,9-看书,10-写作"
前端传递的编号串:3,4,8,9,10
转换为二进制串:0011 1000 1100
转换为数字:908

CC选择了"2-听音乐唱歌,3-篮球,5-看电影,6-享美食,7-健身,9-看书,10-写作"
前端传递的编号串:2,3,5,6,7,9,10
转换为二进制串:0011 0111 0110
转换为数字:886

DD选择了"1-编程,3-篮球,4-玩游戏,6-享美食,7-健身,8-旅游,10-写作"
前端传递的编号串:1,3,4,6,7,8,10
转换为二进制串:0010 0110 1101
转换为数字:749

EE选择了"2-听音乐唱歌,4-玩游戏,5-看电影,7-健身,8-旅游,10-写作"
前端传递的编号串:2,4,5,7,8,10
转换为二进制串:0010 1101 1010
转换为数字:730

FF选择了"1-编程,2-听音乐唱歌,3-篮球,4-玩游戏,5-看电影,7-健身"
前端传递的编号串:1,2,3,4,5,7
转换为二进制串:0000 0101 1111
转换为数字:95

GG选择了"1-编程,3-篮球,4-玩游戏,6-享美食,8-旅游,10-写作"
前端传递的编号串:1,3,4,6,8,10
转换为二进制串:0010 1010 1101
转换为数字:685

插入到数据库

DROP TABLE IF EXISTS person;
CREATE TABLE person (id BIGINT AUTO_INCREMENT,name VARCHAR(50) DEFAULT NULL COMMENT '姓名',gender INT DEFAULT NULL COMMENT '性别,0-女,1-男',address VARCHAR(128) DEFAULT NULL COMMENT '地址',careers BIGINT DEFAULT NULL COMMENT '职业发展多选项',-- 兴趣爱好多选项。可选项:1-编程,2-听音乐唱歌,3-篮球,4-玩游戏,5-看电影,6-享美食,7-健身,8-旅游,9-看书,10-写作。-- 例如,全选:"11 1111 1111",保存为十进制=1023,全不选:"00 0000 0000",保存为十进制=0,只选择听音乐唱歌:"0000 0010",保存为十进制=2-- LONG最大支持64位,最多支持64个多选项的任意选择interests BIGINT DEFAULT NULL COMMENT '兴趣爱好多选项',create_time DATETIME DEFAULT NULL,update_time DATETIME DEFAULT NULL,deleted INT DEFAULT 0 COMMENT '是否删除:0-否,1-是',PRIMARY KEY(id)
);
-- 将上文中构造的数据,以SQL的形式插入到数据库中,只以多选项"兴趣爱好"为例
INSERT INTO person(name, gender, address, careers, interests, create_time, update_time, deleted)
VALUES ('AA', 1, 'SH CN', 1, 243, '2022-12-12','2023-12-12', 0),('BB', 1, 'SH CN', 1, 908, '2022-12-12','2023-12-12', 0),('CC', 1, 'SH CN', 1, 886, '2022-12-12','2023-12-12', 0),('DD', 1, 'SH CN', 1, 749, '2022-12-12','2023-12-12', 0),('EE', 1, 'SH CN', 1, 730, '2022-12-12','2023-12-12', 0),('FF', 1, 'SH CN', 1, 95, '2022-12-12','2023-12-12', 0),('GG', 1, 'SH CN', 1, 685, '2022-12-12','2023-12-12', 0);

查询选择了"1-编程"这个选项的记录

目标:在多选项中查询选择了"1-编程"这个选项的记录

用户的"兴趣爱好"多选项(二进制形式)

AA: 0000 1111 0011
BB: 0011 1000 1100
CC: 0011 0111 0110
DD: 0010 0110 1101
EE:  0010 1101 1010
FF:  0000 0101 1111
GG: 0010 1010 1101

将"1-编程"进行转换

  • 转换为二进制:0000 0000 0001

  • 转换为十进制:1

查询原理:将查询条件"0000 0000 0001"与AA~GG的二进制位进行与运算后,仍然为查询条件的记录,则是选择了"1-编程"这个选项的记录

查询过程

  • 将查询条件与AA的二进制位进行与运算:
   0000 0000 0001
&  0000 1111 0011
#--------------------------0000 0000 0001  与运算结果仍为查询条件,说明这条记录包含了"1-编程"这个选项
  • 将查询条件与BB的二进制位进行与运算:
   0000 0000 0001
&  0011 1000 1100
#----------------------------0000 0000 0000   与运算结果不为查询条件,说明这条记录不包含了"1-编程"这个选项
  • 其他记录运算同理

  • 最终发现只有AA、DD、FF、GG的运算结果符合条件,这四个记录就是满足"选择了1-编程这个选项的所有记录"

SQL实现

select * FROM person WHERE interests & 1 = 1;

查询选择了"2-听音乐唱歌,5-看电影"这些选项的记录

目标:在多选项中查询选择了"2-听音乐唱歌,5-看电影"这些选项的记录

用户的"兴趣爱好"多选项(二进制形式)

AA: 0000 1111 0011
BB: 0011 1000 1100
CC: 0011 0111 0110
DD: 0010 0110 1101
EE:  0010 1101 1010
FF:  0000 0101 1111
GG: 0010 1010 1101

将"2-听音乐唱歌,5-看电影"进行转换

  • 转换为二进制:0000 0001 0010

  • 转换为十进制:18

查询原理:将查询条件"0000 0001 0010"与AA~GG的二进制位进行与运算后,仍然为查询条件的记录,则是选择了"2-听音乐唱歌,5-看电影"这个选项的记录

查询过程

  • 将查询条件与AA的二进制位进行与运算:

  0000 0001 0010
& 0000 1111 0011
#--------------------------0000 0001 0010  与运算结果仍为查询条件,说明这条记录包含了"2-听音乐唱歌,5-看电影"这些选项
  • 将查询条件与BB的二进制位进行与运算:
  0000 0001 0010
& 0011 1000 1100
#----------------------------0000 0000 0000   与运算结果不为查询条件,说明这条记录不包含了"2-听音乐唱歌,5-看电影"这些选项
  • 其他记录运算同理

  • 最终发现只有AA、CC、EE、FF的运算结果符合条件,这四个记录就是满足"选择了2-听音乐唱歌,5-看电影这些选项的所有记录"

SQL实现

select * FROM person WHERE interests & 18 = 18;

 

http://www.jsqmd.com/news/339131/

相关文章:

  • Erlang 使用escript打包多个模块构建一个可执行文件
  • AI产品经理:大模型时代最有“钱“景的岗位,零基础入门到实战全攻略_想转行AI产品经理,90%的人第一步就走错了!
  • 计算机毕业设计springboot飞机票预订系统 基于Spring Boot的航空票务服务平台设计与实现 基于Java Web的民航订票管理系统开发
  • IS420UCSBH4A 产品概述
  • 收藏!AI工程师的两大方向:传统算法VS大模型应用,小白如何抓住AI风口?_传统算法vs大模型应用开发工程师
  • 京东e卡回收参考价格,市场行情与核心数据全解析 - 京顺回收
  • 2025年SEVC SCI2区,结合低差异序列和共轭梯度法的新型异构综合学习粒子群算法,深度解析+性能实测
  • 科技普惠基层,AI肝胆超级医生让优质诊疗服务下沉
  • 妙啊!浙大学者评估动态虚弱轨迹,四库联合登上一区Top(IF 13) | 公共数据库好文汇总
  • 跨境电商营销策略
  • 纳米抗体(VHH):特性优异的新型抗体工具 多领域临床应用潜力显著
  • 芯片产业链全景透视:从EDA到终端,拆解万亿赛道核心壁垒
  • 利用LLM+RAG实现知识图谱自动更新:小白也能上手的AI实战指南
  • 大语言模型在智能风险管理中的推理应用探索
  • 拥抱AI最好的方式:带着兄弟们部署一个OpenClaw,24小时智能助手Get!
  • PDF解析+大模型=翻车?手把手教你构建可靠的知识库系统,建议收藏!
  • Snowflake投资2亿美元引入OpenAI模型提升数据库对话能力
  • 没想到,Momenta单月智驾搭载量近9万了......
  • 【报告】广东鸿图泰国建厂:一次围绕履约半径与组织边界的出海尝试
  • RabbitMQ在大数据领域的实时数据处理架构
  • OpenClaw修复一键远程代码执行漏洞,安全漏洞层出不穷
  • 上交自动驾驶3D重建综述!从NeRF到3DGS的全面调研(T-ITS‘25)
  • 山东道恩高分子材料在越南买下的,不只是一个工厂
  • Pandas 常用函数
  • Software Development Process Project Management 2
  • Oracle数据库26ai首个通用版引质疑
  • 26年已经跑了九家前端面试,基本全过了
  • 嵌入式基础——上拉电阻与下拉电阻
  • 基于深度确定性策略梯度算法(DDPG)进行滑模控制(SMC)调参优化算法DDPG_SMC(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码
  • AI原生应用领域中AI代理的动态调整策略