多属性、多分类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 相关文章推荐
Windows10下安装MySQL8
Apr 06 MySQL
MySQL8.0.24版本Release Note的一些改进点
Apr 22 MySQL
MySQL 逻辑备份与恢复测试的相关总结
May 14 MySQL
MySQL官方导出工具mysqlpump的使用
May 21 MySQL
mysql获取指定时间段中所有日期或月份的语句(不设存储过程,不加表)
Jun 18 MySQL
MySQL into_Mysql中replace与replace into用法案例详解
Sep 14 MySQL
MySQL空间数据存储及函数
Sep 25 MySQL
mysql聚集索引、辅助索引、覆盖索引、联合索引的使用
Feb 12 MySQL
mysql 获取时间方式
Mar 20 MySQL
MySql分区类型及创建分区的方法
Apr 13 MySQL
MySQL普通表如何转换成分区表
May 30 MySQL
MySQL示例讲解数据库约束以及表的设计
Jun 16 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图片上传类带图片显示
2006/11/25 PHP
PHP表单验证的3个函数ISSET()、empty()、is_numeric()的使用方法
2011/08/22 PHP
php 5.6版本中编写一个PHP扩展的简单示例
2015/01/20 PHP
Js callBack 返回前一页的js方法
2008/11/30 Javascript
类似CSDN图片切换效果脚本
2009/09/17 Javascript
为什么Node.js会这么火呢?Node.js流行的原因
2014/12/01 Javascript
JS实现自动切换文字的导航效果代码
2015/08/27 Javascript
深入浅析同源策略和跨域访问
2015/11/26 Javascript
使用Web Uploader实现多文件上传
2016/06/08 Javascript
AngularJS中的API(接口)简单实现
2016/07/28 Javascript
Bootstrap3 Grid system原理及应用详解
2016/09/30 Javascript
JavaScript的new date等日期函数在safari中遇到的坑
2016/10/24 Javascript
vue-resourse将json数据输出实例
2017/03/08 Javascript
webpack进阶——缓存与独立打包的用法
2017/08/02 Javascript
Vue上传组件vue Simple Uploader的用法示例
2017/08/25 Javascript
Vue自定义指令写法与个人理解
2019/02/09 Javascript
extract-text-webpack-plugin用法详解
2019/02/14 Javascript
vue-cli3+typescript新建一个项目的思路分析
2019/08/06 Javascript
VUE.js实现动态设置输入框disabled属性
2019/10/28 Javascript
JavaScript大数相加相乘的实现方法实例
2020/10/18 Javascript
python制作最美应用的爬虫
2015/10/28 Python
Python实现屏幕截图的代码及函数详解
2016/10/01 Python
Python使用min、max函数查找二维数据矩阵中最小、最大值的方法
2018/05/15 Python
Python 从一个文件中调用另一个文件的类方法
2019/01/10 Python
利用pytorch实现对CIFAR-10数据集的分类
2020/01/14 Python
Python3查找列表中重复元素的个数的3种方法详解
2020/02/13 Python
HTML5新特性之语义化标签
2017/10/31 HTML / CSS
WiFi云数码相框:Nixplay
2018/07/05 全球购物
杭州-DOTNET笔试题集
2013/09/25 面试题
理货员的岗位职责
2013/11/23 职场文书
管理部部长岗位职责
2013/12/05 职场文书
电力安全事故反思
2014/04/27 职场文书
大型演出策划方案
2014/05/28 职场文书
政风行风自查自纠报告
2014/10/21 职场文书
干货:如何写好工作计划!
2019/05/17 职场文书
Nginx虚拟主机的搭建的实现步骤
2022/01/18 Servers