2017-08-03 35 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

相关问题