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
в таблицата и след това извиква json_sum_merge
за всеки ред, който има тази стойност на col1
. Обърнете внимание, че заявката за избор на ред изпълнява 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
в процедурата ще трябва да редактирате това (и в двете заявки), за да отговаря.