查询MariaDB / MySQL中的多级JSON以获取特定值

问题描述 投票:1回答:1

我正在使用一个存储JSON值的MariaDB表,如下所示:

{"nextValue":4,"1":{"text":"Item1","textDisplay":"","value":1,"isActive":0},"2":{"text":"Item2","textDisplay":"","value":2,"isActive":1},"3":{"text":"Item3","textDisplay":"","value":3,"isActive":1}}

我要做的是构建一个查询,其中我可以有一个列作为项目,即“Item2”,并在下一列中具有来自该JSON的键“值”的值。因此,如果“Item2”返回,则在它旁边的列中,它将具有“2”。

我已经尝试了许多可用于MariaDB的JSON选项,但我无法弄清楚如何做到这一点。

mysql sql json mariadb
1个回答
2
投票

我用MySQL 8.0测试了以下内容。它根据其文档使用似乎存在于MariaDB中的函数,但我没有要测试的MariaDB实例。

SELECT * FROM mytable;
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| data                                                                                                                                                                                                                               |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| {"1": {"text": "Item1", "value": 1, "isActive": 0, "textDisplay": ""}, "2": {"text": "Item2", "value": 2, "isActive": 1, "textDisplay": ""}, "3": {"text": "Item3", "value": 3, "isActive": 1, "textDisplay": ""}, "nextValue": 4} |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

SELECT JSON_UNQUOTE(JSON_EXTRACT(JSON_EXTRACT(data, REPLACE(JSON_UNQUOTE(JSON_SEARCH(data, 'one', 'Item2')), '.text', '')), '$.value')) AS value FROM mytable;
+-------+
| value |
+-------+
| 2     |
+-------+

这非常脆弱,依赖于在JSON路径上进行字符串替换,然后在其他JSON函数中使用这些路径。这将花费你的雇主很多工程师时间来开发和维护这样复杂的SQL语句。

另一种方法是升级到MySQL 8.0并使用JSON_TABLE()函数将JSON文档映射到派生表,然后可以在WHERE子句中使用条件。

SELECT j.* FROM mytable2, 
JSON_TABLE(mytable2.data, '$[*]' 
  COLUMNS (
    rowid FOR ORDINALITY,
    `text` VARCHAR(20) PATH '$.text',
    textDisplay TEXT PATH '$.textDisplay',
    value INT PATH '$.value',
    isActive BOOL PATH '$.isActive'
  )
) AS j

+-------+-------+-------------+-------+----------+
| rowid | text  | textDisplay | value | isActive |
+-------+-------+-------------+-------+----------+
|     1 | Item1 |             |     1 |        0 |
|     2 | Item2 |             |     2 |        1 |
|     3 | Item3 |             |     3 |        1 |
+-------+-------+-------------+-------+----------+

但这不适用于您拥有的JSON数据,因为该函数仅在您的JSON是数组时有效,而您的数据被格式化为JSON对象。只有当我将JSON数据格式更改为数组时,我才使上面的示例工作:

select * from mytable2;
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| data                                                                                                                                                                                                |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| [{"text": "Item1", "value": 1, "isActive": 0, "textDisplay": ""}, {"text": "Item2", "value": 2, "isActive": 1, "textDisplay": ""}, {"text": "Item3", "value": 3, "isActive": 1, "textDisplay": ""}] |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

您的开发人员设计的JSON格式旨在使他们能够轻松插入数据,但它完全无偿地使用JSON,并且使得对数据的查询不必要地复杂化。这是Inner-Platform Effect的一个例子:

内部平台效应是软件架构师创建一个可定制的系统的趋势,以便成为他们正在使用的软件开发平台的副本,并且通常是一个糟糕的副本。这通常是低效的,并且这种系统通常被认为是反模式的示例。

正如我在上面评论的那样,我将其设计为普通的SQL表:

CREATE TABLE Items (
  id INT AUTO_INCREMENT PRIMARY KEY,
  `text` VARCHAR(20), 
  textDisplay TEXT, 
  value INT, 
  isActive BOOL
);

使用每列中的值填充它:

INSERT INTO Items 
VALUES (1, 'Item1', '', 1, 0),
       (2, 'Item2', '', 2, 1),
       (3, 'Item3', '', 3, 1);

然后你可以非常简单地查询它:

SELECT value FROM Items WHERE `text` = 'Item2';
+-------+
| value |
+-------+
|     2 |
+-------+

您的雇主应该支持以正常方式存储数据的简单性,因为在查询数据时,它们将节省大量时间和金钱。

© www.soinside.com 2019 - 2024. All rights reserved.