多属性、多分类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 相关文章推荐
如何使用Maxwell实时同步mysql数据
Apr 08 MySQL
MySQL性能压力基准测试工具sysbench的使用简介
Apr 21 MySQL
将图片保存到mysql数据库并展示在前端页面的实现代码
May 02 MySQL
MySQL sql_mode修改不生效的原因及解决
May 07 MySQL
MySql学习笔记之事务隔离级别详解
May 12 MySQL
Mysql关于数据库是否应该使用外键约束详解说明
Oct 24 MySQL
SQL 聚合、分组和排序
Nov 11 MySQL
mysql使用instr达到in(字符串)的效果
Apr 03 MySQL
MySQL分区以及建索引的方法总结
Apr 13 MySQL
mysql性能优化以及配置连接参数设置
May 06 MySQL
MySQL生成千万测试数据以及遇到的问题
Aug 05 MySQL
MySQL数据库查询之多表查询总结
Aug 05 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控制用户的浏览器--ob*函数的使用说明
2007/03/16 PHP
PHPer 需要了解的 5 个 Composer 小技巧
2014/08/18 PHP
php天翼开放平台短信发送接口实现方法
2014/12/22 PHP
Laravel中使用Queue的最基本操作教程
2017/12/27 PHP
js计算页面刷新的次数
2009/07/20 Javascript
JScript 脚本实现文件下载 一般用于下载木马
2009/10/29 Javascript
JQuery each()函数如何优化循环DOM结构的性能
2012/12/10 Javascript
jquery 文本上下无缝滚动,鼠标放上去就停止 小例子
2013/06/05 Javascript
javascript实现切换td中的值
2014/12/05 Javascript
jquery左右全屏大尺寸多图滑动效果代码分享
2015/08/28 Javascript
jQuery实现带有洗牌效果的动画分页实例
2015/08/31 Javascript
js判断checkbox是否选中个数的方法(超简单)
2016/08/19 Javascript
基于bootstrap写的一点localStorage本地储存
2017/11/21 Javascript
微信小程序异步API为Promise简化异步编程的操作方法
2018/08/14 Javascript
微信小程序中如何使用flyio封装网络请求
2019/07/03 Javascript
原生js+ajax分页组件
2020/01/30 Javascript
简单了解Vue computed属性及watch区别
2020/07/10 Javascript
原生js实现购物车
2020/09/23 Javascript
[56:35]DOTA2上海特级锦标赛C组小组赛#1 OG VS Archon第二局
2016/02/27 DOTA
python递归删除指定目录及其所有内容的方法
2017/01/13 Python
python django 访问静态文件出现404或500错误
2017/01/20 Python
python爬虫获取新浪新闻教学
2018/12/23 Python
50行Python代码获取高考志愿信息的实现方法
2019/07/23 Python
OpenCV中VideoCapture类的使用详解
2020/02/14 Python
浅谈移动端网页图片预加载方案
2018/11/05 HTML / CSS
法国太阳镜店:Sunglasses Shop
2016/08/27 全球购物
接口的多继承会带来哪些问题
2015/08/17 面试题
运动会入场式解说词
2014/02/18 职场文书
写给老婆的检讨书
2014/02/21 职场文书
一体化教学实施方案
2014/05/10 职场文书
长江三峡导游词
2015/01/31 职场文书
检讨书范文大全
2015/05/07 职场文书
读书笔记格式
2015/07/02 职场文书
护理工作心得体会
2016/01/22 职场文书
Java Shutdown Hook场景使用及源码分析
2021/06/15 Java/Android
Python极值整数的边界探讨分析
2021/09/15 Python