多属性、多分类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 07 MySQL
详解MySQL的半同步
Apr 22 MySQL
MySQL 数据类型选择原则
May 27 MySQL
MySQL库表名大小写的选择
Jun 05 MySQL
MySQL 聚合函数排序
Jul 16 MySQL
WINDOWS 64位 下安装配置mysql8.0.25最详细的教程
Mar 22 MySQL
为什么MySQL不建议使用SELECT *
Apr 03 MySQL
Mysql查询时间区间日期列表,不会由于数据表数据影响
Apr 19 MySQL
MYSQL事务的隔离级别与MVCC
May 25 MySQL
MySQL选择合适的备份策略和备份工具
Jun 01 MySQL
了解MySQL查询语句执行过程(5大组件)
Aug 14 MySQL
DQL数据查询语句使用示例
Dec 24 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
php递归实现无限分类生成下拉列表的函数
2010/08/08 PHP
详解WordPress中用于更新和获取用户选项数据的PHP函数
2016/03/08 PHP
php源码的使用方法讲解
2019/09/26 PHP
Thinkphp 3.2框架使用Redis的方法详解
2019/10/24 PHP
ExtJs 表单提交登陆实现代码
2010/08/19 Javascript
jquery中交替点击事件toggle方法的使用示例
2013/12/08 Javascript
PHP结合jQuery实现的评论顶、踩功能
2015/07/22 Javascript
修复jQuery tablesorter无法正确排序的bug(加千分位数字后)
2016/03/30 Javascript
BootStrap智能表单demo示例详解
2016/06/13 Javascript
jquery 标签 隔若干行加空白或者加虚线的方法
2016/12/07 Javascript
Javascript同时声明一连串(多个)变量的方法
2017/01/23 Javascript
AngularJS实用基础知识_入门必备篇(推荐)
2017/07/10 Javascript
解决webpack无法通过IP地址访问localhost的问题
2018/02/22 Javascript
微信小程序在线客服自动回复功能(基于node)
2019/07/03 Javascript
JS控制只能输入数字并且最多允许小数点两位
2019/11/24 Javascript
js实现mp3录音通过websocket实时传送+简易波形图效果
2020/06/12 Javascript
vue+echarts+datav大屏数据展示及实现中国地图省市县下钻功能
2020/11/16 Javascript
[50:50]完美世界DOTA2联赛PWL S3 INK ICE vs DLG 第一场 12.20
2020/12/23 DOTA
跟老齐学Python之网站的结构
2014/10/24 Python
django启动uwsgi报错的解决方法
2018/04/08 Python
使用 Python 实现文件递归遍历的三种方式
2018/07/18 Python
在pyqt5中QLineEdit里面的内容回车发送的实例
2019/06/21 Python
画pytorch模型图,以及参数计算的方法
2019/08/17 Python
python对验证码降噪的实现示例代码
2019/11/12 Python
在pycharm中为项目导入anacodna环境的操作方法
2020/02/12 Python
500行python代码实现飞机大战
2020/04/24 Python
Python3 socket即时通讯脚本实现代码实例(threading多线程)
2020/06/01 Python
Python使用pyenv实现多环境管理
2021/02/05 Python
用python 绘制茎叶图和复合饼图
2021/02/26 Python
世界上最大的乐器零售商:Guitar Center
2017/11/07 全球购物
毕业生就业自荐书
2013/12/15 职场文书
小学语文国培感言
2014/03/04 职场文书
铁路安全事故反思
2014/04/26 职场文书
婚礼庆典答谢词
2015/01/20 职场文书
务工证明怎么写
2015/06/18 职场文书
MySQL分区以及建索引的方法总结
2022/04/13 MySQL