Sql 按开始和结束时间对分组的条目进行计数

Sql 按开始和结束时间对分组的条目进行计数,sql,postgresql,count,group-by,Sql,Postgresql,Count,Group By,我在PostgreSQL中存储了具有开始和结束时间的事件,如下表所示: CREATE TABLE foo AS SELECT id, name, startTime::timestamp, endTime::timestamp FROM ( VALUES ( 1, 'A', '2017-05-19T12:21:18+00:00', '2017-05-19T15:31:18+00:00' ), ( 2, 'B', '2017-05-19T12:35:18+00:00', '

我在PostgreSQL中存储了具有开始和结束时间的事件,如下表所示:

CREATE TABLE foo
AS
  SELECT id, name, startTime::timestamp, endTime::timestamp
  FROM ( VALUES
    ( 1, 'A', '2017-05-19T12:21:18+00:00', '2017-05-19T15:31:18+00:00' ),
    ( 2, 'B', '2017-05-19T12:35:18+00:00', '2017-05-19T12:48:18+00:00' ),
    ( 3, 'C', '2017-05-19T13:00:18+00:00', '2017-05-19T13:31:18+00:00' ),
    ( 4, 'D', '2017-05-19T13:11:18+00:00', '2017-05-19T13:27:18+00:00' ),
    ( 5, 'E', '2017-05-19T13:45:18+00:00', '2017-05-19T14:55:18+00:00' )
  ) AS (id, name, startTime, endTime);
假设我想从给定的开始和结束时间开始,按15分钟的时间段对这些条目进行分组。例如,从2017-05-19 12:00到2017-05-19 14:00,我希望收到如下信息:

date                      | count
---------------------------------
2017-05-19T12:00:00+00:00 | 0         (A expected)
2017-05-19T12:15:00+00:00 | 1         (A, B expected)
2017-05-19T12:30:00+00:00 | 2         (A, B expected)
2017-05-19T12:45:00+00:00 | 2         (A, C, D expected)
2017-05-19T13:00:00+00:00 | 3         (A, C, D expected)
2017-05-19T13:15:00+00:00 | 3         (A, C expected)
2017-05-19T13:30:00+00:00 | 2         (A, E expected)
2017-05-19T13:45:00+00:00 | 2         (A, E expected)

如何在PostrgreSQL中以最简单的方式实现它?

我不太确定您想要的是什么,但在我看来是这样的

with my_table(id, name, startTime, endTime) as (
values
    (1, 'A', '2017-05-19T12:21:18+00:00'::timestamp, '2017-05-19T15:31:18+00:00'::timestamp),
    (2, 'B', '2017-05-19T12:35:18+00:00', '2017-05-19T12:48:18+00:00'),
    (3, 'C', '2017-05-19T13:00:18+00:00', '2017-05-19T13:31:18+00:00'),
    (4, 'D', '2017-05-19T13:11:18+00:00', '2017-05-19T13:27:18+00:00'),
    (5, 'E', '2017-05-19T13:45:18+00:00', '2017-05-19T14:55:18+00:00')
)

select date, count(id), string_agg(name, ', ') as names
from generate_series('2017-05-19 12:00:00'::timestamp, '2017-05-19 14:00:00', '15m'::interval) as date
left join my_table t on tstzrange(date, date+ '15m') && tstzrange(t.starttime, t.endtime)
group by 1
order by 1;

        date         | count |  names  
---------------------+-------+---------
 2017-05-19 12:00:00 |     0 | 
 2017-05-19 12:15:00 |     1 | A
 2017-05-19 12:30:00 |     2 | A, B
 2017-05-19 12:45:00 |     2 | A, B
 2017-05-19 13:00:00 |     3 | A, C, D
 2017-05-19 13:15:00 |     3 | A, C, D
 2017-05-19 13:30:00 |     2 | A, C
 2017-05-19 13:45:00 |     2 | A, E
 2017-05-19 14:00:00 |     2 | A, E
(9 rows)
SELECT
  to_timestamp(timeseg*60*15) AT TIME ZONE 'localtime' AS tsround,
  count(*),
  array_agg(name)
FROM foo
CROSS JOIN LATERAL generate_series(
  EXTRACT(EPOCH FROM starttime AT TIME ZONE 'localtime')::int / 60 / 15,
  EXTRACT(EPOCH FROM endtime AT TIME ZONE 'localtime')::int   / 60 / 15
) AS t(timeseg)
GROUP BY timeseg
ORDER BY tsround;

       tsround       | count | array_agg 
---------------------+-------+-----------
 2017-05-19 12:15:00 |     1 | {A}
 2017-05-19 12:30:00 |     2 | {A,B}
 2017-05-19 12:45:00 |     2 | {A,B}
 2017-05-19 13:00:00 |     3 | {A,C,D}
 2017-05-19 13:15:00 |     3 | {A,C,D}
 2017-05-19 13:30:00 |     2 | {A,C}
 2017-05-19 13:45:00 |     2 | {A,E}
 2017-05-19 14:00:00 |     2 | {A,E}
 2017-05-19 14:15:00 |     2 | {A,E}
 2017-05-19 14:30:00 |     2 | {A,E}
 2017-05-19 14:45:00 |     2 | {A,E}
 2017-05-19 15:00:00 |     1 | {A}
 2017-05-19 15:15:00 |     1 | {A}
 2017-05-19 15:30:00 |     1 | {A}
(14 rows)

最简单的方法是创建一个包含开始时间和结束时间的表,根据条件连接表,按开始时间分组,并显示count aggregate函数的结果。我对这种显示的具体工作方式感到困惑。如果一个范围从1开始到3结束,而另一个范围从1开始到2结束,那么第一个范围中的第三个区块会显示什么?好吧,谢谢你的回答,但从结果来看,它并不像我期望的那样工作:)例如,对于2017-05-19 12:45:00,我期望计数2,因为ID为1和2的行应该被计数。在这两个条目中,开始和结束之间的时间都是12:45。看起来我们都走神了,但我已经更新了查询并添加了一列以方便验证结果。啊,我现在理解你的结果,但仍然不是我所期望的:)也许我不够清楚,但请看,例如2017-05-19 12:15:00应该表示2017-05-19 12:15:00和2017-05-19 12:29:59之间的时间间隔,因此它不应该返回零,而应该返回一(第1行已计算)。我用预期的ID更新了第一篇文章,这应该被计算在内。我想我们终于达成了协议。请参阅修改后的查询。
SELECT
  to_timestamp(timeseg*60*15) AT TIME ZONE 'localtime' AS tsround,
  count(*),
  array_agg(name)
FROM foo
CROSS JOIN LATERAL generate_series(
  EXTRACT(EPOCH FROM starttime AT TIME ZONE 'localtime')::int / 60 / 15,
  EXTRACT(EPOCH FROM endtime AT TIME ZONE 'localtime')::int   / 60 / 15
) AS t(timeseg)
GROUP BY timeseg
ORDER BY tsround;

       tsround       | count | array_agg 
---------------------+-------+-----------
 2017-05-19 12:15:00 |     1 | {A}
 2017-05-19 12:30:00 |     2 | {A,B}
 2017-05-19 12:45:00 |     2 | {A,B}
 2017-05-19 13:00:00 |     3 | {A,C,D}
 2017-05-19 13:15:00 |     3 | {A,C,D}
 2017-05-19 13:30:00 |     2 | {A,C}
 2017-05-19 13:45:00 |     2 | {A,E}
 2017-05-19 14:00:00 |     2 | {A,E}
 2017-05-19 14:15:00 |     2 | {A,E}
 2017-05-19 14:30:00 |     2 | {A,E}
 2017-05-19 14:45:00 |     2 | {A,E}
 2017-05-19 15:00:00 |     1 | {A}
 2017-05-19 15:15:00 |     1 | {A}
 2017-05-19 15:30:00 |     1 | {A}
(14 rows)