Mysql
 sql >> Datenbank >  >> RDS >> Mysql

MySQL SUM json-Werte gruppiert nach json-Schlüsseln

TL;DR: Ja, es ist möglich, ohne die Schlüsselnamen im Voraus zu kennen, und keines der alternativen Datenformate hat einen Vorteil gegenüber dem Original.

Dies kann getan werden, ohne die Schlüsselnamen im Voraus zu kennen, aber es ist schmerzhaft ... im Grunde müssen Sie sich jeden Wert in der Tabelle ansehen, um die Menge der unterschiedlichen Schlüssel in der Tabelle zu bestimmen, bevor Sie sie summieren können. Aufgrund dieser Anforderung und der Tatsache, dass die alternativen Datenformate alle mehrere Schlüssel pro Eintrag haben können, bringt es keinen Vorteil, einen von ihnen zu verwenden.

Da Sie nach allen unterschiedlichen Schlüsseln suchen müssen, ist es genauso einfach, die Summen zu machen, während Sie danach suchen. Diese Funktion und Prozedur zusammen werden dies tun. Die Funktion json_merge_sum , nimmt zwei JSON-Werte und führt sie zusammen, wobei die Werte summiert werden, wo ein Schlüssel in beiden Werten vorkommt, z. B.

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

Ausgabe:

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

Der Funktionscode:

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

Die Prozedur, count_keys , führt das Äquivalent von GROUP BY aus Klausel. Es findet alle unterschiedlichen Werte von col1 in der Tabelle und ruft dann json_sum_merge auf für jede Zeile, die diesen Wert von col1 hat . Beachten Sie, dass die Zeilenauswahlabfrage ein SELECT ... INTO durchführt eine Dummy-Variable, sodass keine Ausgabe generiert wird, und verwendet ein MIN() um sicherzustellen, dass es nur ein Ergebnis gibt (damit es einer Variablen zugewiesen werden kann).

Das Verfahren:

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

Für ein etwas größeres Beispiel:

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() produziert:

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

Beachten Sie, dass ich die Tabelle table2 genannt habe In der Prozedur müssen Sie das (in beiden Abfragen) entsprechend bearbeiten.