如何在PostgreSQL

时间:2017-08-03 16:36:21

标签: sql postgresql

当我在JSONB中存储了一个数组时,我正在努力学习连接的语法。我已经搜索了一些例子,我无法找到使其在PostgreSQL 9.6中运行的神奇酱油

我在名为disruption_history的表中的JSONB列中存储了以下结构。该元素称为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

我得到了

  

错误:运算符不存在:text = integer

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

有人可以帮忙吗?

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:0)

您可以使用以下查询:

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

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

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

您可以在 dbfiddle here

中查看整个设置

另请注意,此结构会生成糟糕的执行计划,需要对两个表进行完整的顺序扫描。我无法找到任何有助于查询的索引。

请注意,如果您的数组中包含NULL s,这将无法运行,我认为这些数组没有意义。

保持秩序

如果要按顺序保留数组的元素,则需要使用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 here

上观看

<强>替代

如果您的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 here

一起检查所有内容

JSON(B)允许您不进行规范化,也不必考虑您的表格结构,但是您在性能和可维护性方面付出了沉重的代价。