UPDATE:现在可以通过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);
但这伤害了我的眼睛。我的心。
我该怎么做:
SET @j = '[1, 2, 3]';
SELECT GROUP_CONCAT(JSON_EXTRACT(@j, '$[ * ]'))
...让它将数组中的值与JSON数组本身连接在一起吗?
我想我在这里寻找的是某种JSON_SPLIT:
SET @j = '[1, 2, 3]';
SELECT GROUP_CONCAT(val)
FROM
JSON_SPLIT(JSON_EXTRACT(@j, '$[ * ]'), '$')
如果MySQL有一个正确的STRING_SPLIT(val,'separator')表返回函数,我可以破解它(逃避被诅咒),但这也不可用。
答案 0 :(得分:22)
确实,将规范化为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;
结果是:
+---------+-----+---------+
| rec_num | idx | fishes |
+---------+-----+---------+
| 1 | 0 | "red" |
| 1 | 1 | "blue" |
| 2 | 0 | "one" |
| 2 | 1 | "two" |
| 2 | 2 | "three" |
+---------+-----+---------+
看起来MySQL团队可能会在MySQL 8中添加一个JSON_TABLE函数来使这一切变得更容易。 (http://mysqlserverteam.com/mysql-8-0-labs-json-aggregation-functions/)
答案 1 :(得分:2)
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;
将来可以在行中使用简单的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);
通过这种方式。就像“克里斯·海恩斯”那样。但您不需要知道数组大小。
好:清晰,简短,简单的代码,无需知道数组大小,没有循环,无需调用其他函数将很快。
错误:您还需要一张表,行数足够。
答案 2 :(得分:2)
在MySQL 8+中使用JSON_TABLE的方法如下:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<select id="TType">
<option value="type1">Full</option>
<option value="type2">Show less</option>
<option value="type3">Show even less</option>
</select>
<input type="text" maxlength="5" id="customer" name="customer">
<input type="text" maxlength="10" id="extra" name="extra">
<select id="city" name="city">
<option value="0"> - </option>
<option value="1">New York</option>
<option value="2">Chicago</option>
<option value="3">London</option>
<option value="4">Athens</option>
</select>
您还可以通过将带分隔符的字符串并将其转换为JSON字符串,将其用作MySQL否则缺少的常规字符串拆分函数(类似于PG的regexp_split_to_table或MSSQL的STRING_SPLIT):
SELECT *
FROM
JSON_TABLE(
'[5, 6, 7]',
"$[*]"
COLUMNS(
Value INT PATH "$"
)
) data;
答案 3 :(得分:1)
在我的情况下,JSON
功能不可用所以我使用了黑客。
正如Chris MYSQL所提到的那样,没有STRING_SPLIT
,但确实有substring_index
。
输入
{
"requestId":"BARBH17319901529",
"van":"0xxxxx91317508",
"source":"AxxxS",
"txnTime":"15-11-2017 14:08:22"
}
您可以使用:
trim(
replace(
substring_index(
substring(input,
locate('requestid',input)
+ length('requestid')
+ 2), ',', 1), '"', '')
) as Requestid`
输出将是:
BARBH17319901529
您可以根据自己的要求进行修改。
答案 4 :(得分:1)
pseudo_rows
,其值的范围为0到99-这些将用作键(如果您的数组有一百多个值,请向seq_0_to_99
中添加更多值)。 注意:如果您正在运行MariaDB,则可以跳过此步骤,而只需使用伪序列表(例如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)
)。
events
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\"}]');
来存储艺术家组:SELECT
JSON_UNQUOTE(JSON_EXTRACT(events.artists, CONCAT('$[', pseudo_rows.row, '].name'))) AS performer
FROM events
JOIN pseudo_rows
HAVING performer IS NOT NULL
查询以每行一位的方式获取所有艺术家,如下所示:
performer
---------
Pink Floyd
Nirvana
Eminem
结果集是:
function Choice() {
x = document.getElementById("nam");
y = document.getElementById("job_contacts");
var index = y.selectedIndex - 1;
if (index < 0) return
x.value = y.options[index].text;
document.getElementById("nam").value = nam[index];
document.getElementById("pho").value = pho[index];
document.getElementById("email").value = email[index];
}
答案 5 :(得分:1)
简单的例子:
select subtotal, sku
from t1,
json_table(t1.refund_line_items,
'$[*]' columns (
subtotal double path '$.subtotal',
sku char(50) path '$.line_item.sku'
)
) refunds
答案 6 :(得分:0)
我正在一份报告中,其中一列中有一个大的json数组列表。我修改了数据模型以将关系1存储到*而不是将所有内容存储在一个列中。为了完成这个过程,我不得不在存储过程中使用一段时间,因为我不知道最大大小:
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 ;
答案 7 :(得分:0)
如果您不能使用JSON_TABLE函数,但是可以使用递归CTE,则可以执行以下操作:
SET @j = '[1, 2, 3]';
WITH RECURSIVE x AS (
/* Anchor, start at -1 in case empty array */
SELECT -1 AS n
UNION
/* Append indexes up to the length of the array */
SELECT x.n + 1
FROM x
WHERE x.n < JSON_LENGTH(@j) - 1
)
/* Use the table of indexes to extract each item and do your GROUP_CONCAT */
SELECT GROUP_CONCAT(JSON_EXTRACT(@j, CONCAT('$[', x.n, ']')))
FROM x
/* This prevents selecting from empty array */
WHERE x.n >= 0
这将为每个数组项生成一个顺序索引表,您可以使用该表使用JSON_EXTRACT来获取值。
答案 8 :(得分:0)
在此处使用此参考https://dba.stackexchange.com/questions/190527/list-json-array-in-mysql-as-rows/243671#243671。
在我的 MySQL 表 AddressIdentifiers
中有一个 JSON 类型的列 Customers
,数据样本如下所示:
[
{
"code": "123",
"identifier": "0219d5780f6b",
"type": "BILLING",
"info": null
},
{
"code": "240",
"identifier": "c81aaf2c5a1f",
"type": "DELIVERY",
"info": null
}
]
要有这样的输出
Identifier AddressType
------------------------
0219d5780f6b BILLING
c81aaf2c5a1f DELIVERY
此解决方案适用于 MySQL 5.7,您必须手动完成该工作。在 MySQL 8.0+ 的情况下,您可以简单地使用 JSON_TABLE
SELECT
JSON_EXTRACT(C.AddressIdentifiers, CONCAT('$[', Numbers.N - 1, '].Identifier')) AS Identifier,
JSON_EXTRACT(C.AddressIdentifiers, CONCAT('$[', Numbers.N - 1, '].AddressType')) AS AddressType,
FROM
(
SELECT @row := @row + 1 AS N FROM
(SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) T2,
(SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) T1,
(SELECT @row:=0) T0
) Numbers -- Natural numbers from 1 to 100
INNER JOIN Customers C ON Numbers.N < JSON_LENGTH(C.AddressIdentifiers)