【问题标题】:MYSQL: How to query where JSON array contain specific labelMYSQL:如何查询 JSON 数组包含特定标签的位置
【发布时间】:2019-07-31 23:51:39
【问题描述】:

MySQL 5.7.24

假设我有 3 行这样的:

ID (PK) | Name (VARCHAR) | Data (JSON)
--------+----------------+-------------------------------------
 1      | Admad          | [{"label":"Color", "value":"Red"}, {"label":"Age", "value":40}]
 2      | Saleem         | [{"label":"Color", "value":"Green"}, {"label":"Age", "value":37}, {"label":"Hoby", "value":"Chess"}]
 3      | Daniel         | [{"label":"Food", "value":"Grape"}, {"label":"Age", "value":47}, {"label":"State", "value":"Sel"}]

规则 #1:JSON 列是动态的。意味着不是每个人都有相同的结构

规则 #2:假设我不能修改数据结构

我的问题,是否可以查询,以便我可以获得 Age >= 40 的记录的 ID?在本例中为 1 和 3。

附加信息(在被指出为重复之后):如果您查看我的数据,则父容器是数组。如果我像

这样存储我的数据
{"Age":"40", "Color":"Red"} 

那么我可以简单地使用

Data->>'$.Age' >= 40

我目前的想法是使用存储过程来循环数组,但我希望我不必走那条路。第二种选择是使用正则表达式(我也不希望这样)。如果您认为“JSON 搜索”是解决方案,请指出是哪一个(或我这个菜鸟的一些示例)。文档过于笼统,无法满足我的特定需求。

【问题讨论】:

  • 另见manual中的JSON搜索功能
  • 为什么要将 JSON 打包到数据库中而不是使用表、列和行?
  • 看起来它让事情变得更难,而不是更简单。不要偷工减料;从长远来看,它会总是咬你。
  • JSON 使插入数据变得更容易,但显然它使选择数据变得更加复杂。您正在进行权衡,如果不询问 Stack Overflow,您无法弄清楚如何选择数据,这意味着这不是一个好的权衡。

标签: mysql json


【解决方案1】:

这是一个演示:

mysql> create table letsayi (id int primary key, name varchar(255), data json);

mysql> > insert into letsayi values
-> (1, 'Admad', '[{"label":"Color", "value":"Red"}, {"label":"Age", "value":"40"}]'),
-> (2, 'Saleem', '[{"label":"Color", "value":"Green"}, {"label":"Age", "value":"37"}, {"label":"Hoby", "value":"Chess"}]');

mysql>  select id, name from letsayi 
        where json_contains(data, '{"label":"Age","value":"40"}');
+----+-------+
| id | name  |
+----+-------+
|  1 | Admad |
+----+-------+

我不得不说这是存储数据效率最低的方式。即使您在生成的列上使用索引,也无法使用索引来搜索您的数据。您甚至没有将整数“40”存储为整数 - 您将数字存储为字符串,这使得它们占用更多空间。

在不需要时在 MySQL 中使用 JSON 是个坏主意。


是否还能查询年龄>=40?

不使用JSON_CONTAINS()。该函数不像WHERE 子句中的不等式条件。它只匹配子文档的完全相等。

要进行不等式,您必须升级到 MySQL 8.0 并使用 JSON_TABLE()。我最近回答了另一个问题:MySQL nested JSON column search and extract sub JSON

换句话说,您必须将您的 JSON 转换为一种格式,就像您将它存储在传统的行和列中一样。但是您必须在每次查询数据时都这样做。

如果您需要在WHERE 子句中使用条件,最好不要使用 JSON。它只会使您的查询过于复杂。听听这个关于编程的老建议:

“调试的难度是一开始编写代码的两倍。因此,如果你尽可能巧妙地编写代码,那么根据定义,你就不够聪明,无法调试它。” — 布赖恩·克尼汉


人们如何处理动态添加的表单字段

您可以为动态表单字段创建键/值表:

CREATE TABLE keyvalue (
  user_id INT NOT NULL,
  label VARCHAR(64) NOT NULL,
  value VARCHAR(255) NOT NULL,
  PRIMARY KEY (user_id, label),
  INDEX (label)
);

然后您可以为每个用户的动态表单条目添加键/值对:

INSERT INTO keyvalue (user_id, label, value)
VALUES (123, 'Color', 'Red'),
       (123, 'Age', '40');

与真正的列相比,这在存储上仍然有点低效,因为每次输入用户数据时都会存储标签名称,并且仍然将整数存储为字符串。但是如果真的允许用户存储他们自己选择的任何标签,你就不能制作那些真正的列。

有了key/value表,查询年龄>40就更简单了:

SELECT user_id FROM key_value
WHERE label = 'Age' AND value >= 40

【讨论】:

  • 谢谢先生。我可以利用这个答案。顺便说一句,由于您提到将整数存储为字符串,所以我稍微修改了我的问题。是否仍然可以查询年龄> = 40?当你说“在 MySQL 中使用 JSON 而不是”时,我只是想知道人们如何处理动态添加的表单字段
  • 还有一个问题是在将小数存储为字符串时键/值表的舍入问题..您可以设计/扩展此键/值表以保存具有更多表的本机类型..
  • 或者每个 SQL 数据类型有一个 value 列。
  • 由于我们使用 RDBMS 处理动态表单字段,我找不到新的 JSON 数据类型的任何用途。顺便说一句,谢谢 Brian 的报价。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-11-25
  • 2020-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-09
相关资源
最近更新 更多