sql >> Database teknologi >  >> RDS >> Mysql

MySQL SUM json-værdier grupperet efter json-nøgler

TL;DR: ja, det kan gøres uden at kende nøglenavnene på forhånd, og ingen af ​​de alternative dataformater har nogen fordel i forhold til originalen.

Dette kan gøres uden at kende nøglenavnene på forhånd, men det er smertefuldt... dybest set er du nødt til at se på hver værdi i tabellen for at bestemme sættet af forskellige nøgler i tabellen, før du kan summere dem. På grund af dette krav og det faktum, at de alternative dataformater alle kan have flere nøgler pr. indtastning, er der ingen fordel ved at bruge nogen af ​​dem.

Da du skal lede efter alle de særskilte nøgler, er det lige så nemt at lave summen, mens du leder efter dem. Denne funktion og procedure vil sammen gøre det. Funktionen json_merge_sum , tager to JSON-værdier og fletter dem sammen og summerer værdierne, hvor en nøgle vises i begge værdier, f.eks.

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

Output:

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

Funktionskoden:

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

Proceduren, count_keys , udfører det, der svarer til GROUP BY klausul. Den finder alle de forskellige værdier af col1 i tabellen og kalder derefter json_sum_merge for hver række, der har værdien col1 . Bemærk, at rækkevalgsforespørgslen udfører en SELECT ... INTO en dummy-variabel, så der ikke genereres noget output, og bruger en MIN() for at sikre, at der kun er ét resultat (så det kan tildeles en variabel).

Fremgangsmåden:

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

For et lidt større eksempel:

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

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

Bemærk, at jeg har kaldt tabellen table2 i proceduren skal du redigere det (i begge forespørgsler), så det passer.



  1. Bruger MySQL indeks til sortering?

  2. Tilføjelse af data til en Cloud Firestore-database

  3. Udlejning af biler er lige så enkelt som at køre:En datamodel for et biludlejningsfirma

  4. 2 måder at aktivere ordombrydning i SQLite