繁体   English   中英

如何在PostgreSQL中使用JSONB数组结构执行联接

[英]How do I perform a join with a JSONB array structure in PostgreSQL

当我将数组存储在JSONB中时,我正在为联接的语法苦苦挣扎。 我搜索了示例,但找不到使它在PostgreSQL 9.6中起作用的神奇调味料

我有存储在一个表称为JSONB列如下结构disruption_history 该元素称为data

"message": {
        "id": 352,
        "preRecordedMessageList": {
            "preRecordedMessageCodes": [804, 2110, 1864, 1599]
        }
}

然后,我有另一个称为message_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

我懂了

错误:运算符不存在:文本=整数

即使我尝试将数字转换为整数,我得到的WHERE参数也不能返回集合。

有人可以帮忙吗?

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
    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)
            ) ;

请注意,我使用了显式联接 (避免使用隐式联接!)。

这里的技巧是通过使用jsonb_array_elements_text函数preRecordedMessageCodes转换为一组文本 ,将其进一步CAST为整数, 然后ml.component_code比较(通过使用IN条件):

您可以在dbfiddle检查整个安装在这里

还要注意,此结构会产生可怕的执行计划,这需要对两个表进行完整的顺序扫描。 我还找不到能够帮助查询的任何索引。

请注意,如果您的数组中包含NULL ,则此方法将无效,我认为这没有任何意义。


保持秩序

如果WITH ORDINALITY顺序保留数组的元素,则需要使用WITH ORDINALITY谓词不仅获取数组元素,而且获取其相对位置,并将其用于ORDER BY

-- 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 data的结构始终相同,我强烈建议您对设计进行规范化 (至少部分规范化 ):

CREATE TABLE disruption_history_no_json
(
    disruption_history_id SERIAL PRIMARY KEY,
    message_id INTEGER,
    pre_recorded_message_codes INTEGER[]
) ;

CREATE INDEX idx_disruption_history_no_json_pre_recorded_message_codes
    ON disruption_history_no_json USING GIN (pre_recorded_message_codes) ;

将允许一个更简单,有效和简单的查询:

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)允许您不规范化,也不必考虑表结构,但是您在性能和可维护性方面付出了沉重的代价。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM