jsonb 查询数组中的嵌套对象
jsonb query with nested objects in an array
我将 PostgreSQL 9.4 与 table teams
一起使用,其中包含名为 json
的 jsonb
列。我正在寻找一个查询,我可以在其中获得所有在其球员数组中拥有球员 3
、4
和 7
的球队。
table 包含两行 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 在团队中可用,而不是所有这些。
您同时面临两项重要任务。我很好奇。
- 处理具有复杂嵌套结构的
jsonb
。
- 运行相当于文档类型的关系划分查询。
首先,为 jsonb_populate_recordset()
注册一个行类型。您可以使用 CREATE TYPE
创建一个永久类型,或者创建一个临时 table 供临时使用(在会话结束时自动删除):
CREATE TEMP TABLE foo(id int); -- just "id", we don't need "name"
我们只需要 id
,所以不包括 name
。 Per documentation:
JSON fields that do not appear in the target row type will be omitted from the output
查询
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}';
SQL Fiddle for json
在 Postgres 9.3 中(pg 9.4 尚不可用)。
解释一下
提取 JSON 包含玩家记录的数组:
t.json#>'{members,players}'
从这些中,我只用 id
取消嵌套行:
jsonb_populate_recordset(null::foo, t.json#>'{members,players}')
... 并立即将它们聚合到一个 Postgres 数组中,因此我们在基础 table:
中每行保留一行
SELECT ARRAY ( ... )
所有这些都发生在横向连接中:
, LATERAL (SELECT ... ) AS p(players)
立即过滤结果数组以仅保留我们正在寻找的数组 - 使用 "contains" array operator @>
:
WHERE p.players @> '{3,4,7}'
瞧瞧。
如果你 运行 这个查询在一个大的 table 上很多,你可以创建一个假的 IMMUTABLE
函数来提取上面的数组并创建函数 GIN 索引 基于这个函数使这个超级快。
"Fake" 因为该函数取决于基础行类型,即目录查找,并且如果发生变化也会发生变化。 (所以确保它不会改变。)类似于这个:
- Index for finding an element in a JSON array
旁白:
不要使用像 json
这样的类型名称作为列名称(即使允许这样做),这会导致棘手的语法错误和令人困惑的错误消息。
我想和上面的一样。唯一的其他条件是我必须进行子字符串匹配而不是精确匹配。
这就是我最终所做的(当然是从上面的答案中得到启发)
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%';
如果对您有帮助,请在此发布。
https://www.postgresql.org/docs/release/14.0/
Subscripting can now be applied to any data type for which it is a
useful notation, not only arrays. In this release, the jsonb and
hstore types have gained subscripting operators.
Let's use subscripting feature in postgresql 14.
with a as(
select data['id'] as teamid,
(jsonb_array_elements( data['members']['players']))['id'] as playerid
from teams), b as( select teamid, array_agg(playerid) as playerids from a group by 1)
select b.* from b where b.playerids @> '{3,4,7}';
returns:
teamid | playerids
--------+-------------
1 | {2,3,4,6,7}
我将 PostgreSQL 9.4 与 table teams
一起使用,其中包含名为 json
的 jsonb
列。我正在寻找一个查询,我可以在其中获得所有在其球员数组中拥有球员 3
、4
和 7
的球队。
table 包含两行 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 在团队中可用,而不是所有这些。
您同时面临两项重要任务。我很好奇。
- 处理具有复杂嵌套结构的
jsonb
。 - 运行相当于文档类型的关系划分查询。
首先,为 jsonb_populate_recordset()
注册一个行类型。您可以使用 CREATE TYPE
创建一个永久类型,或者创建一个临时 table 供临时使用(在会话结束时自动删除):
CREATE TEMP TABLE foo(id int); -- just "id", we don't need "name"
我们只需要 id
,所以不包括 name
。 Per documentation:
JSON fields that do not appear in the target row type will be omitted from the output
查询
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}';
SQL Fiddle for json
在 Postgres 9.3 中(pg 9.4 尚不可用)。
解释一下
提取 JSON 包含玩家记录的数组:
t.json#>'{members,players}'
从这些中,我只用
id
取消嵌套行:jsonb_populate_recordset(null::foo, t.json#>'{members,players}')
... 并立即将它们聚合到一个 Postgres 数组中,因此我们在基础 table:
中每行保留一行SELECT ARRAY ( ... )
所有这些都发生在横向连接中:
, LATERAL (SELECT ... ) AS p(players)
立即过滤结果数组以仅保留我们正在寻找的数组 - 使用 "contains" array operator
@>
:WHERE p.players @> '{3,4,7}'
瞧瞧。
如果你 运行 这个查询在一个大的 table 上很多,你可以创建一个假的 IMMUTABLE
函数来提取上面的数组并创建函数 GIN 索引 基于这个函数使这个超级快。
"Fake" 因为该函数取决于基础行类型,即目录查找,并且如果发生变化也会发生变化。 (所以确保它不会改变。)类似于这个:
- Index for finding an element in a JSON array
旁白:
不要使用像 json
这样的类型名称作为列名称(即使允许这样做),这会导致棘手的语法错误和令人困惑的错误消息。
我想和上面的一样。唯一的其他条件是我必须进行子字符串匹配而不是精确匹配。
这就是我最终所做的(当然是从上面的答案中得到启发)
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%';
如果对您有帮助,请在此发布。
https://www.postgresql.org/docs/release/14.0/
Subscripting can now be applied to any data type for which it is a useful notation, not only arrays. In this release, the jsonb and hstore types have gained subscripting operators. Let's use subscripting feature in postgresql 14.
with a as(
select data['id'] as teamid,
(jsonb_array_elements( data['members']['players']))['id'] as playerid
from teams), b as( select teamid, array_agg(playerid) as playerids from a group by 1)
select b.* from b where b.playerids @> '{3,4,7}';
returns:
teamid | playerids
--------+-------------
1 | {2,3,4,6,7}