Поиск по дате в массиве объектов в столбце PostgreSQL JSONB - PullRequest
0 голосов
/ 10 октября 2018

У меня есть две таблицы в моем экземпляре 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'}]}                                          |
+----+---------+----------------------------------------------------------------------------------------------------+

Я бы хотел запросить эти две таблицы, чтобы узнать количество пользователей, имеющих детей в возрасте до определенного возраста.Столбец survey_data в таблице surveys является столбцом JSONB.

До сих пор я пытался использовать jsonb_populate_recordset с LATERAL объединениями.Я смог SELECT массив child_list как два столбца, но не мог понять, как использовать это с моими JOIN между users и surveys таблицами.Я использовал следующий запрос:

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';

При этом также используется пользовательский тип, который был создан с использованием этого:

CREATE type json_type AS (gender int, birthday date)

Мой вопрос: есть ли более легкий для чтения способсделай это?Я хотел бы использовать этот запрос со многими другими предложениями JOIN s и WHERE, и мне было интересно, есть ли лучший способ сделать это.

Примечание: это в основном будет использоватьсяСистема отчетов, которая не должна быть супер быстрой, но, конечно, любое увеличение скорости приветствуется.

1 Ответ

0 голосов
/ 10 октября 2018

Используйте функцию 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)

или

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) 

Вы можете упростить свою жизнь, используя представление:

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';
...