【问题标题】:MySQL SUM json values grouped by json keysMySQL SUM json 值按 json 键分组
【发布时间】:2017-01-14 12:58:49
【问题描述】:

是否可以计算按 json 键分组的 json 值的总和?

谷歌云sql上的Mysql版本是5.7.17。

Example_1:我的观点的一个简短例子:

col1 | col2
-----|-----------------------
aaa  | {"key1": 1, "key2": 3}
-----|-----------------------
bbb  | {"key1": 0, "key2": 2}
-----|-----------------------
aaa  | {"key1": 50, "key2": 0}

SQL 查询应该产生:

col1 | col2
-----|-----------------------
aaa  | {"key1": 51, "key2": 3}
-----|-----------------------
bbb  | {"key1": 0, "key2": 2}

是否可以使用以下任何架构?

示例_2:

col1 | col2
-----|-----------------------
aaa  | {{"key_name" : "key1", "key_value" : 1}, {"key_name" : "key2", "key_value" : 3}}
-----|-----------------------
bbb  | {{"key_name" : "key1", "key_value" : 0}, {"key_name" : "key2", "key_value" : 2}}
-----|-----------------------
aaa  | {{"key_name" : "key1", "key_value" : 50}, {"key_name" : "key2", "key_value" : 0}}

示例_3:

col1 | col2
-----|-----------------------
aaa  | [{"key_name" : "key1", "key_value" : 1}, {"key_name" : "key2", "key_value" : 3}]
-----|-----------------------
bbb  | [{"key_name" : "key1", "key_value" : 0}, {"key_name" : "key2", "key_value" : 2}]
-----|-----------------------
aaa  | [{"key_name" : "key1", "key_value" : 50}, {"key_name" : "key2", "key_value" : 0}]

示例_4:

col1 | col2
-----|-----------------------
aaa  | {"key1": {"key_name" : "key1", "key_value" : 1}, "key2": {"key_name" : "key2", "key_value" : 3}}
-----|-----------------------
bbb  | {"key1": {"key_name" : "key1", "key_value" : 0}, "key2": {"key_name" : "key2", "key_value" : 2}}
-----|-----------------------
aaa  | {"key1": {"key_name" : "key1", "key_value" : 50}, "key2": {"key_name" : "key2", "key_value" : 0}}

【问题讨论】:

  • 你有解决办法吗?
  • 我会从放弃 JSON 开始;将键:值对放在 EAV 模式中。那么 SQL 就很简单了。
  • 团队,请不要因为不探索新概念而限制自己。我喜欢 mysql 中的 json 支持,它具有巨大的价值,并且想要探索所有的可能性。所以请不要给传统的group by option作为答案。
  • @wchiquito 你能帮帮忙吗..?

标签: mysql json aggregate-functions google-cloud-sql


【解决方案1】:

TL;DR:是的,它可以在事先不知道键名的情况下完成,并且没有任何一种替代数据格式比原始数据格式有任何优势。

这可以在事先不知道键名的情况下完成,但这很痛苦……基本上,您必须先查看表中的每个值以确定表中不同键的集合,然后才能对它们求和。由于这个要求,以及备用数据格式每个条目都可以有多个键的事实,使用它们中的任何一个都没有优势。

由于您必须查找所有不同的键,因此在查找它们的同时求和也很容易。这个函数和过程一起完成。函数json_merge_sum 采用两个 JSON 值并将它们合并,将键出现在两个值中的值相加,例如

SELECT json_sum_merge('{"key1": 1, "key2": 3}', '{"key3": 1, "key2": 2}')

输出:

{"key1": 1, "key2": 5, "key3": 1}

功能代码:

DELIMITER //
DROP FUNCTION IF EXISTS json_merge_sum //
CREATE FUNCTION json_sum_merge(IN j1 JSON, IN total JSON) RETURNS JSON
BEGIN
  DECLARE knum INT DEFAULT 0;
  DECLARE jkeys JSON DEFAULT JSON_KEYS(j1);
  DECLARE kpath VARCHAR(20);
  DECLARE v INT;
  DECLARE l INT DEFAULT JSON_LENGTH(jkeys);
  kloop: LOOP
    IF knum >= l THEN
      LEAVE kloop;
    END IF;
    SET kpath = CONCAT('$.', JSON_EXTRACT(jkeys, CONCAT('$[', knum, ']')));
    SET v = JSON_EXTRACT(j1, kpath);
    IF JSON_CONTAINS_PATH(total, 'one', kpath) THEN
      SET total = JSON_REPLACE(total, kpath, JSON_EXTRACT(total, kpath) + v);
    ELSE
      SET total = JSON_SET(total, kpath, v);
    END IF;
    SET knum = knum + 1;
  END LOOP kloop;
  RETURN total;
END

过程count_keys 执行与GROUP BY 子句等效的操作。它在表中找到col1 的所有不同值,然后为具有col1 值的每一行调用json_sum_merge。请注意,行选择查询执行 SELECT ... INTO 一个虚拟变量,因此不会生成输出,并使用 MIN() 确保只有一个结果(以便可以将其分配给变量)。

程序:

DELIMITER //
DROP PROCEDURE IF EXISTS count_keys //
CREATE PROCEDURE count_keys()
BEGIN
  DECLARE finished INT DEFAULT 0;
  DECLARE col1val VARCHAR(20);
  DECLARE col1_cursor CURSOR FOR SELECT DISTINCT col1 FROM table2;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished=1;
  OPEN col1_cursor;
  col1_loop: LOOP
    FETCH col1_cursor INTO col1val;
    IF finished=1 THEN
      LEAVE col1_loop;
    END IF;
    SET @total = '{}';
    SET @query = CONCAT("SELECT MIN(@total:=json_sum_merge(col2, @total)) INTO @json FROM table2 WHERE col1='", col1val, "'");
    PREPARE stmt FROM @query;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
    SELECT col1val AS col1, @total AS col2;
  END LOOP col1_loop;
END

举个稍微大一点的例子:

col1    col2    
aaa     {"key1": 1, "key2": 3}
bbb     {"key1": 4, "key2": 2}
aaa     {"key1": 50, "key3": 0}
ccc     {"key2": 5, "key3": 1, "key4": 3}
bbb     {"key1": 5, "key2": 1, "key5": 3}

CALL count_keys() 产生:

col1    col2    
aaa     {"key1": 51, "key2": 3, "key3": 0}
bbb     {"key1": 9, "key2": 3, "key5": 3}
ccc     {"key2": 5, "key3": 1, "key4": 3}

请注意,我在过程中调用了表 table2,您需要对其进行编辑(在两个查询中)以适合。

【讨论】:

  • 您确定吗,Example_1 与其他 Example 架构的 ..相比没有优势?
  • 对不起,我没有说清楚,示例1实际上是最容易使用的。示例 2 是第二简单的,示例 3 和 4 都很难使用。
【解决方案2】:

我相信这样的事情是可行的。

SELECT SUM(col2->>"$.key1"), SUM(col2->>"$.key2") FROM your_table GROUP BY col1

【讨论】:

  • 但是我应该知道准备查询的键:(
【解决方案3】:

“简短示例”的 SQL:

SELECT col1,
       JSON_OBJECT('key1', SUM(value1), 'key2', SUM(value2)) AS col2
FROM
    (SELECT col1,
            JSON_EXTRACT(col2, '$.key1') AS value1,
            JSON_EXTRACT(col2, '$.key2') AS value2
     FROM tbl) subq
GROUP BY col1;

【讨论】:

  • 谢谢!但我需要一个使用通用名称的动态查询。但是在您的示例中,您提到了“key1 和 key2”,它是静态信息,每当我们创建查询时都必须添加/记住。我认为 Example_1 是不可能的,因此在问题中提到了通用名称 key_namekey_value 的其他示例。
【解决方案4】:

Example_3 的解决方案:

DROP TABLE IF EXISTS jsondata;
CREATE TABLE jsondata (json JSON, col varchar(11));

INSERT INTO jsondata VALUES
('[{"key_name" : "key1", "key_value" : 1}, {"key_name" : "key2", "key_value" : 3}]', 'aaa'),
('[{"key_name" : "key1", "key_value" : 0}, {"key_name" : "key3", "key_value" : 2}]', 'bbb'),
('[{"key_name" : "key1", "key_value" : 50}, {"key_name" : "key2", "key_value" : 0}]', 'aaa');

DROP FUNCTION IF EXISTS json_sum_by_col;
CREATE FUNCTION json_sum_by_col(col varchar(100)) RETURNS JSON
BEGIN
    DECLARE i INT DEFAULT 0;
    DECLARE done INT DEFAULT FALSE;
    DECLARE select_values JSON;
    DECLARE temp_result JSON;
    DECLARE json_result JSON DEFAULT '[]';
    DECLARE temp_key varchar(11);
    DECLARE temp_value int;

    DECLARE curs CURSOR FOR SELECT json FROM jsondata WHERE jsondata.col = col;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    OPEN curs;
        read_loop: LOOP
            SET i = 0;
            FETCH curs INTO select_values;

            IF done THEN
              LEAVE read_loop;
            END IF;

            WHILE i < JSON_LENGTH(select_values) DO
                -- extract key and value for i element
                SET temp_key = JSON_EXTRACT(JSON_EXTRACT(select_values, CONCAT('$[',i,']')), '$.key_name');
                SET temp_value = JSON_EXTRACT(JSON_EXTRACT(select_values, CONCAT('$[',i,']')), '$.key_value');

                -- search json_result for key
                SET @search = JSON_SEARCH(json_result, 'one', JSON_UNQUOTE(temp_key));
                IF @search IS NOT NULL THEN
                    -- if exists add to existing value
                    SET @value_path = JSON_UNQUOTE(REPLACE(@search, 'name', 'value'));
                    SET temp_value = temp_value + JSON_EXTRACT(json_result, @value_path);
                    SET json_result = JSON_REPLACE(json_result, @value_path, temp_value);
                ELSE
                    -- else attach it to json_result
                    SET temp_result = JSON_OBJECT("key_name", JSON_UNQUOTE(temp_key), "key_value", temp_value);
                    SET json_result = JSON_INSERT(json_result, CONCAT('$[',JSON_LENGTH(json_result),']'), temp_result);
                END IF;

                SELECT i + 1 INTO i;
            END WHILE;
        END LOOP;
    CLOSE curs;

    RETURN json_result;
END;

SELECT col, json_sum_by_col(col) FROM jsondata GROUP BY col;

You can run it here

【讨论】:

    猜你喜欢
    • 2021-03-10
    • 2023-02-24
    • 1970-01-01
    • 1970-01-01
    • 2020-11-26
    • 2017-06-26
    • 2016-01-10
    • 2016-05-03
    • 1970-01-01
    相关资源
    最近更新 更多