我通过sequelize迁移以这种方式创建了一个jsonb专栏:

queryInterface.addColumn('Entities', 'summary', {
  type: Sequelize.DataTypes.JSONB,
})

我的Postgres版本:

基于aarch64-未知-linux-gnu的PostgreSQL15.2(Debian15.2-1.pgdg110+1),由GCC(Debian10.2.1-6)编译10.2.1 20210110,64位

我的行通常具有这样的 struct :

{ 
  summary: {
    foobar: 123.456
  }
}

我需要一个将参数与该JSONB摘要的属性进行比较的查询:

where: {
  summary: {
    foobar: {
      [Sequelize.Op.lt]: parseFloat(maxFoobar),
    },
  },
}

Sequelize生成的查询:

SELECT "id", "name", "type", "geometry", "summary", "createdAt", "updatedAt"
FROM "Entities" AS "Entity"
WHERE CAST(("Entity"."summary"#>>'{foobar}') AS DOUBLE PRECISION) < 64
ORDER BY "Entity"."createdAt" DESC
LIMIT 100
OFFSET 0;

对于150,000行,这需要300-400毫秒:

                                                                         QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=29685.31..29696.98 rows=100 width=1475) (actual time=81.465..85.319 rows=100 loops=1)
   Output: id, name, type, geometry, summary, "createdAt", "updatedAt"
   Buffers: shared hit=91259 read=17301
   ->  Gather Merge  (cost=29685.31..34772.56 rows=43602 width=1475) (actual time=81.461..85.310 rows=100 loops=1)
         Output: id, name, type, geometry, summary, "createdAt", "updatedAt"
         Workers Planned: 2
         Workers Launched: 2
         Buffers: shared hit=91259 read=17301
         ->  Sort  (cost=28685.29..28739.79 rows=21801 width=1475) (actual time=76.879..76.889 rows=50 loops=3)
               Output: id, name, type, geometry, summary, "createdAt", "updatedAt"
               Sort Key: "Entity"."createdAt" DESC
               Sort Method: top-N heapsort  Memory: 309kB
               Buffers: shared hit=91259 read=17301
               Worker 0:  actual time=75.047..75.062 rows=65 loops=1
                 Sort Method: top-N heapsort  Memory: 353kB
                 Buffers: shared hit=29140 read=5589
               Worker 1:  actual time=75.044..75.055 rows=63 loops=1
                 Sort Method: top-N heapsort  Memory: 351kB
                 Buffers: shared hit=29074 read=5532
               ->  Parallel Seq Scan on public."Entities" "Entity"  (cost=0.00..27852.07 rows=21801 width=1475) (actual time=0.184..68.223 rows=20705 loops=3)
                     Output: id, name, type, geometry, summary, "createdAt", "updatedAt"
                     Filter: ((("Entity".summary #>> '{foobar}'::text[]))::double precision < '64'::double precision)
                     Rows Removed by Filter: 31618
                     Buffers: shared hit=91145 read=17301
                     Worker 0:  actual time=0.240..66.849 rows=20034 loops=1
                       Buffers: shared hit=29083 read=5589
                     Worker 1:  actual time=0.081..66.737 rows=19791 loops=1
                       Buffers: shared hit=29017 read=5532
 Planning:
   Buffers: shared hit=40
 Planning Time: 0.815 ms

如何在JSONB键上构建索引以运行查询?

推荐答案

考虑到您的筛选条件不是很有 Select 性,只扫描"createdAt"个索引肯定是个不错的 Select .Postgres将快速找到"createdAt"行来满足查询.判断JSON文档的嵌套值总是会增加成本(特别是在值很大的情况下).

这一量身定制的部分索引降低了成本:

CREATE INDEX ON "Entities" ("createdAt" DESC)
WHERE (summary->>'foobar')::float8 < 64;

添加一个(冗余的)生成的列,并基于该列创建索引将会有更多帮助:

ALTER TABLE "Entities"
ADD COLUMN foobar float8 GENERATED ALWAYS AS ((summary->>'foobar')::float8) STORED;

CREATE INDEX ON "Entities" ("createdAt" DESC)
WHERE foobar < 64;

请参见:

根据完整的情况和缺失的信息,一个更通用的索引可能就足够好了--并有更多的用途:

CREATE INDEX ON "Entities" ("createdAt");

If your pagination goes deep, consider upgrading from LIMIT / OFFSET to "ROW value comparsion", a.k.a. "keyset pagination". 请参见:

Postgresql相关问答推荐

在PL/pgSQL中使用循环时缓存

复合索引列的顺序导致不同的计划

使函数内部动态插入更具可读性

在 Postgres 中,部分索引比普通索引需要更多的时间和成本来执行

PostgreSQL 不删除旧的 WAL 档案

Postgres 将分区附加到表的时间太长.想明白为什么

如何使用 SQLAlchemy 将列默认设置为 PostgreSQL 函数?

如何在 Postgres 中的列上删除唯一约束?

从左连接更新 Postgres

Postgres 图像未创建数据库

plpgsql:使用 2 个 OUT 参数调用函数

在 PostgreSQL 中的表上禁用 DELETE?

在 PostgreSQL 中跳过每 n 个结果行

无法在 postgresql hibernate 中使用名为user的表

全文的 Postgresql 前缀通配符

PostgreSQL - 必须出现在 GROUP BY 子句中或在聚合函数中使用

在 postgresql 中对使用 array_agg 创建的文本聚合进行排序

Android 的 JDBC 与 Web 服务

从 5 分钟前的表中获取行

如何在不丢失openproject数据的情况下将postgresql数据库从10升级到12