Convert JSON array in MySQL to rows
更新:现在可以通过JSON_TABLE函数在MySQL 8中实现:https://dev.mysql.com/doc/refman/8.0/en/json-table-functions.html
我喜欢MySQL 5.7中的新JSON函数,但是遇到了一个试图将JSON中的值合并到普通表结构中的块。
掌握JSON,从中处理和提取数组等都很简单。始终使用JSON_EXTRACT。但是从JSON数组到行的逆过程呢?也许我对现有的MySQL JSON功能非常关注,但是我无法弄清楚这一点。
例如,假设我有一个JSON数组,想为数组中的每个元素插入一行及其值?我发现的唯一方法是编写一堆JSON_EXTRACT(...'$ [0]')JSON_EXTRACT(...'$ [1]')等并将它们结合在一起。
还是说我有一个JSON数组,想将GROUP_CONCAT()变成一个逗号分隔的字符串?
换句话说,我知道我可以这样做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
但这伤了我的眼睛。还有我的心。
我该怎么做:
1 2 |
...并且将数组中的值与JSON数组本身连接在一起?
我猜我在这里寻找的是某种类似于JSON_SPLIT的代码:
1 2 3 4 5 |
如果MySQL具有适当的STRING_SPLIT(val,'separator')表返回函数,则可以对其进行破解(该死的该死),但这也不可用。
的确,将规范化为JSON并不是一个好主意,但是有时您需要处理JSON数据,并且有一种方法可以将JSON数组提取到查询的行中。
技巧是在索引的临时表或内联表上执行联接,从而为JSON数组中的每个非空值提供一行。即,如果您有一个值分别为0、1和2的表,并连接到具有两个条目的JSON数组" fish",则fish [0]匹配0,结果为一行,fish1匹配1,结果为第二行,但是fish [2]为null,因此它与2不匹配,并且在联接中不产生任何行。索引表中需要的数字与JSON数据中任何数组的最大长度一样多。这有点骇人听闻,和OP的示例一样痛苦,但是非常方便。
示例(需要MySQL 5.7.8或更高版本):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | CREATE TABLE t1 (rec_num INT, jdoc JSON); INSERT INTO t1 VALUES (1, '{"fish": ["red","blue"]}'), (2, '{"fish": ["one","two","three"]}'); SELECT rec_num, idx, JSON_EXTRACT(jdoc, CONCAT('$.fish[', idx, ']')) AS fishes FROM t1 -- Inline table of sequential values to index into JSON array JOIN ( SELECT 0 AS idx UNION SELECT 1 AS idx UNION SELECT 2 AS idx UNION -- ... continue as needed to max length of JSON array SELECT 3 ) AS indexes WHERE JSON_EXTRACT(jdoc, CONCAT('$.fish[', idx, ']')) IS NOT NULL ORDER BY rec_num, idx; |
结果是:
1 2 3 4 5 6 7 8 9 | +---------+-----+---------+ | rec_num | idx | fishes | +---------+-----+---------+ | 1 | 0 |"red" | | 1 | 1 |"blue" | | 2 | 0 |"one" | | 2 | 1 |"two" | | 2 | 2 |"three" | +---------+-----+---------+ |
看来MySQL团队可能在MySQL 8中添加了(MySQL团队添加了
这是在MySQL 8+中使用JSON_TABLE的方法:
您还可以通过使用带分隔符的字符串并将其转换为JSON字符串,将其用作MySQL否则缺少的常规字符串拆分函数(类似于PG的regexp_split_to_table或MSSQL的STRING_SPLIT):
1 2 3 4 5 6 7 8 9 10 11 |
在2018年。我要为这种情况做些什么。
准备一个表,该表的行必须连续不断。
将来可以在行中使用简单的JSON数组。
这样就像"克里斯·海恩斯"那样。但是您不需要知道数组大小。
良好:清晰,简短的代码,无需知道数组大小,无需循环,无需调用其他函数将很快。
坏:您还需要一个具有足够行的表。
注意:如果您正在运行MariaDB,则可以跳过此步骤,而只需使用伪序列表(例如
1 2 3 4 5 6 | CREATE TABLE `pseudo_rows` ( `row` int(10) unsigned NOT NULL, PRIMARY KEY (`row`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT pseudo_rows VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12), (13), (14), (15), (16), (17), (18), (19), (20), (21), (22), (23), (24), (25), (26), (27), (28), (29), (30), (31), (32), (33), (34), (35), (36), (37), (38), (39), (40), (41), (42), (43), (44), (45), (46), (47), (48), (49), (50), (51), (52), (53), (54), (55), (56), (57), (58), (59), (60), (61), (62), (63), (64), (65), (66), (67), (68), (69), (70), (71), (72), (73), (74), (75), (76), (77), (78), (79), (80), (81), (82), (83), (84), (85), (86), (87), (88), (89), (90), (91), (92), (93), (94), (95), (96), (97), (98), (99) |
1 2 3 4 5 6 7 8 | CREATE TABLE `events` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `artists` json DEFAULT NOT NULL, PRIMARY KEY (`id`), ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; INSERT INTO `events` (`id`, `artists`) VALUES ('1', '[{\"id\": 123, \"name\": \"Pink Floyd\"}]'); INSERT INTO `events` (`id`, `artists`) VALUES ('2', '[{\"id\": 456, \"name\": \"Nirvana\"}, {\"id\": 789, \"name\": \"Eminem\"}]'); |
获取所有艺术家的查询,每行一个,如下所示:
1 2 3 4 5 |
结果集是:
1 2 3 4 5 | performer --------- Pink Floyd Nirvana Eminem |
在我的案例中,
如Chris MYSQL所述,它没有
对于输入
1 2 3 4 5 6 | { "requestId":"BARBH17319901529", "van":"0xxxxx91317508", "source":"AxxxS", "txnTime":"15-11-2017 14:08:22" } |
您可以使用:
1 2 3 4 5 6 7 8 |
输出将是:
1 | BARBH17319901529 |
您可以根据需要进行修改。
简单的例子:
1 2 3 4 5 6 7 8 |
我正在一份报告中工作,其中一列中有一个很大的json数组列表。我修改了数据模型以将关系1与*进行存储,而不是将所有内容存储在单个列中。为了执行此过程,由于不知道最大大小,我不得不在存储过程中使用一段时间:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | DROP PROCEDURE IF EXISTS `test`; DELIMITER # CREATE PROCEDURE `test`() PROC_MAIN:BEGIN DECLARE numNotes int; DECLARE c int; DECLARE pos varchar(10); SET c = 0; SET numNotes = (SELECT ROUND ( ( LENGTH(debtor_master_notes) - LENGTH( REPLACE ( debtor_master_notes,"Id","") ) ) / LENGTH("Id") ) AS countt FROM debtor_master order by countt desc Limit 1); DROP TEMPORARY TABLE IF EXISTS debtorTable; CREATE TEMPORARY TABLE debtorTable(debtor_master_id int(11), json longtext, note int); WHILE(c <numNotes) DO SET pos = CONCAT('$[', c, ']'); INSERT INTO debtorTable(debtor_master_id, json, note) SELECT debtor_master_id, JSON_EXTRACT(debtor_master_notes, pos), c+1 FROM debtor_master WHERE debtor_master_notes IS NOT NULL AND debtor_master_notes like '%[%' AND JSON_EXTRACT(debtor_master_notes, pos) IS NOT NULL AND JSON_EXTRACT(debtor_master_notes, pos) IS NOT NULL; SET c = c + 1; END WHILE; SELECT * FROM debtorTable; END proc_main # DELIMITER ; |