Mysql
 sql >> Base de données >  >> RDS >> Mysql

Valeurs json MySQL SUM regroupées par clés json

TL;DR : oui, cela peut être fait sans connaître à l'avance les noms des clés, et aucun des formats de données alternatifs n'a d'avantage sur l'original.

Cela peut être fait sans connaître à l'avance les noms des clés, mais c'est pénible... en gros, vous devez examiner chaque valeur du tableau pour déterminer l'ensemble des clés distinctes du tableau avant de pouvoir les additionner. En raison de cette exigence et du fait que les formats de données alternatifs peuvent tous avoir plusieurs clés par entrée, il n'y a aucun avantage à utiliser l'un d'entre eux.

Puisque vous devez rechercher toutes les clés distinctes, il est aussi facile de faire les sommes pendant que vous les recherchez. Cette fonction et cette procédure le feront ensemble. La fonction, json_merge_sum , prend deux valeurs JSON et les fusionne, en additionnant les valeurs où une clé apparaît dans les deux valeurs, par exemple

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

Sortie :

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

Le code de la fonction :

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

La procédure, count_keys , effectue l'équivalent du GROUP BY clause. Il trouve toutes les valeurs distinctes de col1 dans la table puis appelle json_sum_merge pour chaque ligne qui a cette valeur de col1 . Notez que la requête de sélection de ligne effectue un SELECT ... INTO une variable factice donc aucune sortie n'est générée et utilise un MIN() pour s'assurer qu'il n'y a qu'un seul résultat (afin qu'il puisse être affecté à une variable).

La procédure :

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

Pour un exemple un peu plus grand :

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

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

Notez que j'ai appelé la table table2 dans la procédure, vous devrez modifier cela (dans les deux requêtes) en fonction.