Chr*_*nes 21 mysql json database-normalization
更新:现在可以通过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()它到一个逗号分隔的字符串?
换句话说,我知道我可以这样做:
SET @j = '[1, 2, 3]';
SELECT GROUP_CONCAT(JSON_EXTRACT(@j, CONCAT('$[', x.n, ']'))) AS val
FROM
(
SELECT 0 AS n
UNION
SELECT 1 AS n
UNION
SELECT 2 AS n
UNION
SELECT 3 AS n
UNION
SELECT 4 AS n
UNION
SELECT 5 AS n
) x
WHERE x.n < JSON_LENGTH(@j);
Run Code Online (Sandbox Code Playgroud)
但这伤害了我的眼睛.而我的心.
我该怎么做:
SET @j = '[1, 2, 3]';
SELECT GROUP_CONCAT(JSON_EXTRACT(@j, '$[ * ]'))
Run Code Online (Sandbox Code Playgroud)
...并将它与数组中的值和JSON数组本身连接在一起?
我想我在这里寻找的是某种JSON_SPLIT:
SET @j = '[1, 2, 3]';
SELECT GROUP_CONCAT(val)
FROM
JSON_SPLIT(JSON_EXTRACT(@j, '$[ * ]'), '$')
Run Code Online (Sandbox Code Playgroud)
如果MySQL有一个正确的STRING_SPLIT(val,'separator')表返回函数,我可以破解它(逃避被诅咒),但这也不可用.
小智 28
确实,将规范化为JSON并不是一个好主意,但有时您需要处理JSON数据,并且有一种方法可以将JSON数组提取到查询中的行中.
诀窍是在临时或内联索引表上执行连接,这为JSON数组中的每个非空值提供了一行.即,如果你有一个值为0,1和2的表,你加入一个带有两个条目的JSON数组"fish",那么fish [0]匹配0,结果是一行,fish [1]匹配1,导致第二行,但fish [2]为null,因此它与2不匹配,并且在连接中不产生行.索引表中需要的数字与JSON数据中任何数组的最大长度一样多.这有点像黑客,它和OP的例子一样痛苦,但它非常方便.
示例(需要MySQL 5.7.8或更高版本):
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;
Run Code Online (Sandbox Code Playgroud)
结果是:
+---------+-----+---------+
| rec_num | idx | fishes |
+---------+-----+---------+
| 1 | 0 | "red" |
| 1 | 1 | "blue" |
| 2 | 0 | "one" |
| 2 | 1 | "two" |
| 2 | 2 | "three" |
+---------+-----+---------+
Run Code Online (Sandbox Code Playgroud)
看起来MySQL团队可能会在MySQL 8中添加一个JSON_TABLE函数来使这一切变得更容易.(http://mysqlserverteam.com/mysql-8-0-labs-json-aggregation-functions/)
小智 8
在2018年。我要为这种情况做些什么。
准备一个表,该表的行必须连续不断。
CREATE TABLE `t_list_row` (
`_row` int(10) unsigned NOT NULL,
PRIMARY KEY (`_row`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT t_list_row VALUES (0), (1), (2) .... (65535) big enough;
Run Code Online (Sandbox Code Playgroud)将来可以在行中使用简单的JSON数组。
SET @j = '[1, 2, 3]';
SELECT
JSON_EXTRACT(@j, CONCAT('$[', B._row, ']'))
FROM (SELECT @j AS B) AS A
INNER JOIN t_list_row AS B ON B._row < JSON_LENGTH(@j);
Run Code Online (Sandbox Code Playgroud)这样 就像“克里斯·海恩斯”那样。但是您不需要知道数组大小。
良好:清晰,简短的代码,无需知道数组大小,无需循环,无需调用其他函数将很快。
坏:您还需要一个具有足够行的表。
这是在MySQL 8+中使用JSON_TABLE的方法:
SELECT *
FROM
JSON_TABLE(
'[5, 6, 7]',
"$[*]"
COLUMNS(
Value INT PATH "$"
)
) data;
Run Code Online (Sandbox Code Playgroud)
您还可以通过将带分隔符的字符串并将其转换为JSON字符串,将其用作MySQL否则缺少的常规字符串拆分函数(类似于PG的regexp_split_to_table或MSSQL的STRING_SPLIT):
set @delimited = 'a,b,c';
SELECT *
FROM
JSON_TABLE(
CONCAT('["', REPLACE(@delimited, ',', '", "'), '"]'),
"$[*]"
COLUMNS(
Value varchar(50) PATH "$"
)
) data;
Run Code Online (Sandbox Code Playgroud)