来自 JSON 数组的 mySQL WHERE IN

Mat*_*son 9 mysql arrays json

我有一个包含 JSON 数据的表,以及一个为每一行提取一组 ID 的语句......

SELECT items.data->"$.matrix[*].id" as ids
FROM items
Run Code Online (Sandbox Code Playgroud)

这会导致类似..

+------------+
|    ids     |
+------------+
| [1,2,3]    |
+------------+
Run Code Online (Sandbox Code Playgroud)

接下来我想从另一个表中选择另一个表的 ID 在数组中,类似于WHERE id IN ('1,2,3')但使用 JSON 数组...

类似的东西...

SELECT * FROM other_items 
WHERE id IN ( 
  SELECT items.data->"$.matrix[*].id" FROM items
);
Run Code Online (Sandbox Code Playgroud)

但它需要一些 JSON 魔法,我无法解决......

123*_*4ru 10

从 MySQL 8.0.13 开始,有MEMBER OF运算符,它完全可以满足您的需求。

不过,该查询应该重写为 的形式JOIN

SELECT o.* FROM other_items o
JOIN items i ON o.id MEMBER OF(i.data->>'$.id')
Run Code Online (Sandbox Code Playgroud)

如果您希望查询具有更好的性能,请考虑在 JSON 列上使用多值索引。


MEMBER OF()下面的例子可以更清楚地解释of 的使用:

CREATE TABLE items ( data JSON );

INSERT INTO items
SET data = '{"id":[1,2,3]}';
Run Code Online (Sandbox Code Playgroud)

这就是确定 JSON 数组中是否存在该值的方法:

SELECT * FROM items
WHERE 3 MEMBER OF(data->>'$.id');
Run Code Online (Sandbox Code Playgroud)
+-------------------+
| data              |
+-------------------+
| {"id": [1, 2, 3]} |
+-------------------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

请注意,与常规比较不同,在这种情况下值的类型很重要。如果以字符串形式传递,则不会匹配:

+-------------------+
| data              |
+-------------------+
| {"id": [1, 2, 3]} |
+-------------------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

Empty set (0.00 sec)

尽管定期比较会返回1

SELECT * FROM items
WHERE "3" MEMBER OF(data->>'$.id');
Run Code Online (Sandbox Code Playgroud)
+---------+
| 3 = "3" |
+---------+
|       1 |
+---------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)


Sim*_*ley 6

下面是一个完整的答案。您可能需要'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), '$')
Run Code Online (Sandbox Code Playgroud)

您应该会看到以下结果:

结果


Coi*_*sox 5

在 MySQL 中引入 JSON 之前,我使用的是:

  1. 你的原始数据:[1,2,3]

  2. 将逗号替换为 '][' 后:[1][2][3]

  3. 将你的 id 括在“[]”中

  4. 然后使用 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, ']'), '%')
Run Code Online (Sandbox Code Playgroud)

为什么要换行成“[]”

'[12,23,34]' LIKE '%1%' --> true
'[12,23,34]' LIKE '%12%' --> true
Run Code Online (Sandbox Code Playgroud)

如果换行成'[]'

'[12][23][34]' LIKE '%[1]%' --> false
'[12][23][34]' LIKE '%[12]%' --> true
Run Code Online (Sandbox Code Playgroud)