#postgresql #datetime #window-functions
#postgresql #дата и время #оконные функции
Вопрос:
У меня есть набор данных, который содержит по одной строке на событие для каждой проблемы. У меня есть таблица дат, которую я использую в качестве основы для дат. Я хочу заполнить статус проблемы, чтобы у меня был статус проблемы, представленный каждый месяц, когда проблема открыта, даже если для проблемы нет события. Каждый день для проблемы может происходить несколько событий, таких как присвоение, комментарий, открытие, закрытие, повторное открытие.
Фактический результат: Мой код в настоящее время продолжает заполнять статус даже после закрытия проблемы.
Ожидаемый результат: заполняйте статус только с месяца открытия выпуска до его закрытия, отмечая, что тот же выпуск может быть открыт снова и впоследствии закрыт. В приведенном ниже примере не должно быть производного статуса проблемы за декабрь, поскольку проблема была закрыта в ноябре.
WITH cte (cal_month) as ( SELECT date_trunc('month', timestamp '2021-07-01T00:00:00') UNION ALL SELECT date_trunc('month', timestamp '2021-08-01T00:00:00') UNION ALL SELECT date_trunc('month', timestamp '2021-09-01T00:00:00') UNION ALL SELECT date_trunc('month', timestamp '2021-10-01T00:00:00') UNION ALL SELECT date_trunc('month', timestamp '2021-11-01T00:00:00') UNION ALL SELECT date_trunc('month', timestamp '2021-12-01T00:00:00') ), cte_2 (cal_month, issue_id, version, action_day, action_month, issue_status) as ( SELECT m.cal_month , issue_id , version , date_trunc('day', action_timestamp) as action_day , date_trunc('month', action_timestamp) as action_month , issue_status , SUM(CASE WHEN issue_status IS NOT NULL THEN 1 END) OVER (ORDER BY cal_month, issue_status, version) as grp FROM cte m LEFT JOIN issue_history h ON m.cal_month = date_trunc('month', action_timestamp) AND issue_id IN (23865) ORDER BY cal_month, issue_id, version ) SELECT * , first_value(issue_status) over (partition by grp) as derived_issue_status FROM cte_2 ORDER BY cal_month, issue_id, version
Пример вывода из кода
месяц | идентификатор проблемы | версия | день действия | месяц действия | состояние проблемы | врп | derived_issue_статус |
---|---|---|---|---|---|---|---|
2021-07-01T00:00:00 | 23865 | 1 | 2021-07-25T00:00:00 | 2021-07-01T00:00:00 | Для Разрешения | 1 | Для Разрешения |
2021-07-01T00:00:00 | 23865 | 2 | 2021-07-25T00:00:00 | 2021-07-01T00:00:00 | Для Разрешения | 2 | Для Разрешения |
2021-07-01T00:00:00 | 23865 | 3 | 2021-07-26T00:00:00 | 2021-07-01T00:00:00 | Для Разрешения | 3 | Для Разрешения |
2021-07-01T00:00:00 | 23865 | 4 | 2021-07-26T00:00:00 | 2021-07-01T00:00:00 | Для Разрешения | 4 | Для Разрешения |
2021-08-01T00:00:00 | нулевой | нулевой | нулевой | нулевой | нулевой | 4 | Для Разрешения |
2021-09-01T00:00:00 | нулевой | нулевой | нулевой | нулевой | нулевой | 4 | For Resolution |
2021-10-01T00:00:00 | нулевой | нулевой | нулевой | нулевой | нулевой | 4 | For Resolution |
2021-11-01T00:00:00 | 23865 | 5 | 2021-11-09T00:00:00 | 2021-11-01T00:00:00 | Для Разрешения | 5 | For Resolution |
2021-11-01T00:00:00 | 23865 | 6 | 2021-11-09T00:00:00 | 2021-11-01T00:00:00 | Для Разрешения | 6 | For Resolution |
2021-11-01T00:00:00 | 23865 | 7 | 2021-11-09T00:00:00 | 2021-11-01T00:00:00 | Для Разрешения | 7 | For Resolution |
2021-11-01T00:00:00 | 23865 | 8 | 2021-11-15T00:00:00 | 2021-11-01T00:00:00 | Закрытый | 8 | Closed |
2021-12-01T00:00:00 | нулевой | нулевой | нулевой | нулевой | нулевой | 8 | Closed |
Комментарии:
1. В качестве примечания: ваш
cal_month
может быть упрощен доSELECT * from generate_series(date '2021-07-01', date '2021-12-01', interval ' 1 month')
2. если вы добавите в
cte_2
предложение WHERE сstatus != 'Closed' the record for december should not exist. BTW: On
2021-08-01`, будет ли замечен идентификаторnull
проблемы, действительно ли это то, что вы хотите ?
Ответ №1:
Попробуйте это :
WITH cte_2 AS ( SELECT m.cal_month , issue_id , version , date_trunc('day', action_timestamp) as action_day , date_trunc('month', action_timestamp) as action_month , issue_status , count(*) FILTER (WHERE issue_status IS NOT NULL) OVER (ORDER BY cal_month, issue_status, version) as grp , (jsonb_agg(to_jsonb(issue_status)) FILTER (WHERE issue_status IS NOT NULL) OVER (ORDER BY cal_month, issue_status, version))-gt;'-1' as derived_issue_status FROM generate_series(date '2021-07-01', date '2021-12-01', interval ' 1 month') m LEFT JOIN issue_history h ON m.cal_month = date_trunc('month', action_timestamp) AND issue_id IN (23865) ORDER BY cal_month, issue_id, version ) SELECT m.cal_month , issue_id , version , action_day , action_month , issue_status , grp , CASE WHEN derived_issue_status = 'Closed' AND issue_status IS NULL THEN NULL ELSE derived_issue_status END AS derived_issue_status FROM cte_2 ORDER BY cal_month, issue_id, version
Предложения об изменениях :
(1) Как предложено @a_horse_with_no_name, cte подавляется и заменяется generate_series(date '2021-07-01', date '2021-12-01', interval ' 1 month')
в cte2
(2) В cte2 :
SUM(CASE WHEN issue_status IS NOT NULL THEN 1 END) OVER (ORDER BY cal_month, issue_status, version) as grp
заменено на
count(*) FILTER (WHERE issue_status IS NOT NULL) OVER (ORDER BY cal_month, issue_status, version) as grp
даже если grp
in больше не используется в окончательном запросе, потому derived_issue_status
что вычисляется в cte2 с помощью оператора new :
(jsonb_agg(to_jsonb(issue_status)) FILTER (WHERE issue_status IS NOT NULL) OVER (ORDER BY cal_month, issue_status, version))-gt;gt;'-1' as derived_issue_status
(3) A CASE
добавляется в окончательный запрос для отображения derived_issue_status = NULL, когда текущее значение issue_status равно NULL, а последнее ненулевое значение issue_status = «Закрыто».
Комментарии:
1. Спасибо за это! Это почти на месте. Я хочу иметь только календарные месяцы для каждого месяца, когда проблема открыта, даже если для нее нет события. Мне нужно сгенерировать серию дат на следующие 12 месяцев, поэтому после закрытия проблемы будет создано много пустых строк. Есть ли способ обойти это?
2. Я подошел к этому так. cte 1 создайте раздел для каждого набора событий открытия и закрытия для каждой проблемы, используя ФИЛЬТР COUNT (*) (ГДЕ issue_status = 1) (РАЗДЕЛ ПО ПОРЯДКУ идентификаторов выпуска ПО версии) в качестве grp. В приведенном выше примере все эти события были бы в grp 1. Если бы проблема была вновь открыта, были бы grp 1 и 2. cte 2 извлекает самое раннее открытие и последнее закрытие в рамках grp. Эти значения передаются в generate_series для каждой проблемы. Присоедините cte2 к предложенному вами cte и удалите оператор case, который проверяет наличие нулевого и закрытого статуса.?