具有动态json键的mysql过滤器

jv2fixgn  于 2021-06-20  发布在  Mysql
关注(0)|答案(2)|浏览(386)

我需要从下面的mysql数据集中选择实现的所有值之和(=true)。在这个例子中,我应该只得到1000。
请注意,键是动态的(年\月\日)。
我可以通过运行下面的sql来获取值列表:

SELECT (JSON_EXTRACT(json_value, "$**.value")) AS total FROM milestone

但是,我似乎无法让过滤器部分与此配合工作:

JSON_CONTAINS_PATH(json_value, 'all', "$**.realized") IS NOT NULL

id=1,列值

{
  "2018": {
    "5": {
      "4": {
        "value": "5000"
      }
    },
    "12": {
      "4": {
        "value": "80000",
        "realized": "false"
      }
    }
  }
}

id=2,列值

{
  "2016": {
    "12": {
      "4": {
        "value": "1000",
        "realized": "true"
      }
    }
  }
}
nr9pn0ug

nr9pn0ug1#

下面是一个适用于MySQL5.7的查询:

SELECT SUM(
  JSON_UNQUOTE(
    JSON_EXTRACT(
      json_value,
      CONCAT(
        SUBSTRING_INDEX(
          JSON_UNQUOTE(JSON_SEARCH(json_value, 'all', 'true')), '.', 4),
        '.value'
      )
    )
  )
) AS sum
FROM milestone 
WHERE SUBSTRING_INDEX(
    JSON_UNQUOTE(JSON_SEARCH(json_value, 'all', 'true')),
  '.', -1) = 'realized'

这是相当复杂的,并且很难开发和维护。此外,它可能无法处理您有多个 realized: true 给定json文档中的条目。或者如果除了“realized”之外还有其他json键的值为“true”。甚至考虑边缘情况都是相当棘手的,更不用说在代码中处理它们了。
出于好奇,为什么不将这些数据存储为传统的表呢?

CREATE TABLE milestone (
  milestone_id INT NOT NULL,
  date DATE NOT NULL,
  value INT NOT NULL,
  realized BOOL,
  PRIMARY KEY (milestone_id, date)
);

INSERT INTO milestone VALUES
(1, '2018-05-04',  5000, NULL),
(1, '2018-12-04', 80000, false),
(2, '2016-12-04',  1000, true);

这样做查询就更简单了:

SELECT SUM(value) FROM milestone WHERE realized = true;

我很好奇,因为我看到越来越多的人在mysql中使用json来处理更容易使用普通表和列的情况。更容易编写查询、更容易理解查询、更高效地存储数据和优化查询性能。
以您目前的方式使用json会使查询变得更加困难。

insrf1ej

insrf1ej2#

在mysql的现代版本(>=8.0.4)中,查询相对简单(请参见json\表):

SELECT
  `milestone`.`id`,
  SUM(`der`.`value`) `total`
FROM
  `milestone`,
  JSON_TABLE(
    JSON_ARRAY(`json_value`),
    '$[*]' COLUMNS(
      NESTED PATH '$**.*' COLUMNS(
        `value` DECIMAL(10, 2) PATH '$.value',
        `realized` VARCHAR(5) PATH '$.realized'
      )
    )
  ) `der`
WHERE
  `der`.`value` IS NOT NULL AND
  `der`.`realized` = 'true'
GROUP BY
  `milestone`.`id`;

见db小提琴。

相关问题