簡體   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