13

我正在使用 PostgreSQL 9.4 和一个teams包含jsonb名为json. 我正在寻找一个查询,我可以在其中获取所有拥有 Players 的球队34以及7他们的球员数组。

该表包含两行,其中包含以下json数据:

第一排:

{
    "id": 1,
    "name": "foobar",
    "members": {
        "coach": {
            "id": 1,
            "name": "A dude"
        },
        "players": [
            {
                "id": 2,
                "name": "B dude"
            },
            {
                "id": 3,
                "name": "C dude"
            },
            {
                "id": 4,
                "name": "D dude"
            },
            {
                "id": 6,
                "name": "F dude"
            },
            {
                "id": 7,
                "name": "G dude"
            }
        ]
    }
}

第二行:

{
    "id": 2,
    "name": "bazbar",
    "members": {
        "coach": {
            "id": 11,
            "name": "A dude"
        },
        "players": [
            {
                "id": 3,
                "name": "C dude"
            },
            {
                "id": 5,
                "name": "E dude"
            },
            {
                "id": 6,
                "name": "F dude"
            },
            {
                "id": 7,
                "name": "G dude"
            },
            {
                "id": 8,
                "name": "H dude"
            }
        ]
    }
}

查询必须如何才能获得所需的团队列表?我尝试了一个查询,我将从成员玩家创建一个数组jsonb_array_elements(json -> 'members' -> 'players')->'id'并比较它们,但我所能完成的只是一个结果,其中任何比较的玩家 ID 在团队中可用,而不是全部可用。

4

2 回答 2

16

您同时面临两项重要的任务。我很感兴趣。

  • jsonb具有复杂嵌套结构的流程。
  • 对文档类型运行等效的关系除法查询。

首先,为 注册一个行类型jsonb_populate_recordset()。您可以使用 永久创建类型CREATE TYPE,也可以创建临时表以供临时使用(在会话结束时自动删除):

CREATE TEMP TABLE foo(id int);  -- just "id", we don't need "name"

我们只需要id,所以不要包括name根据文档:

未出现在目标行类型中的 JSON 字段将从输出中省略

询问

SELECT t.json->>'id' AS team_id, p.players
FROM   teams t
     , LATERAL (SELECT ARRAY (
         SELECT * FROM jsonb_populate_recordset(null::foo, t.json#>'{members,players}')
         )
       ) AS p(players)
WHERE p.players @> '{3,4,7}';

Postgres 9.3中的SQL Fiddlejson(第 9.4 页尚不可用)。

解释

  • 提取带有玩家记录的 JSON 数组:

    t.json#>'{members,players}'
    
  • 从这些中,我只使用 with 来取消嵌套行id

    jsonb_populate_recordset(null::foo, t.json#>'{members,players}')
    

    ...并立即将它们聚合到 Postgres 数组中,因此我们在基表中每行保留一行:

    SELECT ARRAY ( ... )
    
  • 所有这些都发生在横向连接中:

    , LATERAL (SELECT ... ) AS p(players)
    
  • 立即过滤结果数组以仅保留我们正在寻找的数组 - 使用“包含”数组运算符@>

    WHERE p.players @> '{3,4,7}'
    

瞧。

如果你在一个大表上运行这个查询很多,你可以创建一个假IMMUTABLE函数来提取上面的数组,并基于这个函数创建功能性GIN 索引,以使其超级快。
“假”,因为该函数取决于基础行类型,即目录查找,并且如果更改会更改。(所以确保它不会改变。)类似于这个:

另外:
不要使用类型名称,例如json列名称(即使允许这样做),这会引起棘手的语法错误和令人困惑的错误消息。

于 2015-03-18T05:07:49.883 回答
1

我想做和上面一样的事情。只有其他条件是我必须进行子字符串匹配而不是精确匹配。

这就是我最终要做的(当然灵感来自上面的答案)

SELECT t.json->>'name' AS feature_name, f.features::text
FROM   teams t
 , LATERAL  (
     SELECT * FROM json_populate_recordset(null::foo, t.json#>'{members,features}')
   ) AS f(features)
 WHERE f.features LIKE '%dud%';

如果有任何帮助,请在此处发布。

于 2017-06-23T06:57:33.387 回答