PostgreSQL 在 JSONB[] 上创建索引
PostgreSQL create index on JSONB[]
考虑一个 table 定义如下:
CREATE TABLE test (
id int4 NOT NULL,
tag_counts _jsonb NOT NULL DEFAULT ARRAY[]::jsonb[]
);
INSERT INTO test(id, tag_counts) values(1,array['{"type":1, "count":4}','{"type":2, "count":10}' ]::jsonb[])
如何在 json 键 type
上创建索引以及如何查询它?
编辑:以前,json 键上没有索引,select 查询使用 unnest
操作,如下所示:
select * from (SELECT unnest(tag_counts) as tc
FROM public.test) as t
where tc->'type' = '2';
问题是,如果 table 有大量的行,上面的查询将不仅包括完整的 table 扫描,而且还会过滤每个 jsonb数组。
有一种方法可以对此进行索引,但不确定速度有多快。
如果那是“常规”jsonb
列,您可以使用 where tag_counts @> '[{"type": 2}]'
这样的条件,它可以在该列上使用 GIN 索引。
如果将数组转换为“普通”数组,则可以使用该运算符json值:
select *
from test
where to_jsonb(tag_counts) @> '[{"type": 2}]'
不幸的是,to_jsonb()
未标记为 immutable(我猜是因为其中可能存在时间戳转换),如果您想在索引中使用表达式,这是必需的。
但是对于你的数据,这确实是 immutable,所以我们可以创建一个小包装函数:
create function as_jsonb(p_input jsonb[])
returns jsonb
as
$$
select to_jsonb(p_input);
$$
language sql
immutable;
使用该函数我们可以创建一个索引:
create index on test using gin ( as_jsonb(tag_counts) jsonb_path_ops);
您需要在查询中使用该函数:
select *
from test
where as_jsonb(tag_counts) @> '[{"type": 2}]'
在具有一百万行的 table 上,我得到以下执行计划:
Bitmap Heap Scan on stuff.test (cost=1102.62..67028.01 rows=118531 width=252) (actual time=15.145..684.062 rows=147293 loops=1)
Output: id, tag_counts
Recheck Cond: (as_jsonb(test.tag_counts) @> '[{"type": 2}]'::jsonb)
Heap Blocks: exact=25455
Buffers: shared hit=25486
-> Bitmap Index Scan on ix_test (cost=0.00..1072.99 rows=118531 width=0) (actual time=12.347..12.356 rows=147293 loops=1)
Index Cond: (as_jsonb(test.tag_counts) @> '[{"type": 2}]'::jsonb)
Buffers: shared hit=31
Planning:
Buffers: shared hit=23
Planning Time: 0.444 ms
Execution Time: 690.160 ms
考虑一个 table 定义如下:
CREATE TABLE test (
id int4 NOT NULL,
tag_counts _jsonb NOT NULL DEFAULT ARRAY[]::jsonb[]
);
INSERT INTO test(id, tag_counts) values(1,array['{"type":1, "count":4}','{"type":2, "count":10}' ]::jsonb[])
如何在 json 键 type
上创建索引以及如何查询它?
编辑:以前,json 键上没有索引,select 查询使用 unnest
操作,如下所示:
select * from (SELECT unnest(tag_counts) as tc
FROM public.test) as t
where tc->'type' = '2';
问题是,如果 table 有大量的行,上面的查询将不仅包括完整的 table 扫描,而且还会过滤每个 jsonb数组。
有一种方法可以对此进行索引,但不确定速度有多快。
如果那是“常规”jsonb
列,您可以使用 where tag_counts @> '[{"type": 2}]'
这样的条件,它可以在该列上使用 GIN 索引。
如果将数组转换为“普通”数组,则可以使用该运算符json值:
select *
from test
where to_jsonb(tag_counts) @> '[{"type": 2}]'
不幸的是,to_jsonb()
未标记为 immutable(我猜是因为其中可能存在时间戳转换),如果您想在索引中使用表达式,这是必需的。
但是对于你的数据,这确实是 immutable,所以我们可以创建一个小包装函数:
create function as_jsonb(p_input jsonb[])
returns jsonb
as
$$
select to_jsonb(p_input);
$$
language sql
immutable;
使用该函数我们可以创建一个索引:
create index on test using gin ( as_jsonb(tag_counts) jsonb_path_ops);
您需要在查询中使用该函数:
select *
from test
where as_jsonb(tag_counts) @> '[{"type": 2}]'
在具有一百万行的 table 上,我得到以下执行计划:
Bitmap Heap Scan on stuff.test (cost=1102.62..67028.01 rows=118531 width=252) (actual time=15.145..684.062 rows=147293 loops=1)
Output: id, tag_counts
Recheck Cond: (as_jsonb(test.tag_counts) @> '[{"type": 2}]'::jsonb)
Heap Blocks: exact=25455
Buffers: shared hit=25486
-> Bitmap Index Scan on ix_test (cost=0.00..1072.99 rows=118531 width=0) (actual time=12.347..12.356 rows=147293 loops=1)
Index Cond: (as_jsonb(test.tag_counts) @> '[{"type": 2}]'::jsonb)
Buffers: shared hit=31
Planning:
Buffers: shared hit=23
Planning Time: 0.444 ms
Execution Time: 690.160 ms