sql >> Databáze >  >> RDS >> Mysql

MySQL SUM Hodnoty json seskupené podle klíčů json

TL;DR: ano, lze to provést bez znalosti názvů klíčů předem a žádný z alternativních datových formátů nemá oproti původnímu žádnou výhodu.

To lze provést bez znalosti názvů klíčů předem, ale je to bolestivé... v podstatě se musíte podívat na každou hodnotu v tabulce, abyste určili sadu odlišných klíčů v tabulce, než je můžete sečíst. Vzhledem k tomuto požadavku a skutečnosti, že všechny alternativní datové formáty mohou mít více klíčů na položku, není použití žádného z nich výhodné.

Vzhledem k tomu, že musíte hledat všechny odlišné klíče, je stejně snadné dělat součty, když je hledáte. Tato funkce a postup to společně zajistí. Funkce json_merge_sum , vezme dvě hodnoty JSON a sloučí je sečtením hodnot, kde se klíč vyskytuje v obou hodnotách, např.

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

Výstup:

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

Kód funkce:

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 , provede ekvivalent GROUP BY doložka. Najde všechny odlišné hodnoty col1 v tabulce a poté zavolá json_sum_merge pro každý řádek, který má hodnotu col1 . Všimněte si, že dotaz pro výběr řádku provádí SELECT ... INTO fiktivní proměnná, takže není generován žádný výstup, a používá MIN() aby bylo zajištěno, že existuje pouze jeden výsledek (takže jej lze přiřadit proměnné).

Postup:

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

Pro trochu větší příklad:

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}

Poznámka:Tabulku jsem nazval table2 v proceduře to budete muset upravit (v obou dotazech), aby vyhovovaly.



  1. Jak EXP() funguje v MariaDB

  2. Něco podobného jako Hibernate v PHP?

  3. Jak načíst všechny řádky v mé DB?

  4. Mysql NENÍ IN a NEEXISTUJE stejné?