我有一个包含JSON数据的表,以及一个为每行提取ID数组的语句......
SELECT items.data->"$.matrix[*].id" as ids
FROM items
这导致类似......
+------------+
| ids |
+------------+
| [1,2,3] |
+------------+
接下来我想从另一个表中选择其他表的ID在数组中,类似于WHERE id IN ('1,2,3')
但使用JSON数组...
有些事情......
SELECT * FROM other_items
WHERE id IN (
SELECT items.data->"$.matrix[*].id" FROM items
);
但它需要一些JSON魔法,我无法解决它......
答案 0 :(得分:2)
以下是完整的答案。您可能需要在脚本顶部添加'use <db_name>;'
语句。关键是要表明JSON_CONTAINS()可用于实现所需的连接。
DROP TABLE IF EXISTS `tmp_items`;
DROP TABLE IF EXISTS `tmp_other_items`;
CREATE TABLE `tmp_items` (`id` int NOT NULL PRIMARY KEY AUTO_INCREMENT, `data` json NOT NULL);
CREATE TABLE `tmp_other_items` (`id` int NOT NULL, `text` nvarchar(30) NOT NULL);
INSERT INTO `tmp_items` (`data`)
VALUES
('{ "matrix": [ { "id": 11 }, { "id": 12 }, { "id": 13 } ] }')
, ('{ "matrix": [ { "id": 21 }, { "id": 22 }, { "id": 23 }, { "id": 24 } ] }')
, ('{ "matrix": [ { "id": 31 }, { "id": 32 }, { "id": 33 }, { "id": 34 }, { "id": 35 } ] }')
;
INSERT INTO `tmp_other_items` (`id`, `text`)
VALUES
(11, 'text for 11')
, (12, 'text for 12')
, (13, 'text for 13')
, (14, 'text for 14 - never retrieved')
, (21, 'text for 21')
, (22, 'text for 22')
-- etc...
;
-- Show join working:
SELECT
t1.`id` AS json_table_id
, t2.`id` AS joined_table_id
, t2.`text` AS joined_table_text
FROM
(SELECT st1.id, st1.data->'$.matrix[*].id' as ids FROM `tmp_items` st1) t1
INNER JOIN `tmp_other_items` t2 ON JSON_CONTAINS(t1.ids, CAST(t2.`id` as json), '$')
您应该看到以下结果:
答案 1 :(得分:2)
在将JSON引入MySQL之前,我使用以下方法:
您的原始数据:[1,2,3]
用'] ['替换逗号后:[1][2][3]
将您的ID包裹在“ []”中
然后使用REVERSE LIKE代替IN:WHERE '[1][2][3]' LIKE
'%[1]%'
回答您的问题:
SELECT * FROM other_items
WHERE
REPLACE(SELECT items.data->"$.matrix[*].id" FROM items, ',', '][')
LIKE CONCAT('%', CONCAT('[', id, ']'), '%')
为什么要换成'[]'
'[12,23,34]' LIKE '%1%' --> true
'[12,23,34]' LIKE '%12%' --> true
如果换成'[]'
'[12][23][34]' LIKE '%[1]%' --> false
'[12][23][34]' LIKE '%[12]%' --> true
答案 2 :(得分:1)
请注意,可接受的答案不会使用<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.1/jquery.min.js"></script>
<ul id="top-menu">
<li class="active">
<a href="#">Top</a>
</li>
<li>
<a href="#foo">Foo</a>
</li>
<li>
<a href="#bar">Bar</a>
</li>
<li>
<a href="#baz">Baz</a>
</li>
</ul>
<a id="foo">Foo</a>
<a id="bar">Bar</a>
<a id="baz">Baz</a>
上的索引,这会导致较大表的性能下降。
在这种情况下,我通常使用一个tmp_other_items
表,该表包含从0到任意固定数字N(大约一百万以下)的整数,并且我加入该整数表以获取第n个JSON元素:
integers
现在,您可以再次尝试接受答案的查询,这需要大约11秒才能运行(但很简单):
DROP TABLE IF EXISTS `integers`;
DROP TABLE IF EXISTS `tmp_items`;
DROP TABLE IF EXISTS `tmp_other_items`;
CREATE TABLE `integers` (`n` int NOT NULL PRIMARY KEY);
CREATE TABLE `tmp_items` (`id` int NOT NULL PRIMARY KEY AUTO_INCREMENT, `data` json NOT NULL);
CREATE TABLE `tmp_other_items` (`id` int NOT NULL PRIMARY KEY, `text` nvarchar(30) NOT NULL);
INSERT INTO `tmp_items` (`data`)
VALUES
('{ "matrix": [ { "id": 11 }, { "id": 12 }, { "id": 13 } ] }'),
('{ "matrix": [ { "id": 21 }, { "id": 22 }, { "id": 23 }, { "id": 24 } ] }'),
('{ "matrix": [ { "id": 31 }, { "id": 32 }, { "id": 33 }, { "id": 34 }, { "id": 35 } ] }')
;
-- Put a lot of rows in integers (~1M)
INSERT INTO `integers` (`n`)
(
SELECT
a.X
+ (b.X << 1)
+ (c.X << 2)
+ (d.X << 3)
+ (e.X << 4)
+ (f.X << 5)
+ (g.X << 6)
+ (h.X << 7)
+ (i.X << 8)
+ (j.X << 9)
+ (k.X << 10)
+ (l.X << 11)
+ (m.X << 12)
+ (n.X << 13)
+ (o.X << 14)
+ (p.X << 15)
+ (q.X << 16)
+ (r.X << 17)
+ (s.X << 18)
+ (t.X << 19) AS i
FROM (SELECT 0 AS x UNION SELECT 1) AS a
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS b ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS c ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS d ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS e ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS f ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS g ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS h ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS i ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS j ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS k ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS l ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS m ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS n ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS o ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS p ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS q ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS r ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS s ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS t ON TRUE)
;
-- Insert normal rows (a lot!)
INSERT INTO `tmp_other_items` (`id`, `text`)
(SELECT n, CONCAT('text for ', n) FROM integers);
将其与将JSON转换为id(临时)表然后在其上进行JOIN的更快方法进行比较(这会导致即时结果,根据heidiSQL,结果为0.000sec) :
-- Show join working (slow)
SELECT
t1.`id` AS json_table_id
, t2.`id` AS joined_table_id
, t2.`text` AS joined_table_text
FROM
(SELECT st1.id, st1.data->'$.matrix[*].id' as ids FROM `tmp_items` st1) t1
INNER JOIN `tmp_other_items` t2 ON JSON_CONTAINS(t1.ids, CAST(t2.`id` as JSON), '$')
;
最里面的-- Fast
SELECT
i.json_table_id,
t2.id AS joined_table_id,
t2.`text` AS joined_table_text
FROM (
SELECT
j.json_table_id,
-- Don't forget to CAST if needed, so the column type matches the index type
-- Do an "EXPLAIN" and check its warnings if needed
CAST(JSON_EXTRACT(j.ids, CONCAT('$[', i.n - 1, ']')) AS UNSIGNED) AS id
FROM (
SELECT
st1.id AS json_table_id,
st1.data->'$.matrix[*].id' as ids,
JSON_LENGTH(st1.data->'$.matrix[*].id') AS len
FROM `tmp_items` AS st1) AS j
INNER JOIN integers AS i ON i.n BETWEEN 1 AND len) AS i
INNER JOIN tmp_other_items AS t2 ON t2.id = i.id
;
检索JSON ID列表及其长度(用于外部联接)。
第二个内部SELECT接受此ID列表,然后对整数进行JOIN检索每个JSON列表的第n个ID,从而生成一个ID表(而不是JSON表)。
最外面的SELECT现在只需将此id表与包含所需数据的表连接起来。
以下是使用WHERE IN的相同查询,以匹配问题标题:
SELECT
答案 3 :(得分:0)
从MySQL 8.0.13开始,有MEMBER OF运算符,它完全可以满足您的需求。
尽管如此,查询应该以{{1}}的形式重写:
JOIN
如果您希望查询具有更好的性能,请考虑在JSON列上使用multi-valued indexes。
在以下示例中,可以更清楚地说明SELECT o.* FROM other_items o
JOIN items i ON o.id MEMBER OF(i.data->>'$.id')
的使用:
MEMBER OF()
这就是找出值是否存在于JSON数组中的方法:
CREATE TABLE items ( data JSON );
INSERT INTO items
SET data = '{"id":[1,2,3]}';
SELECT * FROM items
WHERE 3 MEMBER OF(data->>'$.id');
请注意,在这种情况下,值类型很重要,与常规比较不同。如果以字符串形式传递它,则将不存在匹配项:
+-------------------+
| data |
+-------------------+
| {"id": [1, 2, 3]} |
+-------------------+
1 row in set (0.00 sec)
SELECT * FROM items
WHERE "3" MEMBER OF(data->>'$.id');
尽管常规比较将返回Empty set (0.00 sec)
:
1
SELECT 3 = "3";