8

有两个表:

授权联系人 ( 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 对象数组中的 json 对象auth_contacts.contacts

一种非常粗暴的方法是:

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:在数组内的 JSON 字段 id 上聚合/连接表

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

在网上搜索时,这似乎是一项相当繁琐的任务?

4

1 回答 1

20

概念证明

您的“粗鲁方式”实际上不起作用。这是另一种粗略的方法:

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

如前所述,您还可以使用contains 运算符@>制定连接条件:

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 类型是不合适的,因为它会使记录数量增加一倍或三倍。

正确的相反。将连接表所需的 ID 包装成 JSON 文档类型是无稽之谈。使用多对多关系规范化您的表,并将您在数据库中使用的所有 ID 实现为具有适当数据类型的单独列。基本:

于 2015-07-10T06:20:59.880 回答