sql >> Database >  >> RDS >> Mysql

MySQL SUM json-waarden gegroepeerd op json-sleutels

TL;DR: ja, het kan worden gedaan zonder de sleutelnamen van tevoren te kennen, en geen van de alternatieve gegevensformaten heeft enig voordeel ten opzichte van het origineel.

Dit kan worden gedaan zonder de sleutelnamen van tevoren te kennen, maar het is pijnlijk... in principe moet je naar elke waarde in de tabel kijken om de reeks verschillende sleutels in de tabel te bepalen voordat je ze kunt optellen. Vanwege deze vereiste en het feit dat de alternatieve gegevensindelingen allemaal meerdere sleutels per item kunnen hebben, heeft het geen voordeel om ze te gebruiken.

Omdat je moet zoeken naar alle verschillende sleutels, is het net zo eenvoudig om de sommen te maken terwijl je ernaar zoekt. Deze functie en procedure samen zullen dat doen. De functie, json_merge_sum , neemt twee JSON-waarden en voegt ze samen, waarbij de waarden worden opgeteld waar een sleutel in beide waarden voorkomt, bijvoorbeeld

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

Uitgang:

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

De functiecode:

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

De procedure, count_keys , voert het equivalent uit van de GROUP BY clausule. Het vindt alle afzonderlijke waarden van col1 in de tabel en roept vervolgens json_sum_merge . aan voor elke rij met de waarde col1 . Merk op dat de rijselectiequery een SELECT ... INTO . uitvoert een dummy variabele zodat er geen uitvoer wordt gegenereerd, en gebruikt een MIN() om ervoor te zorgen dat er maar één resultaat is (zodat het aan een variabele kan worden toegewezen).

De procedure:

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

Voor een iets groter voorbeeld:

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

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

Opmerking ik heb de tabel table2 . genoemd in de procedure moet je dat (in beide zoekopdrachten) aanpassen.



  1. TypeError:'int' object ondersteunt geen indexering

  2. Hoe de database te controleren op een niet-geroot Android-apparaat?

  3. Hoe lege spaties in null-waarden om te zetten met behulp van SQL Server?

  4. hoe een op een blob gebaseerd bestand te downloaden van de MySQL-database in PHP?