mysql - 选择 MySQL 表中 JSON 类型值的计数
问题描述
假设我们有下表
+-----+------------+
| id | categories |
+-----+------------+
| id1 | [20,25] |
| id2 | [25] |
| id3 | [20,25,28] |
| id4 | [28,25] |
| id5 | [20,25] |
+-----+------------+
字段categories
为 JSON 类型。它仅包含已知且有限的整数列表 - 例如 20、25、28。所以,我需要以某种方式计算所有这些值的所有包含项,如下所示:
+-------+--------+
| count | number |
+-------+--------+
| 20 | 3 |
| 25 | 5 |
| 28 | 2 |
+-------+--------+
主要问题是使用单个请求进行此操作,而无需遍历服务器代码或过程调用中的类别编号
正面决定如下
SELECT
COUNT(id) AS 'count', '20' AS number
FROM
ml_categories
WHERE
JSON_CONTAINS(categories, '20')
UNION SELECT
COUNT(id) AS 'count', '25' AS number
FROM
ml_categories
WHERE
JSON_CONTAINS(categories, '25')
UNION SELECT
COUNT(id) AS 'count', '28' AS number
FROM
ml_categories
WHERE
JSON_CONTAINS(categories, '28')
但是这个解决方案具有 O(n) 复杂性,并且代码本身还不够好。例如,对于我的硬件上的一个类别,循环约 500K 记录大约需要 1 秒,因此计算 10 个类别大约需要 10 秒。不好。有没有办法优化这样的查询?
提前谢谢各位
解决方案
但是这个解决方案有 O(n) 复杂度
我不确定你的情况是什么。但我敢肯定,您不会找到比O(n)更好的解决方案。
假设以下数字:
- n:项目数(“ml_categories”表中的行)
- m : 所有类别的数量
- a:每个项目的平均类别数
您的查询的复杂度为O(n*m)。即使是 Bill Karwins 解决方案(我认为这是最佳的)也具有O(n*a)的复杂性(假设 GROUP BY 子句的索引category_id
)。
如果您有一个categories
包含所有类别的表,则可以使用以下查询:
select c.id as category, count(*)
from ml_categories i
join categories c on json_contains(i.categories, cast(c.id as json))
group by c.id
它将返回与您的 UNION 查询相同的内容:
| category | count(*) |
| -------- | -------- |
| 20 | 3 |
| 25 | 5 |
| 28 | 2 |
并且因为没有索引可以用于 JOIN,所以它可能与您的查询一样快或一样慢(最好的情况是索引可用于 GROUP BY 避免使用filesort)。
如果你使用 MySQL 8(至少 8.0.4),你可以使用JSON_TABLE():
select c.category, count(*)
from ml_categories i
join json_table(
i.categories,
'$[*]' columns (category int path '$')
) c
group by c.category;
使用 JSON_TABLE 的 JOIN 会将 JSON 列中的类别“解包”成行。如果您删除 GROUP BY 子句,那么您将获得一个(动态)规范化表。这应该与O(n*a)一起缩放。但是由于表是动态创建的,所以不会有索引来支持 GROUP BY 子句。所以必须首先对结果进行排序,最终的复杂度为O(n*a * log(n*a))。这比O(n*m)更好地扩展(如果m增长而a不增长)。但是,如果m(类别的数量)足够小,您的查询可能仍然是使用给定模式所能做的最好的查询。
推荐阅读
- c++ - 如何使用 Makefile 编译和运行 C++ 项目?
- java - 如何使用 resttemplate 获取 JSON 数组
- android - 我的 Android 上的 USB 调试选项一直打开
- sql - UTF-8 字符不通过 SQL 脚本
- c++ - 尝试使用生产者消费者模型来模仿 linux cat 命令,但输出似乎显示了一行的多个副本
- python - 是否可以使用 EasyGui multenterbox 创建默认值?
- python-3.x - TimeGrid() 的 Python QuantLib 问题
- android-studio - Android studio 4.1,Kotlin 1.4.20-RC,不正确的导入语法错误显示
- reactjs - 组件重新渲染时状态不更新?
- neo4j - 返回不是由特定导演执导但在其他电影中与该导演合作过的演员和电影。密码(neo4j)