带有动态JSON键的mysql过滤器

时间:2018-09-03 07:37:39

标签: mysql json

我需要从下面的mysql数据集中选择所有实现的值(= true)的SUM。在这个例子中,我应该只得到1000。

请注意,键是动态的(年\月\日)。

我可以通过运行以下sql来获取值列表:

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

但是,我似乎无法使过滤器部分与此配合使用:

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

id = 1,列json_value

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

id = 2,列json_value

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

2 个答案:

答案 0 :(得分:1)

以下是可在MySQL 5.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'

这是相当复杂的,并且难以开发和维护。同样,它可能无法处理在给定的JSON文档中有多个realized: true条目的情况。或者,除了“ realized”以外,还有其他值为“ true”的JSON密钥。甚至想到边缘情况都将非常棘手,更不用说用代码处理它们了。

出于好奇,为什么不将这些数据存储为传统表?

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会使查询比原本要困难得多。

答案 1 :(得分:0)

在现代版本的MySQL(> = 8.0.4)中,查询相对简单(请参见JSON_TABLE):

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-fiddle