查询嵌套在 JSON 数组深处的数据的最有效方法?
Most efficient way to query data nested deep in JSON arrays?
目前我正在针对 JSONB table 编写查询,其中包含 800 万多行。如何以最有效的方式查询 parent
和 friends
对象?
查询(Postgres 9.6):
select distinct id, data->>'_id' jsonID, data->>'email' email, friends->>'name' friend_name, parent->>'name' parent
from temp t
CROSS JOIN jsonb_array_elements(t.data->'friends') friends
CROSS JOIN jsonb_array_elements(friends->'parent') parent
where friends ->> 'name' = 'Chan Franco'
and parent->>'name' = 'Hannah Golden'
示例 DDL(带数据):https://pastebin.com/byN7uyKx
与规范化关系设计一样,您的规则结构化数据会更干净、更小、更快。
就是说,为了使您的设置快很多(如果不如具有匹配索引的规范化设计那么快),添加一个 GIN 索引表达式 data->'friends'
:
CREATE INDEX tbl_data_friends_gin_idx ON tbl USING gin ((data->'friends'));
然后使用包含运算符 @>
:
向我们的查询添加匹配的 WHERE
子句
SELECT DISTINCT -- why DISTINCT ?
id, data->>'_id' AS json_id, data->>'email' AS email
, friends->>'name' AS friend_name, parent->>'name' AS parent
FROM tbl t
CROSS JOIN jsonb_array_elements(t.data->'friends') friends
CROSS JOIN jsonb_array_elements(friends->'parent') parent
<b>WHERE t.data->'friends' @> '[{"name": "Chan Franco", "parent": [{"name": "Hannah Golden"}]}]'</b>
AND friends->>'name' = 'Chan Franco'
AND parent ->>'name' = 'Hannah Golden';
db<>fiddle here
巨大 区别:在索引的帮助下,Postgres 现在可以在 before 取消嵌套整个 table 中每个嵌套的 "friends" 数组。只有 after 已经识别出基础 table 中的匹配行,jsonb_array_elements()
被调用,并且保留具有合格数组元素的结果行。
请注意,搜索表达式必须有效 JSON,匹配 JSON 数组 data->'friends'
的结构 - 包括外括号 []
。但是忽略所有不应用作过滤器的 key/value 对。
相关:
- Index for finding an element in a JSON array
我避免使用 table 名称 temp
,因为这是一个 SQL 关键字,可能会导致混淆错误。请改用名称 tbl
。
目前我正在针对 JSONB table 编写查询,其中包含 800 万多行。如何以最有效的方式查询 parent
和 friends
对象?
查询(Postgres 9.6):
select distinct id, data->>'_id' jsonID, data->>'email' email, friends->>'name' friend_name, parent->>'name' parent
from temp t
CROSS JOIN jsonb_array_elements(t.data->'friends') friends
CROSS JOIN jsonb_array_elements(friends->'parent') parent
where friends ->> 'name' = 'Chan Franco'
and parent->>'name' = 'Hannah Golden'
示例 DDL(带数据):https://pastebin.com/byN7uyKx
与规范化关系设计一样,您的规则结构化数据会更干净、更小、更快。
就是说,为了使您的设置快很多(如果不如具有匹配索引的规范化设计那么快),添加一个 GIN 索引表达式 data->'friends'
:
CREATE INDEX tbl_data_friends_gin_idx ON tbl USING gin ((data->'friends'));
然后使用包含运算符 @>
:
WHERE
子句
SELECT DISTINCT -- why DISTINCT ?
id, data->>'_id' AS json_id, data->>'email' AS email
, friends->>'name' AS friend_name, parent->>'name' AS parent
FROM tbl t
CROSS JOIN jsonb_array_elements(t.data->'friends') friends
CROSS JOIN jsonb_array_elements(friends->'parent') parent
<b>WHERE t.data->'friends' @> '[{"name": "Chan Franco", "parent": [{"name": "Hannah Golden"}]}]'</b>
AND friends->>'name' = 'Chan Franco'
AND parent ->>'name' = 'Hannah Golden';
db<>fiddle here
巨大 区别:在索引的帮助下,Postgres 现在可以在 before 取消嵌套整个 table 中每个嵌套的 "friends" 数组。只有 after 已经识别出基础 table 中的匹配行,jsonb_array_elements()
被调用,并且保留具有合格数组元素的结果行。
请注意,搜索表达式必须有效 JSON,匹配 JSON 数组 data->'friends'
的结构 - 包括外括号 []
。但是忽略所有不应用作过滤器的 key/value 对。
相关:
- Index for finding an element in a JSON array
我避免使用 table 名称 temp
,因为这是一个 SQL 关键字,可能会导致混淆错误。请改用名称 tbl
。