Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Wartości json MySQL SUM pogrupowane według kluczy json

TL;DR: tak, można to zrobić bez wcześniejszej znajomości nazw kluczy, a żaden z alternatywnych formatów danych nie ma żadnej przewagi nad oryginałem.

Można to zrobić bez wcześniejszej znajomości nazw kluczy, ale jest to bolesne… w zasadzie musisz spojrzeć na każdą wartość w tabeli, aby określić zestaw odrębnych kluczy w tabeli, zanim będziesz mógł je zsumować. Ze względu na ten wymóg oraz fakt, że wszystkie alternatywne formaty danych mogą mieć wiele kluczy na wpis, korzystanie z żadnego z nich nie jest korzystne.

Ponieważ musisz szukać wszystkich odrębnych kluczy, równie łatwo jest obliczać sumy, gdy ich szukasz. Ta funkcja i procedura razem to zrobią. Funkcja json_merge_sum , pobiera dwie wartości JSON i łączy je, sumując wartości, w których klucz występuje w obu wartościach, np.

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

Wyjście:

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

Kod funkcji:

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

Procedura, count_keys , wykonuje odpowiednik GROUP BY klauzula. Znajduje wszystkie odrębne wartości col1 w tabeli, a następnie wywołuje json_sum_merge dla każdego wiersza, który ma wartość col1 . Zwróć uwagę, że zapytanie wybierające wiersz wykonuje SELECT ... INTO zmienna fikcyjna, więc żadne dane wyjściowe nie są generowane, i używa MIN() aby upewnić się, że jest tylko jeden wynik (aby można go było przypisać do zmiennej).

Procedura:

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

Dla nieco większego przykładu:

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

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

Uwaga Nazwałem tabelę table2 w procedurze będziesz musiał to zmienić (w obu zapytaniach), aby dopasować.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Wyjście UTF-8 z CakePHP

  2. Ogłoszenie MySQL 5.6 EOL

  3. Laravel 4:wielokrotność gdzie z lub w elokwentnym

  4. Zapytanie o aktualizację MySQL z lewym dołączeniem i grupowaniem według

  5. użyj tego samego wyzwalacza dla różnych tabel