2017-08-03 36 views
0

當我有一個存儲在JSONB中的數組時,我正在努力處理連接的語法。我搜索了一些例子,但是我找不到在PostgreSQL 9.6中使用這個工具的魔法醬。我有一個名爲disruption_history的表,在JSONB列中存儲了以下結構。該元素被稱爲data如何在PostgreSQL中使用JSONB數組結構執行連接

"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 

我得到

ERROR: operator does not exist: text = integer

即使我嘗試投的數字爲整數,我得到的WHERE參數不能返回一個集合。

有人可以幫忙嗎?

+0

不知道這是否重要,但在最後一行有一個額外的右括號。 –

回答

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)讓你不正常化,而不是不得不多想你的表結構,但一分錢一分貨的性能和可維護性沉重的代價。

+0

感謝您的全面解答。這可以工作,但JSON數組中的代碼以看似隨機的順序進行處理。我需要將它們按照該數組中的順序轉換爲文本,因爲它們形成可讀的句子。在我的情況下,我得到'大理石拱門。是封閉站「而不是」大理石拱門站「關閉。 這與@Oto的答案是一樣的,也是一樣的。不知道哪個是最有效的方法。 – user752113

+0

關係理論沒有* order *的概念,所以大多數你在SQL中做什麼也沒有,除非你明確地要求給定的順序,你沒有指定* this *作爲一個要求,不要在SQL中假設一個順序,查看*保持順序的更新版本* 。 – joanolo

+0

這是非常感謝,我明白缺乏SQL的順序,我只是忘了把它作爲一個要求添加它:-) 正如你所說,它會更好地規範化,但這是一個複雜的變化的一部分結構,JSONB結構的用途 – user752113

0
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 
+0

嘿@Oto,你認爲在突出你的解決方案的工作原理時會有優點,我相信它確實有用,但總是有教學時刻。 –

+0

謝謝,這也可以工作,但會遇到與上述相同的順序問題。 – user752113

相關問題