PostgreSQL 索引未用于非相等的 JSONB 字段过滤:我错过了什么?

问题描述 投票:0回答:1

我的数据库中有一个名为“mytable”的表,其中包含一个带有 JSONB 数据的“日期”字段,如下所示:

{
  ...
  "myfield": "value1"
}

“myfield”字段当前可以具有值“value1”、“value2”和“value3”。此外,“myfield”可以为空,或者它可能根本不存在。

在我的查询中,我需要查找“myfield”不等于“value1”的行。

select * from mytable where data->>'myfield' <> 'value1'

我在“mytable”表上创建了部分索引以加快查询速度:

CREATE INDEX "idx_mytable_myfield" ON "mytable" USING btree (
  ((data->>'myfield'::text) COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
) WHERE 
  (data->>'myfield'::text)::text <> 'value1'::text
)

但是,查询并未使用该索引。同时,查询的选择性很高,顺序扫描速度明显慢。

如果我重写查询和索引以使用“in”而不是“<>”,则索引将在查询中使用。但这意味着将来添加新值时我必须重写查询和索引:

select * from mytable where (data->>'myfield'::text)::text in ('value2'::text,'value3'::text)

你能帮我理解我做错了什么吗?有没有办法重写查询和/或索引?


提前解决可能出现的问题:

  1. 每次索引更改后我都会运行
    ANALYZE mytable
  2. 查询选择性高。
  3. ChatGPT 询问了这个问题并准确地建议了我正在尝试做的事情。
  4. 我的 PostgreSQL 版本是 12.14。
  5. 由于性能和索引大小的原因,我不想在 data->>'myfield' 上构建非部分索引或在所有数据字段上构建 GIN 索引

附注 我尝试向索引和查询添加附加条件,但没有帮助:

...
and data is not null
and data->'myfield'::text is not null
CREATE INDEX "mytable_myfield_idx" ON "set10"."mytable" USING btree (
  ("data" ->> 'myfield'::text)  COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
)
WHERE ("data" ->> 'myfield'::text <> 'нет');
EXPLAIN (ANALYZE, BUFFERS, VERBOSE, COSTS)
SELECT  
    *
FROM
    mytable
WHERE
    "data" ->> 'myfield'::text <> 'нет'
Seq Scan on set10.mytable  (cost=0.00..2041.22 rows=86911 width=35) (actual time=19.098..35.258 rows=74 loops=1)
  Output: "jiraKey", data, created
  Filter: ((mytable.data ->> 'myfield'::text) <> 'нет'::text)
  Rows Removed by Filter: 87274
  Buffers: shared hit=731
Planning Time: 0.051 ms
Execution Time: 35.278 ms

我的实验表明问题出在“<>”中,优化器在这种情况下不会使用索引。

更新

如果我强制禁用 seqscan - 优化器会正确使用我的索引。由于某种原因,它认为使用 seqscan 更好,但这是错误的

SET enable_seqscan = false;
EXPLAIN (ANALYZE,BUFFERS, COSTS, VERBOSE)
select 
    *
FROM    
    mytable
WHERE
    "data"->>'myfield' <> 'нет' 
Bitmap Heap Scan on set10.mytable  (cost=30.20..2064.86 rows=86911 width=35) (actual time=0.024..0.098 rows=74 loops=1)
  Output: "jiraKey", data, created
  Recheck Cond: ((mytable.data ->> 'myfield'::text) <> 'нет'::text)
  Heap Blocks: exact=52
  Buffers: shared hit=53
  ->  Bitmap Index Scan on mytable_myfield_idx  (cost=0.00..8.47 rows=86911 width=0) (actual time=0.013..0.014 rows=74 loops=1)
        Buffers: shared hit=1
Planning Time: 0.059 ms
Execution Time: 0.128 ms
postgresql sql-execution-plan postgresql-12 query-planner partial-index
1个回答
0
投票

为了让它准确估计行数,您需要这样的表达式索引:

create index on mytable ((data->>'myfield'));

创建后进行ANALYZE。您已经拥有部分形式的表达式索引,但规划器不使用部分索引来派生行计数。

如果您使用的是该软件的现代版本,您可以创建表达式的扩展统计数据:

create statistics asldfj on (data->>'myfield') from mytable;

这将生成与索引相同的统计信息,但没有与索引相同的存储或维护需求。但需要v14以上版本。

© www.soinside.com 2019 - 2024. All rights reserved.