简体   繁体   English

在 PostgreSQL JSONB 列中的对象数组中按日期搜索

[英]Search by date in array of objects within PostgreSQL JSONB Column

I have two tables in my PostgreSQL 9.6 instance.我的 PostgreSQL 9.6 实例中有两个表。

users

+----+------------+-----------+-------------------+
| id | first_name | last_name | email             |
+----+------------+-----------+-------------------+
| 1  | John       | Doe       | john.doe@test.com |
+----+------------+-----------+-------------------+
| 2  | Jane       | Doe       | jane.doe@test.com |
+----+------------+-----------+-------------------+
| 3  | Mike       | Doe       | mike.doe@test.com |
+----+------------+-----------+-------------------+


surveys
+----+---------+----------------------------------------------------------------------------------------------------+
| id | user_id | survey_data                                                                                        |
+----+---------+----------------------------------------------------------------------------------------------------+
| 1  | 1       | {'child_list': [{'gender': 1, 'birthday': '2015-10-01'}, {'gender': 2, 'birthday': '2017-05-01'}]} |
+----+---------+----------------------------------------------------------------------------------------------------+
| 2  | 2       | {'child_list': []}                                                                                 |
+----+---------+----------------------------------------------------------------------------------------------------+
| 3  | 3       | {'child_list': [{'gender': 2, 'birthday': '2008-01-01'}]}                                          |
+----+---------+----------------------------------------------------------------------------------------------------+

I would like be able to query these two tables to get the number of users who have children between certain age.我希望能够查询这两个表以获取在特定年龄之间有孩子的用户数量。 The survey_data column in surveys table is a JSONB column.survey_datasurveys表是JSONB列。

So far I've tried using jsonb_populate_recordset with LATERAL joins.到目前为止,我已经尝试将jsonb_populate_recordsetLATERAL连接一起使用。 I was able to SELECT the child_list array as two columns but couldn't figure out how to use that with my JOIN between users and surveys tables.我能够将child_list数组SELECT为两列,但无法弄清楚如何在userssurveys表之间使用我的JOIN The query I used is as below:我使用的查询如下:

SELECT DISTINCT u.email
FROM surveys
  CROSS  JOIN LATERAL (
   SELECT *
   FROM  jsonb_populate_recordset(null::json_type, (survey.survey_data->>'child_list')::jsonb) AS d
   ) d
INNER JOIN users u ON u.id = survey.user_id
WHERE d.birthday BETWEEN '2014-05-05' AND '2018-05-05';

This also uses a custom type which was created using this:这也使用了使用此创建的自定义类型:

CREATE type json_type AS (gender int, birthday date)

My question is, is there an easier to read way to do this?我的问题是,有没有更容易阅读的方法来做到这一点? I would like to use this query with many other JOIN s and WHERE clauses and I was wondering if there is a better way of doing this.我想将此查询与许多其他JOINWHERE子句一起使用,我想知道是否有更好的方法来做到这一点。

Note: this is mainly going to be used by a reporting system which does not need to be super fast but of course any speed gains are welcome.注意:这主要由不需要超快但当然欢迎任何速度提升的报告系统使用。

Use the function jsonb_array_elements(), examples:使用函数jsonb_array_elements(),示例:

select email, (elem->>'gender')::int as gender, (elem->>'birthday')::date as birthday
from users u
left join surveys s on s.user_id = u.id
cross join jsonb_array_elements(survey_data->'child_list') as arr(elem)

       email       | gender |  birthday  
-------------------+--------+------------
 john.doe@test.com |      1 | 2015-10-01
 john.doe@test.com |      2 | 2017-05-01
 mike.doe@test.com |      2 | 2008-01-01
(3 rows)

or或者

select distinct email
from users u
left join surveys s on s.user_id = u.id
cross join jsonb_array_elements(survey_data->'child_list') as arr(elem)
where (elem->>'birthday')::date between '2014-05-05' and '2018-05-05';

       email       
-------------------
 john.doe@test.com
(1 row) 

You can make your life easier using a view:您可以使用视图让您的生活更轻松:

create view users_children as
    select email, (elem->>'gender')::int as gender, (elem->>'birthday')::date as birthday
    from users u
    left join surveys s on s.user_id = u.id
    cross join jsonb_array_elements(survey_data->'child_list') as arr(elem);

select distinct email
from users_children
where birthday between '2014-05-05' and '2018-05-05';

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM