sql >> Databasteknik >  >> RDS >> Mysql

MySQL SUM json-värden grupperade efter json-nycklar

TL;DR: ja, det kan göras utan att känna till nyckelnamnen i förväg, och inget av de alternativa dataformaten har någon fördel jämfört med originalet.

Detta kan göras utan att känna till nyckelnamnen i förväg, men det är smärtsamt... i princip måste du titta på varje värde i tabellen för att bestämma uppsättningen av distinkta nycklar i tabellen innan du kan summera dem. På grund av detta krav, och det faktum att de alternativa dataformaten alla kan ha flera nycklar per post, finns det ingen fördel med att använda någon av dem.

Eftersom du måste leta efter alla distinkta nycklar är det lika enkelt att göra summorna medan du letar efter dem. Denna funktion och procedur tillsammans kommer att göra det. Funktionen, json_merge_sum , tar två JSON-värden och slår samman dem, summerar värdena där en nyckel förekommer i båda värdena, t.ex.

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

Utdata:

{"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 , utför motsvarigheten till GROUP BY klausul. Den hittar alla distinkta värden för col1 i tabellen och anropar sedan json_sum_merge för varje rad som har värdet col1 . Observera att radvalsfrågan utför en SELECT ... INTO en dummyvariabel så att ingen utdata genereras, och använder en MIN() för att säkerställa att det bara finns ett resultat (så att det kan tilldelas en variabel).

Proceduren:

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 ett lite större exempel:

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

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

Observera att jag har kallat tabellen table2 i proceduren måste du redigera det (i båda frågorna) så att det passar.



  1. Anslutningspoolning i .NET/SQL-server?

  2. UniVers tips

  3. Är sp_-prefixet fortfarande ett nej-nej?

  4. Hur använder man befintlig Oracle-sekvens för att generera id i viloläge?