我有一个名为 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 != '[]' 上创建索引?
jsonpath
即使没有索引支持,也应该比这更快。 db<>fiddle 的演示:
select distinct jsonb_path_query(data,'$.data[*].items[*].name')
from orders
where data @? '$.data[*].items[*].name';
您可以通过让索引缩小应该检查的行来进一步改进,与上面的
where
相同:
create index on orders((data @? '$.data[*].items[*].name'));
您的问题有一些问题
您提供的
jsonb
值格式不正确。 data
是一个键,因此它需要位于一个对象中。我猜测它的值是一个数组,如它包含的方括号所示以及您在其上调用 jsonb_array_elements()
的事实。 items
也是一个键,因此它需要位于对象中,作为 data
数组的元素。所以你缺少一对外部大括号,以及另一对 items
:
{ "data": [
{ "items": [
{ "name": "Peter"},
{ "name": "John" }
]
}
]
}
查询在语法或逻辑上无效:
jsonb_array_elements()
的结果集设置了别名,但未命名其字段/列,因此您的引用是其整个记录而不是其中的内容:
CROSS JOIN jsonb_array_elements(data) itemsData
CROSS JOIN jsonb_array_elements(itemsData->'items') NameList
为结果集定义一个名称和其字段名称,以便能够像这样使用它:
CROSS JOIN jsonb_array_elements(data) AS elements1(itemsData)
CROSS JOIN jsonb_array_elements(itemsData->'items') AS elements2(NameList)
NameList != '[]'
数组,那么
jsonb
将会起作用,但您刚刚使用了一个函数将其打开并分解为单个元素;在上述修复之前, 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') != '[]'
但这个条件并没有真正的帮助:如果该数组为空,该函数无论如何都不会产生任何内容,因此无需将其过滤掉。它也不能用于估计可以跳过表的哪些行,因为为了建立这一点,它必须到达每行的深度,并且因为这是通过设置返回函数执行的,所以您不能构建与此条件相对应的表达式索引(见下文)。jsonb_array_elements()
是一个集合返回函数,因此不能在 表达式索引中使用:
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
列表,这实际上并没有帮助。
话虽这么说,标准化结构将更容易索引和查询,并且更轻、更快。