多属性、多分类MySQL模式设计


Posted in MySQL onApril 05, 2021

0、导读

这是来自B乎的一个问答。
当数据同时具备多个属性/分类时,改如何设计表结构和查询?

1、需求描述

我偶尔也会逛逛B乎,看到一些感兴趣的话题也会回复下。
有一次,看到这样的一个话题:

链接:https://www.zhihu.com/question/337083976/answer/767075575

 

[mysql] 当数据同时属于多个分类时,该怎么查询?

分类cate字段为[1,2,3,4,5] ,假如要查询满足分类'2'和'5' 的数据该怎么查询?
我尝试过用 cate like '%2%' AND cate like '%5%'去查。
想问有没有更好的办法,我这样写数据少了还好,多了根本没法查,效率太低了。

恰好我以前做过类似的业务需求设计,所以就回复了这个问题。

2、模式设计思路

这个需求可以有几种不同的解决思路,我们分别展开说一下。

2.1 用bit数据类型

大概思路如下:
1、物品属性列c1 用bit数据类型 来表示,也就是只有0、1两种取值
2、当物品属性具备某个分类属性时,其值为1,否则为0
3、假如共有5个分类,当物品拥有全部分类属性时,则其值为11111,若其不具备第3个分类属性,则其值为11011,在数据库中转成十进制存储
4、上述两种情况下,将二进制转换成十进制表示,即分别是31和27(建议横版观看,可左右滑动

[root@yejr.me] [zhishutang]> select conv(11111, 2, 10), conv(11011, 2, 10);
+--------------------+--------------------+
| conv(11111, 2, 10) | conv(11011, 2, 10) |
+--------------------+--------------------+
| 31                 | 27                 |
+--------------------+--------------------+

5、然后,只需要对该列用十进制值进行查询比对就行
6、现在如果想判断是否同时具备2、5两个分类属性时,其二进制表示为01001,转成十进制为9,只需要用条件 where c1=9 即可

我们来演示一下:(建议横版观看,可左右滑动

[root@yejr.me] [zhishutang]>show create table t_bit\G
*************************** 1. row ***************************
       Table: t_bit
Create Table: CREATE TABLE `t_bit` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `c1` int(10) unsigned NOT NULL DEFAULT '0',
  `c2` varchar(10) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `c1` (`c1`)
) ENGINE=InnoDB;

insert into t_bit select 0,conv(00001, 2, 10), 'item1';
insert into t_bit select 0,conv(00011, 2, 10), 'item2';
insert into t_bit select 0,conv(00111, 2, 10), 'item3';
insert into t_bit select 0,conv(01111, 2, 10), 'item4';
insert into t_bit select 0,conv(11111, 2, 10), 'item5';
insert into t_bit select 0,conv(10111, 2, 10), 'item6';
insert into t_bit select 0,conv(11011, 2, 10), 'item7';
insert into t_bit select 0,conv(11101, 2, 10), 'item8';
insert into t_bit select 0,conv(11110, 2, 10), 'item9';

[root@yejr.me] [zhishutang]>select * from t_bit;
+----+----+-------+
| id | c1 | c2    |
+----+----+-------+
|  1 |  1 | item1 |
|  2 |  3 | item2 |
|  3 |  7 | item3 |
|  4 | 15 | item4 |
|  5 | 31 | item5 |
|  6 | 23 | item6 |
|  7 | 27 | item7 |
|  8 | 29 | item8 |
|  9 | 30 | item9 |
+----+----+-------+

[root@yejr.me] [zhishutang]>select * from t_bit where c1 = conv(11011,2,10);
+----+----+-------+
| id | c1 | c2    |
+----+----+-------+
|  7 | 27 | item7 |
+----+----+-------+

# 同时我们也注意到这个SQL是可以正常使用索引的
[root@yejr.me] [zhishutang]>desc select * from t_bit where c1 = conv(11011,2,10)\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t_bit
   partitions: NULL
         type: ref
possible_keys: c1
          key: c1
      key_len: 4
          ref: const
         rows: 1
     filtered: 100.00
        Extra: NULL

下面两种方法是B乎网友的回复,大家也可以参考下。

  1. 用JSON数据类型,然后利用JSON_CONTAINS()函数进行查询

  2. 用SET数据类型,然后利用FIND_IN_SET()函数进行查询

不过,JSON和SET这两种数据类型都不方便加索引以及利用索引扫描,即便是用了5.7的JSON+虚拟列功能,索引效率也是比较低的。而支持JSON数据类型 多值索引(multi-valued Indexes) 也要8.0.17 以上版本才支持。

3、总结

这样看来,总的来说,用二进制转十进制方式来解决本案例需求更为高效,也欢迎提出更多方案思路。

延伸阅读

  • Multi-Valued Indexes,https://dev.mysql.com/doc/refman/8.0/en/create-index.html#create-index-multi-valued

  • The SET Type,https://dev.mysql.com/doc/refman/8.0/en/set.html

Enjoy MySQL :)

MySQL 相关文章推荐
mysql查询的控制语句图文详解
Apr 11 MySQL
MySQL Router实现MySQL的读写分离的方法
May 27 MySQL
mysql5.7使用binlog 恢复数据的方法
Jun 03 MySQL
MySQL 发生同步延迟时Seconds_Behind_Master还为0的原因
Jun 21 MySQL
详解MySQL多版本并发控制机制(MVCC)源码
Jun 23 MySQL
解决mysql的int型主键自增问题
Jul 15 MySQL
解决Mysql多行子查询的使用及空值问题
Jan 22 MySQL
浅谈redis的过期时间设置和过期删除机制
Mar 18 MySQL
MySQL实现配置主从复制项目实践
Mar 31 MySQL
MySQL创建管理KEY分区
Apr 13 MySQL
MySQL索引 高效获取数据的数据结构
May 02 MySQL
mysql 体系结构和存储引擎介绍
May 06 MySQL
多表查询、事务、DCL
Mysql Show Profile
Apr 05 #MySQL
Mysql - 常用函数 每天积极向上
Apr 05 #MySQL
mysql多表查询-笔记七
Apr 05 #MySQL
mysql部分操作
Apr 05 #MySQL
left join、inner join、right join的区别
数据库的高级查询六:表连接查询:外连接(左外连接,右外连接,UNION关键字,连接中ON与WHERE的不同)
You might like
我的论坛源代码(九)
2006/10/09 PHP
探讨:使用XMLSerialize 序列化与反序列化
2013/06/08 PHP
php实现监控varnish缓存服务器的状态
2014/12/30 PHP
php session的应用详细介绍
2017/03/22 PHP
一起来写段JS drag拖动代码
2010/12/09 Javascript
为超链接加上disabled后的故事
2010/12/10 Javascript
js中关于String对象的replace使用详解
2011/05/24 Javascript
jquery图片滚动放大代码分享(1)
2015/08/25 Javascript
JS实现n秒后自动跳转的两种方法
2020/11/30 Javascript
AngularJS基础 ng-dblclick 指令用法
2016/08/01 Javascript
jQuery通过改变input的type属性实现密码显示隐藏切换功能
2017/02/08 Javascript
Angular.js自定义指令学习笔记实例
2017/02/24 Javascript
Vue-Router模式和钩子的用法
2018/02/28 Javascript
解决Vue-cli npm run build生产环境打包,本地不能打开的问题
2018/09/20 Javascript
jquery登录的异步验证操作示例
2019/05/09 jQuery
详解VSCode配置启动Vue项目
2019/05/14 Javascript
详解Nuxt.js中使用Element-UI填坑
2019/09/06 Javascript
原生js实现表格翻页和跳转
2020/09/29 Javascript
[28:05]完美世界DOTA2联赛循环赛Inki vs DeMonsTer 第一场 10月30日
2020/10/31 DOTA
Python3.2中的字符串函数学习总结
2015/04/23 Python
开源软件包和环境管理系统Anaconda的安装使用
2017/09/04 Python
Python3结合Dlib实现人脸识别和剪切
2018/01/24 Python
基于PyQt4和PySide实现输入对话框效果
2019/02/27 Python
python实现飞机大战游戏
2020/10/26 Python
Python3.7 读取 mp3 音频文件生成波形图效果
2019/11/05 Python
python实现交并比IOU教程
2020/04/16 Python
Python调用jar包方法实现过程解析
2020/08/11 Python
What's the difference between an interface and abstract class? (接口与抽象类有什么区别)
2012/10/29 面试题
聚美优品励志广告词
2014/03/14 职场文书
企业领导班子四风对照检查材料
2014/09/27 职场文书
2014年教研工作总结
2014/12/06 职场文书
三峡大坝导游词
2015/01/31 职场文书
家长意见书
2015/06/04 职场文书
实习证明格式范文
2015/06/16 职场文书
Python 内置函数速查表一览
2021/06/02 Python
nginx的zabbix 5.0安装部署的方法步骤
2021/07/16 Servers