假设我有下parents表:
parents
create table parents ( id integer not null constraint parents_pkey primary key, name text not null, children jsonb not null );
以下结构children的 json数组 在哪里:
children
[ { "name": "child1", "age": 10 }, { "name": "child2", "age": 12 } ]
例如,我需要让所有父母育有10至12岁的孩子。
我创建以下查询:
select distinct p.* from parents p, jsonb_array_elements(p.children) c where (c->>'age')::int between 10 and 12;
当表parents很大时(例如1M记录),它可以很好地工作,但是非常慢。我尝试在children字段上使用’gin’索引,但这无济于事。
那么有没有办法加快这种查询呢?或者,也许有另一种解决方案,使查询/索引对 字段 中 嵌套的JSON阵列 ?
查询计划 :
Unique (cost=1793091.18..1803091.18 rows=1000000 width=306) (actual time=4070.866..5106.998 rows=399947 loops=1) -> Sort (cost=1793091.18..1795591.18 rows=1000000 width=306) (actual time=4070.864..4836.241 rows=497313 loops=1) Sort Key: p.id, p.children, p.name Sort Method: external merge Disk: 186040kB -> Gather (cost=1000.00..1406321.34 rows=1000000 width=306) (actual time=0.892..1354.147 rows=497313 loops=1) Workers Planned: 2 Workers Launched: 2 -> Nested Loop (cost=0.00..1305321.34 rows=416667 width=306) (actual time=0.162..1794.134 rows=165771 loops=3) -> Parallel Seq Scan on parents p (cost=0.00..51153.67 rows=416667 width=306) (actual time=0.075..239.786 rows=333333 loops=3) -> Function Scan on jsonb_array_elements c (cost=0.00..3.00 rows=1 width=0) (actual time=0.004..0.004 rows=0 loops=1000000) Filter: ((((value ->> 'age'::text))::integer >= 10) AND (((value ->> 'age'::text))::integer <= 12)) Rows Removed by Filter: 3 Planning time: 0.218 ms Execution time: 5140.277 ms
第一个直接措施是使查询速度更快:
SELECT * FROM parents p WHERE EXISTS ( SELECT FROM jsonb_array_elements(p.children) c WHERE (c->>'age')::int BETWEEN 10 AND 12 );
EXISTS当多个数组对象匹配时,半联接避免了中间表中行的重复-以及DISTINCT ON在外部查询中的需要。但这只是稍微快一点。
EXISTS
DISTINCT ON
核心问题是您想测试 整数值范围 ,而现有的jsonb运算符不提供此类功能。
jsonb
有多种解决方法。不知道这些,这里有一个“智能”解决方案可以解决给定的示例。诀窍是将范围划分为不同的值,并使用jsonbcontainer运算符@>:
@>
SELECT * FROM parents p WHERE (p.children @> '[{"age": 10}]' OR p.children @> '[{"age": 11}]' OR p.children @> '[{"age": 12}]');
由jsonb_path_opsGIN索引支持:
jsonb_path_ops
CREATE INDEX parents_children_gin_idx ON parents USING gin (children jsonb_path_ops);
但是,如果您的范围跨越了一个不完整的整数值,那么您将需要一些更通用的东西。与 往常一样 ,最佳解决方案取决于整体情况:数据分布,值频率,查询中的典型范围,可能为NULL值,行大小,读/写模式, 每个 jsonb值都具有一个或多个匹配age键吗?…
age