按时间范围选择,按1小时细分结果

时间:2019-05-09 15:47:10

标签: sql postgresql

我需要以下要求的帮助:

我有下表:

id | status  | created_at             | closed_at
1     'OPEN'   '2019-05-08T12:30:24Z'   null
2     'CLOSED' '2019-05-08T12:50:22Z'   '2019-05-08T13:05:53Z'
3     'CLOSED' '2019-05-08T13:20:00Z'   '2019-05-08T13:40:12Z'
4     'CLOSED' '2019-05-08T13:55:47Z'   '2019-05-08T14:05:36Z'
5     'OPEN'   '2019-05-08T14:15:57Z'   null
6     'CLOSED' '2019-05-08T14:30:29Z'   '2019-05-08T14:40:00Z'
7     'CLOSED' '2019-05-08T14:55:38Z'   '2019-05-08T15:05:51Z'

对于时间范围 created_at='2019-05-08T13:00:00Z' closed_at ='2019-05-08T15:00:00Z' 输出应为:

timestamp             |    id
2019-05-08 13:00:00      1,2,3,4
2019-05-08 14:00:00      1,4,5,6,7

请注意,1在13:00和14:00存在,因为它的created_at从12:30开始,并且没有closed_at(空),因此它在整个时间范围内都“存在”。

我能想到的最接近的是:

SELECT TIMESTAMP WITH TIME ZONE 'epoch' +
    INTERVAL '1 second' * round(extract('epoch' from created_at) / 3600) * 3600 as created_at,
    id
FROM issues 
WHERE (created_at BETWEEN '2019-05-08T13:00:00Z' AND '2019-05-08T15:00:00Z') OR 
    (closed_at BETWEEN '2019-05-08T13:00:00Z' AND '2019-05-08T15:00:00Z') OR
    (status='OPEN')
GROUP BY 
    created_at,id
ORDER BY created_at;

给出

"2019-05-08 13:00:00+00"    "3"
"2019-05-08 13:00:00+00"    "2"
"2019-05-08 13:00:00+00"    "1"
"2019-05-08 14:00:00+00"    "4"
"2019-05-08 14:00:00+00"    "5"
"2019-05-08 15:00:00+00"    "6"
"2019-05-08 15:00:00+00"    "7"

甚至可以通过选择查询来做到吗?

1 个答案:

答案 0 :(得分:2)

假设您的意思是created_at ='2019-05-08T13:00:00Z',您应该可以使用tstzranges轻松完成此操作。

WITH time_ranges as 
   (SELECT tstzrange(t, t + '1 hour'::interval, '[]') as t_range 
    FROM generate_series('2019-05-08T13:00:00Z'::timestamptz, 
                         '2019-05-08T14:00:00Z'::timestamptz, 
                         '1 hour'::interval) g(t))
,
test_values AS (
  SELECT * from (values
  (1, '2019-05-08T12:30:24Z'::timestamptz, null::timestamptz),
  (2, '2019-05-08T12:50:22Z', '2019-05-08T13:05:53Z'),
  (3, '2019-05-08T13:20:00Z', '2019-05-08T13:40:12Z'),
  (4, '2019-05-08T13:55:47Z', '2019-05-08T14:05:36Z'), 
  (5, '2019-05-08T14:15:57Z', null), 
  (6, '2019-05-08T14:30:29Z', '2019-05-08T14:40:00Z'), 
  (7, '2019-05-08T14:55:38Z', '2019-05-08T15:05:51Z')
  ) v(id, created_at, closed_at)
)
select lower(t_range), string_agg(id::text, ',' ORDER BY id)
FROM time_ranges
JOIN test_values on tstzrange(created_at, closed_at, '[]') && t_range
GROUP BY lower(t_range);
         lower          | string_agg
------------------------+------------
 2019-05-08 13:00:00+00 | 1,2,3,4
 2019-05-08 14:00:00+00 | 1,4,5,6,7
(2 rows)

唯一棘手的部分是加入从created_at到closed_at的tstzrange与给定小时数之间的重叠。您根本不需要状态栏。