如何在PostgreSQL中使用JSONB数组结构执行联接
当我在JSONB中存储了一个数组时,我正在努力学习连接的语法。我已经搜索了一些示例,但在PostgreSQL 9.6中找不到使这一功能发挥作用的神奇酱汁 我将以下结构存储在一个名为如何在PostgreSQL中使用JSONB数组结构执行联接,sql,postgresql,Sql,Postgresql,当我在JSONB中存储了一个数组时,我正在努力学习连接的语法。我已经搜索了一些示例,但在PostgreSQL 9.6中找不到使这一功能发挥作用的神奇酱汁 我将以下结构存储在一个名为disruption\u history的表中的JSONB列中。该元素称为数据: "message": { "id": 352, "preRecordedMessageList": { "preRecordedMessageCodes": [804, 2110,
disruption\u history
的表中的JSONB列中。该元素称为数据:
"message": {
"id": 352,
"preRecordedMessageList": {
"preRecordedMessageCodes": [804, 2110, 1864, 1599]
}
}
然后我有另一个标准表,名为message\u library
component_code | integer | not null
message_text | character varying(255) | not null
我试图为每一组消息代码生成文本。大概是
SELECT
ml.message_text
FROM
message_library ml, disruption_history dh
WHERE
jsonb_array_elements_text(dh.data->'message'->'preRecordedMessageList'
->'preRecordedMessageCodes')) = ml.component_code
我明白了
错误:运算符不存在:text=integer
即使我尝试将数字转换为整数,我也会得到的参数,其中
不能返回一个集合
有人能帮忙吗?您可以使用以下查询:
SELECT
CAST(dh.data->'message'->>'id' AS INTEGER) AS message_id,
ml.message_text
FROM
disruption_history dh
JOIN message_library ml
ON ml.component_code IN
(SELECT
CAST(jsonb_array_elements_text(
dh.data->'message'->'preRecordedMessageList'->'preRecordedMessageCodes'
)
AS INTEGER)
) ;
注意,我使用了显式连接(避免隐式连接!)
这里的诀窍是使用,将预先录制的消息代码转换为一组文本,进一步将CAST
转换为整数,然后与ml.component\u code
进行比较(在
条件下使用):
您可以在DBFIDLE上检查整个设置
还要注意,这个结构产生了一个糟糕的执行计划,需要对两个表进行完整的顺序扫描。我还没有找到任何有助于查询的索引
请注意,如果数组中有NULL
s,这将不起作用,我认为这是没有意义的
维持秩序:
如果希望保持数组元素的顺序,则需要使用具有顺序性的谓词,以不仅获取数组元素,还获取其相对位置,并使用它来按顺序排序
-- Keeping order
SELECT
CAST(dh.data->'message'->>'id' AS INTEGER) AS message_id,
ml.message_text
FROM
disruption_history dh
JOIN LATERAL
jsonb_array_elements_text(dh.data->'message'->'preRecordedMessageList'->'preRecordedMessageCodes')
WITH ORDINALITY AS x(mc, ord) /* We will want to use 'ord' to order by */
ON true
JOIN message_library ml ON ml.component_code = cast(mc AS INTEGER)
ORDER BY
message_id, ord ;
在dbfiddle看这个
备选方案:
如果json数据的结构始终相同,我强烈建议您规范化设计(至少部分):
将允许更简单、高效和更简单的查询:
SELECT
message_id,
ml.message_text
FROM
disruption_history_no_json dh
JOIN message_library ml
ON ml.component_code = ANY(pre_recorded_message_codes) ;
在dbfiddle一起检查所有东西
JSON(B)
允许您不进行规范化,也不必考虑您的表结构,但您在性能和可维护性方面付出了高昂的代价。不确定这是否重要,但最后一行有一个额外的右括号。嘿@Oto,您认为突出显示您的解决方案工作的原因是否有价值。我相信是的,但总有一个教学时间。谢谢,这也很有效,但与我上面描述的排序问题相同。谢谢你的全面回答。不过,JSON数组中的代码似乎是以随机顺序处理的。我需要将它们按照数组的顺序转换成文本,因为它们形成了一个可读的句子。就我而言,我得到的是“大理石拱门”。车站关门了吗“而不是‘大理石拱门车站关闭了’。@Oto的回答也是如此,同样有效。当然,不确定哪种方法最有效。关系理论没有顺序的概念,因此在SQL中执行的大多数操作也没有顺序,除非明确要求给定顺序。您没有将此指定为要求。决不要在SQL中采用顺序。查看保持订单的更新版本。非常感谢。我理解SQL中缺乏秩序。我只是忘了将它作为一个需求添加:-)正如您所说,规范化会更好,但这是一个复杂的变化结构的一部分,JSONB结构就是用来做这种事情的。
select message_library.message_text
from disruption_history
join lateral jsonb_array_elements_text(data->'message'->'preRecordedMessageList'->'preRecordedMessageCodes') v
on true
join message_library
on v.value::int = message_library.component_code
select message_library.message_text
from disruption_history
join lateral jsonb_array_elements_text(data->'message'->'preRecordedMessageList'->'preRecordedMessageCodes') v
on true
join message_library
on v.value::int = message_library.component_code