JSON数组中JSON对象的PostgreSQL集合字段

JSON数组中JSON对象的PostgreSQL集合字段,sql,json,postgresql,Sql,Json,Postgresql,我有一张这样的桌子: | id (SERIAL) | game (TEXT) | players (JSONB) | +-------------+-------------+-----------------+ | 1 | chess | [{name: Joe, role: admin}, {name: Mike, role: user}] | | 2 | football | [{name: Foo, role: user}, {n

我有一张这样的桌子:

| id (SERIAL) | game (TEXT) | players (JSONB) |
+-------------+-------------+-----------------+
|      1      | chess       | [{name: Joe, role: admin}, {name: Mike, role: user}] |
|      2      | football    | [{name: Foo, role: user}, {name: Bar, role: user}] |
+-------------+-------------+-----------------+
| id (SERIAL) | game (TEXT) | players (JSONB) |
+-------------+-------------+-----------------+
|      1      | chess       | [{name: Joe, role: user}, {name: Mike, role: user}] |
|      2      | football    | [{name: Foo, role: user}, {name: Bar, role: user}] |
+-------------+-------------+-----------------+
我想将玩家(Joe)的角色设置为特定游戏(国际象棋)中的特定值(用户),因此结果如下所示:

| id (SERIAL) | game (TEXT) | players (JSONB) |
+-------------+-------------+-----------------+
|      1      | chess       | [{name: Joe, role: admin}, {name: Mike, role: user}] |
|      2      | football    | [{name: Foo, role: user}, {name: Bar, role: user}] |
+-------------+-------------+-----------------+
| id (SERIAL) | game (TEXT) | players (JSONB) |
+-------------+-------------+-----------------+
|      1      | chess       | [{name: Joe, role: user}, {name: Mike, role: user}] |
|      2      | football    | [{name: Foo, role: user}, {name: Bar, role: user}] |
+-------------+-------------+-----------------+

是否可以通过一个查询来实现这一点?

这可以通过在每次更新时重新创建json数组来实现

用于表创建和示例数据插入的SQL:

CREATE TABLE test_table(
  id BIGSERIAL PRIMARY KEY ,
  game TEXT,
  players JSONB
);

INSERT INTO test_table(game, players)
    VALUES
      ('chess', '[{"name": "Joe", "role": "admin"}, {"name": "Mike", "role": "user"}]'),
      ('football', '[{"name": "Foo", "role": "user"}, {"name": "Bar", "role": "user"}]');
插入的数据:

+----+----------+----------------------------------------------------------------------+
| id |   game   |                               players                                |
+----+----------+----------------------------------------------------------------------+
|  1 | chess    | [{"name": "Joe", "role": "admin"}, {"name": "Mike", "role": "user"}] |
|  2 | football | [{"name": "Foo", "role": "user"}, {"name": "Bar", "role": "user"}]   |
+----+----------+----------------------------------------------------------------------+
更新查询:

WITH json_rows AS
(SELECT id, jsonb_array_elements(players) as json_data FROM test_table
WHERE game = 'chess'),
 updated_rows AS (
    SELECT
      id,
      array_to_json(array_agg(
      CASE WHEN json_data -> 'name' = '"Joe"'
        THEN jsonb_set(json_data, '{role}', '"user"')
      ELSE json_data END)) as updated_json
    FROM json_rows
    GROUP BY id
)
UPDATE test_table SET players = u.updated_json
FROM updated_rows u
WHERE test_table.id = u.id;
查询结果:

+----+----------+---------------------------------------------------------------------+
| id |   game   |                               players                               |
+----+----------+---------------------------------------------------------------------+
|  2 | football | [{"name": "Foo", "role": "user"}, {"name": "Bar", "role": "user"}]  |
|  1 | chess    | [{"name": "Joe", "role": "user"}, {"name": "Mike", "role": "user"}] |
+----+----------+---------------------------------------------------------------------+
查询的工作方式如下:

  • 将json数组转换为json行,并通过
    game
    属性对其进行过滤。这是通过创建
    json_行
    CTE来完成的

  • 更新找到用户“Joe”的json行中的json数据

  • 获得新的json值后,只需根据id进行更新


  • 注意:如您所见,在当前的实现中,json数组被重新创建(仅在需要更新的行中)。这可能会导致数组中元素的顺序发生变化

    我知道这会发生的。。。谢谢,但我知道你应该避免在关系数据库中使用JSON。我的用例非常特殊,需要JSON,否则我不会使用它。