TL;DR: ano, lze to provést bez znalosti názvů klíčů předem a žádný z alternativních datových formátů nemá oproti původnímu žádnou výhodu.
To lze provést bez znalosti názvů klíčů předem, ale je to bolestivé... v podstatě se musíte podívat na každou hodnotu v tabulce, abyste určili sadu odlišných klíčů v tabulce, než je můžete sečíst. Vzhledem k tomuto požadavku a skutečnosti, že všechny alternativní datové formáty mohou mít více klíčů na položku, není použití žádného z nich výhodné.
Vzhledem k tomu, že musíte hledat všechny odlišné klíče, je stejně snadné dělat součty, když je hledáte. Tato funkce a postup to společně zajistí. Funkce json_merge_sum
, vezme dvě hodnoty JSON a sloučí je sečtením hodnot, kde se klíč vyskytuje v obou hodnotách, např.
SELECT json_sum_merge('{"key1": 1, "key2": 3}', '{"key3": 1, "key2": 2}')
Výstup:
{"key1": 1, "key2": 5, "key3": 1}
Kód funkce:
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
Procedura, count_keys
, provede ekvivalent GROUP BY
doložka. Najde všechny odlišné hodnoty col1
v tabulce a poté zavolá json_sum_merge
pro každý řádek, který má hodnotu col1
. Všimněte si, že dotaz pro výběr řádku provádí SELECT ... INTO
fiktivní proměnná, takže není generován žádný výstup, a používá MIN()
aby bylo zajištěno, že existuje pouze jeden výsledek (takže jej lze přiřadit proměnné).
Postup:
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
Pro trochu větší příklad:
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()
produkuje:
col1 col2
aaa {"key1": 51, "key2": 3, "key3": 0}
bbb {"key1": 9, "key2": 3, "key5": 3}
ccc {"key2": 5, "key3": 1, "key4": 3}
Poznámka:Tabulku jsem nazval table2
v proceduře to budete muset upravit (v obou dotazech), aby vyhovovaly.