以下情况的最佳选择是什么?

What is the best alternative for the following situation?

我在我的 Postgresql 数据库中使用 JSONB 字段来存储以下文档。我拥有数千份文件。我需要用这些数据创建报告,但搜索速度很慢。

如果我需要创建一份报告说明一个月的新用户,我需要查看整个文档,比较用户是在一个月内还是在另一个月内。

留言文件:

[{"recipient":1,"user":4,"created_at":"2016-11-10","content":"Duis aliquam convallis nunc.","is_sender_user":true},
{"recipient":1,"user":18,"created_at":"2016-12-10","content":"Proin eu mi.","is_sender_user":false},
{"recipient":1,"user":4,"created_at":"2016-11-20","content":"In hac habitasse platea dictumstm.","is_sender_user":true},
{"recipient":1,"user":20,"created_at":"2016-12-14","content":"Donec ut dolor.","is_sender_user":true},
{"recipient":1,"user":13,"created_at":"2016-12-06","content":"Nulla mollis molestie lorem. Quisque ut erat. Curabitur gravida nisi at nibh.","is_sender_user":true}]

最好创建一个用户 table 并创建一个 JSONB 消息字段来存储您的消息。或者我可以使用 JSONB 查询创建报告的方式?

您的消息文档描述了用户之间的关系:发件人 将内容传输给收件人。发件人可以发送很多消息,收件人可能会收到很多消息。这在关系结构中得到了最好的体现,用户 table 和消息 table 具有针对发件人和收件人的外键约束。

可以像您所做的那样将所有内容都放入 JSONB 字段中,但存在一些主要缺点:查询性能受到影响,尽管正如 Samuil Petrov 提到的,这可以通过索引得到改善;但更重要的是,没有什么可以阻止消息具有无效的用户或收件人 ID。使用无模式 JSONB 字段可以简化开发,同时您仍在散列需要存储的内容,但一旦您知道需要什么,它应该由您的模式强制执行。

正如 Samuil Petrov 提到的,您可以在 jsonb 字段上创建索引,我建议在 created_atuser

的月份部分创建索引
create INDEX td002_si3 ON testData002 (substring(doc->>'created',0,8),(doc->>'user'));

有了这个查询

SELECT 
      substring(doc ->> 'created', 0, 8) AS m,
      ARRAY_AGG(DISTINCT doc ->> 'user')          AS users
    FROM testData002
    GROUP BY substring(doc ->> 'created', 0, 8)

将为您提供索引扫描中的每月用户数

GroupAggregate  (cost=0.28..381.52 rows=3485 width=50)
  Group Key: ""substring""((doc ->> 'created'::text), 0, 8)
  ->  Index Scan using td002_si3 on testdata002  (cost=0.28..294.28 rows=3500 width=50)

使用

生成的测试数据
create table testData002 as 
     select row_number() OVER () as id
           ,jsonb_build_object('created',dt::DATE
                              ,'user',(random()*1000)::INT) as doc 
       from generate_series(1,10),generate_series('2016-01-01'::TIMESTAMP,'2016-12-15'::TIMESTAMP,'1 day'::INTERVAL) as dt;