TL; DR: có, nó có thể được thực hiện mà không cần biết trước các tên khóa và không có định dạng dữ liệu thay thế nào có bất kỳ lợi thế nào so với định dạng ban đầu.
Điều này có thể được thực hiện mà không cần biết trước các tên khóa nhưng thật khó ... về cơ bản bạn phải xem xét mọi giá trị trong bảng để xác định tập hợp các khóa riêng biệt trong bảng trước khi bạn có thể tính tổng chúng. Do yêu cầu này và thực tế là các định dạng dữ liệu thay thế đều có thể có nhiều khóa cho mỗi mục nhập, nên không có lợi thế khi sử dụng bất kỳ định dạng nào trong số chúng.
Vì bạn phải tìm tất cả các khóa riêng biệt, nên việc tính tổng trở nên dễ dàng khi bạn đang tìm kiếm chúng. Hàm và thủ tục này kết hợp với nhau sẽ thực hiện điều đó. Hàm, json_merge_sum
, nhận hai giá trị JSON và hợp nhất chúng, tổng các giá trị mà khóa xuất hiện trong cả hai giá trị, ví dụ:
SELECT json_sum_merge('{"key1": 1, "key2": 3}', '{"key3": 1, "key2": 2}')
Đầu ra:
{"key1": 1, "key2": 5, "key3": 1}
Mã chức năng:
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
Thủ tục, count_keys
, thực hiện tương đương với GROUP BY
mệnh đề. Nó tìm thấy tất cả các giá trị riêng biệt của col1
trong bảng và sau đó gọi json_sum_merge
cho mỗi hàng có giá trị đó là col1
. Lưu ý rằng truy vấn chọn hàng thực hiện SELECT ... INTO
một biến giả để không có đầu ra nào được tạo và sử dụng MIN()
để đảm bảo chỉ có một kết quả (để nó có thể được gán cho một biến).
Thủ tục:
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
Đối với một ví dụ lớn hơn một chút:
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()
sản xuất:
col1 col2
aaa {"key1": 51, "key2": 3, "key3": 0}
bbb {"key1": 9, "key2": 3, "key5": 3}
ccc {"key2": 5, "key3": 1, "key4": 3}
Lưu ý rằng tôi đã gọi bảng là table2
trong thủ tục, bạn sẽ cần phải chỉnh sửa điều đó (trong cả hai truy vấn) cho phù hợp.