多属性、多分类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 联合查询优化机制
May 10 MySQL
详解GaussDB for MySQL性能优化
May 18 MySQL
解决Navicat for MySQL 连接 MySQL 报2005错误的问题
May 29 MySQL
mysql获取指定时间段中所有日期或月份的语句(不设存储过程,不加表)
Jun 18 MySQL
详解MySQL中timestamp和datetime时区问题导致做DTS遇到的坑
Dec 06 MySQL
解析MySQL索引的作用
Mar 03 MySQL
一条慢SQL语句引发的改造之路
Mar 16 MySQL
mysql的Buffer Pool存储及原理
Apr 02 MySQL
MySQL磁盘碎片整理实例演示
Apr 03 MySQL
Mysql查询时间区间日期列表,不会由于数据表数据影响
Apr 19 MySQL
MySql中的json_extract函数处理json字段详情
Jun 05 MySQL
MySQL外键约束(Foreign Key)案例详解
Jun 28 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读取超大文件的实例代码
2012/04/01 PHP
记录mysql性能查询过程的使用方法
2013/05/02 PHP
解析phpstorm + xdebug 远程断点调试
2013/06/20 PHP
Windows和Linux中php代码调试工具Xdebug的安装与配置详解
2014/05/08 PHP
PHP中addslashes()和stripslashes()实现字符串转义和还原用法实例
2016/01/07 PHP
php实现页面纯静态的实例代码
2017/06/21 PHP
PHP defined()函数的使用图文详解
2019/07/20 PHP
javascript客户端解决方案 缓存提供程序
2010/07/14 Javascript
基于jQuery的简单九宫格实现代码
2012/08/09 Javascript
jQuery学习笔记 操作jQuery对象 文档处理
2012/09/19 Javascript
关闭时刷新父窗口两种方法
2014/05/07 Javascript
Mac OS X 系统下安装和部署Egret引擎开发环境
2014/09/03 Javascript
JavaScript charCodeAt方法入门实例(用于取得指定位置字符的Unicode编码)
2014/10/17 Javascript
JavaScript 学习笔记之语句
2015/01/14 Javascript
js输出数据精确到小数点后n位代码
2016/07/02 Javascript
常用JS图片滚动(无缝、平滑、上下左右滚动)代码大全(推荐)
2016/12/20 Javascript
JavaScript 程序错误Cannot use 'in' operator to search的解决方法
2017/07/10 Javascript
Underscore之Array_动力节点Java学院整理
2017/07/10 Javascript
使用Bootstrap + Vue.js实现表格的动态展示、新增和删除功能
2017/11/27 Javascript
微信小程序实现多图上传
2020/06/19 Javascript
vue实现广告栏上下滚动效果
2020/11/26 Vue.js
Python 递归函数详解及实例
2016/12/27 Python
django celery redis使用具体实践
2019/04/08 Python
python自动循环定时开关机(非重启)测试
2019/08/26 Python
css3动画过渡实现鼠标跟随导航效果
2018/02/08 HTML / CSS
一文彻底解决HTML5页面中长按保存图片功能
2019/06/10 HTML / CSS
Martinelli官方商店:西班牙皮鞋和高跟鞋品牌
2019/07/30 全球购物
水污染治理专业毕业生推荐信
2013/11/14 职场文书
庆八一活动方案
2014/01/25 职场文书
2015国际残疾人日活动总结
2015/03/24 职场文书
2016银行招聘自荐信
2016/01/28 职场文书
人力资源部工作计划
2019/05/14 职场文书
python引入其他文件夹下的py文件具体方法
2021/05/23 Python
MySQL连表查询分组去重的实现示例
2021/07/01 MySQL
i5-10400f处理相当于i7多少水平
2022/04/19 数码科技
vue特效之翻牌动画
2022/04/20 Vue.js