MYSQL搜索JSON值:如何在没有硬编码索引情况下从另一个表中搜索值?

q0qdq0h2  于 2023-06-21  发布在  Mysql
关注(0)|答案(3)|浏览(133)

我在mysql数据库中有两个表,如下所示:
character_classes表:

+--------+----------+
| id | name |
+--------+----------+
| CLA001 | assassin |
| CLA002 | knight |
| CLA003 | vanguard |
+--------+----------+

player_inventories表:

+--------------+----------------------+
| player_id | character_class |
+--------------+----------------------+
| UID000000001 | ["CLA001"] |
| UID000000002 | ["CLA001", "CLA002"] |
| UID000000003 | ["CLA001", "CLA002", "CLA003"] |
+--------------+----------------------+

我正在尝试将player_inventories tbl连接到``character_classes to get character_classes 's names from character_classes`表:

SELECT player_id, (SELECT CONCAT_WS(
', ',
(select name from character_classes where id = JSON_EXTRACT( character_class, '$[0]') ),
(select name from character_classes where id = JSON_EXTRACT( character_class, '$[1]') ),
(select name from character_classes where id = JSON_EXTRACT( character_class, '$[2]') )
) ) as character_class_name
from player_inventories;

但问题是player_inventories tbl的character_class字段中的json项的数量是不同的,它可以是1,2或3甚至更多,所以我需要硬编码索引,即$[0 ],$[1]$[2]以获得相应的名称。
有没有什么方法可以改进这个查询,使我可以在不对索引进行硬编码的情况下快速获得所有字符类的名称?

zhte4eai

zhte4eai1#

不要将类数据存储为JSON。创建一个多对多的表,将玩家IDMap到类ID,然后您可以在查询中使用JOIN来检索所有类名,但必须使用数组索引。
这也使得在类上查询更加容易。查找表中的所有向导比搜索JSON数组要容易得多。
例如:

create table players (
  id int auto_increment primary key,
  name varchar(45)
  );
create table player_inventories(
  player_id int,
  class_id int,
   index (player_id));
create table character_classes (
  id int auto_increment primary key,
  name varchar(45)
  );
  
  Insert players (name) values ('Alice'),('Bob'),('Charles'),('Dave'),('Eve');
  insert character_classes (id,name) values (1,'Wizard'),(2,'Elf'),(3,'Goblin'),(4,'Elder');
  insert player_inventories (player_id, class_id) values (1,1),(1,2),(1,3),(2,3),(2,4)

查询#1

select players.name, group_concat(cc.name) as classes from players 
    join player_inventories pi ON players.id = pi.player_id
    join character_classes cc on pi.class_id = cc.id
    group by players.name;

| 姓名|类|
| - -----|- -----|
| 爱丽丝|精灵、地精、巫师|
| 鲍勃|长老,地精|
View on DB Fiddle

ru9i0ody

ru9i0ody2#

另一种方法是不对索引进行硬编码,您可以使用JSON_CONTAINS函数和一个子查询来匹配character_classes表,从而从player_inventory表中提取所有角色类名。然后应使用GROUP_CONCAT将这些名称合并为一列。使用GROUP BY按player_id对结果进行分组。

SELECT
    pi.player_id,
    GROUP_CONCAT(cc.name SEPARATOR ', ') AS character_class_names
FROM
    player_inventories pi
JOIN
    character_classes cc ON JSON_CONTAINS(pi.character_class, CONCAT('"', cc.id, '"'))
GROUP BY
    pi.player_id;
sigwle7e

sigwle7e3#

考虑@Tangential的答案,如果不能改变表结构,请尝试以下解决方案:
首先使用JSON_TABLE将JSON数组转换为行,然后使用GROUP BY和聚合函数GROUP_CONCAT获得预期的输出:

SELECT p.player_id, GROUP_CONCAT(cc.name SEPARATOR ', ') AS character_class_name
FROM player_inventories p,
     JSON_TABLE( character_class, "$[*]"
         COLUMNS( element text PATH "$" )
     ) d
INNER JOIN character_classes as cc on cc.id = d.element
GROUP BY p.player_id

Demo here

相关问题