使用JSONB列中的值连接表

时间:2015-07-09 06:04:30

标签: sql postgresql-9.4 jsonb

有两个表:

授权联系人(auth_contacts):

(
userid varchar
contacts jsonb
)

contacts包含一系列具有属性{contact_id, type}

的联系人

discussion

(
contact_id varchar
discussion_id varchar
discussion_details jsonb
)

auth_contacts至少有100k记录,因此非JSONB类型是不合适的,因为它会使记录数量增加一倍或三倍。

auth_contacts的示例数据:

userid  | contacts
'11111' | '{"contact": [{"type": "type_a", "contact_id": "1-A-12"}
                      , {"type": "type_b", "contact_id": "1-A-13"}]}'

discussion表有500万条记录。

我想在discussion.contact_id(关系列)上加入联系人ID,其中json对象位于auth_contacts.contacts的json对象数组中。

一种非常粗暴的方式是:

SELECT *
FROM discussion d 
JOIN (SELECT userid, JSONB_OBJECT_KEYS(a.contacts) AS auth_contact
      FROM auth_contacts a) AS contacts
      ON (d.contact_id = contacts.auth_contact::text)

这实际上是在运行时创建(内部sql)用户ID与联系人id表(这是我避免的,因此用于JSONB数据类型 对具有大记录的用户的此查询需要26 +秒,这并非全都是好的。 尝试了其他一些方法:PostgreSQL 9.4: Aggregate / Join table on JSON field id inside array

但应该有一种更清洁,更好的方法,就像这样简单 加入d.contact_id = contacts -> contact -> contact_id? 当我尝试这个时,它不会产生任何结果。

在搜索网络时,这似乎是一项非常麻烦的任务?

1 个答案:

答案 0 :(得分:9)

概念证明

你粗暴的方式"实际上并没有工作。这是另一种粗糙的方式:

SELECT *
FROM  auth_contacts a
    , jsonb_to_recordset(a.contacts->'contact') AS c(contact_id text)
JOIN  discussion d USING (contact_id);

如评论所述,您还可以使用contains operator @>

制定联接条件
SELECT *
FROM   auth_contacts a
JOIN   discussion d ON a.contacts->'contact'
                    @> json_build_array(json_build_object('contact_id', d.contact_id))::jsonb

而是使用JSON创建函数而不是字符串连接。看起来很麻烦,但如果支持功能jsonb_path_ops GIN 索引,它实际上会非常快:

CREATE INDEX auth_contacts_contacts_gin_idx ON auth_contacts
USING  gin ((contacts->'contact') jsonb_path_ops);

详细说明:

正确的解决方案

这一切都很吸引人,但这里的问题是关系模型。你的主张:

  

因此根据它不适合使用非JSONB类型   将记录数量增加一倍或三倍。

正好相反。它将无意义包装为将表连接到JSON文档类型所需的ID。使用多对多关系规范化表,并将您在DB中使用的所有ID实现为具有适当数据类型的单独列。基本信息: