我有一个名为orders的表,其中包含一个名为data的列,其数据类型为jsonb,如下所示

"data": [
  "items": [
   {"name": "Peter"},
   {"name": "John"}
  ]
]

这是用于检索唯一名称列表的查询:

    SELECT distinct NameList->'name' AS uniqueNames
FROM orders
         CROSS JOIN jsonb_array_elements(data) itemsData
         CROSS JOIN jsonb_array_elements(itemsData->'items') NameList
WHERE NameList != '[]'

在这里,data有很多其他信息,但我对items感兴趣.因此,如何在NameList != '[]'上创建索引?

推荐答案

如果您正在寻找性能,在PostgreSQL12+jsonpath上应该比这更快,即使没有索引支持.Demo at db<>fiddle:

select distinct jsonb_path_query(data,'$.data[*].items[*].name') 
from orders 
where data @? '$.data[*].items[*].name';

@? "path-exists" operator的功能与您try 执行的操作相同:它会判断该路径下是否有name.关键区别在于它在单个jsonpath表达式中做到了这一点,而且由于有了[*]数组访问器,它不需要取消任何层的嵌套 或者根本不使用集返回函数.

您可以通过让索引缩小不需要判断的行的范围,重新使用上面where中的条件来提高其性能:

create index on orders((data @? '$.data[*].items[*].name'));

你的问题有几个问题

  1. 您提供的jsonb值格式不正确.data是一个键,所以它需要在一个对象中.我猜它的值是一个数组,正如它包含的方括号所示,事实上你正在调用jsonb_array_elements().items也是一个键,所以它需要在一个对象中,作为data数组的一个元素.所以你少了一对外部的花括号,还有一个在items周围:

    {   "data": [
            {   "items": [
                    {   "name": "Peter"},
                    {   "name": "John" }
                ]
            }
        ]
    }
    
  2. 该查询在语法上或逻辑上无效:

    1. 您为jsonb_array_elements()的结果集指定了别名,但没有命名它们的字段/列,因此引用的是它们的整个记录,而不是其中的内容:
      CROSS JOIN jsonb_array_elements(data) itemsData
      CROSS JOIN jsonb_array_elements(itemsData->'items') NameList
      
      定义结果集and的名称及其字段名,以便能够这样使用它:
      CROSS JOIN jsonb_array_elements(data) AS elements1(itemsData)
      CROSS JOIN jsonb_array_elements(itemsData->'items') AS elements2(NameList)
      
    2. 如果您判断一个空的jsonb数组,则NameList != '[]'可以工作,但您只是使用了一个函数将其打开并分解为单独的元素;在上面的修复之前,NameList是保存单个数组元素的记录.修复之后,它是数组元素.我想(itemsData->'items')是你要判断的数组
      CROSS JOIN jsonb_array_elements(data) AS elements1(itemsData)
      CROSS JOIN jsonb_array_elements(itemsData->'items') AS elements2(NameList)
      WHERE (itemsData->'items') != '[]'
      
      但该条件并没有真正的帮助:如果该数组为空,则该函数无论如何都不会产生任何结果,因此不需要将其过滤掉.它也不能用来估计表的哪些行可以跳过,因为它必须深入到每一行才能确定这一点,而且因为这是通过集返回函数执行的,所以您不能构建与此条件相对应的表达式索引(见下文).
  3. jsonb_array_elements()是一个集返回函数,所以不能在an expression index中使用:

    ERROR:  set-returning functions are not allowed in index expressions
    

    尽管如此,由于你的数组判断可以被修复,一个类似的修复可以被用来给你一个有点可用的索引:

    create index on orders((data->'data')) 
    where (jsonb_array_length(data->'data')>0);
    

    如果你的data有时是空的,而且你经常判断它的内容,那么这可能会有帮助,但你似乎担心的是里面有items个列表,这对你的帮助不大.

这就是说,normalised struct 将更容易索引和查询,而且在这两个方面都更轻、更快.

Postgresql相关问答推荐

我无法在过程中提交

到第二天的Postgres计时器

将整数(以毫秒为单位的epoch时间)转换为PrimitiveDateTime

使用doobie,如何将Scala case类映射到带有类型tstzmultirange的PostgreSQL列?

Postgres数据库系统已准备好接受连接和docker compose

如何创建一个触发器来传播对主键表的更新?

gorm 不生成字符串列

Postgres 低估了导致错误查询计划的行数

PostgreSQL - 继承表的常见自动增量

使用 postgresql Select 整数作为位和状态表

如何在 Sequelize ORM 中插入 PostGIS GEOMETRY 点?

推送到 Heroku 时出现带有 Postgres 的 Rails 迁移错误

Rails:安装 pg gem 时出错

在psql中,为什么有些命令没有效果?

如何使用 pg_dump 或 psql 从 *.sql 恢复 PostgreSQL 表?

如何防止用户看到其他数据库和其他数据库中的表?

从 PostgreSQL 中的时间戳获取日期

带有 WITH 子句的查询时出现 Postgresmissing FROM-clause entry错误

如何在postgresql中编写关于最大行数的约束?

使用 Postgres 在 Rust 的 Diesel 库中添加时间戳