我在红移数据库中有一组记录,每个记录都有一个表示活动开始的时间戳,以及一个表示活动结束的时间戳。
timestamp_start |timestamp_end
2017-01-01 01:01:31 |2017-01-01 01:48:31
2017-01-01 01:02:35 |2017-01-01 02:08:35
2017-01-01 01:09:10 |2017-01-01 02:18:10
2017-01-01 01:10:05 |2017-01-01 02:00:05
2017-01-01 01:14:58 |2017-01-01 01:56:58
2017-01-01 01:19:10 |2017-01-01 02:18:10
2017-01-01 01:25:10 |2017-01-01 01:54:10
2017-01-01 01:30:23 |2017-01-01 01:56:23
2017-01-01 01:36:26 |2017-01-01 03:06:26
2017-01-01 01:37:03 |2017-01-01 02:14:03
2017-01-01 01:37:15 |2017-01-01 02:08:15
2017-01-01 01:37:55 |2017-01-01 02:58:55
2017-01-01 01:42:49 |2017-01-01 02:59:49
2017-01-01 01:44:10 |2017-01-01 03:23:10
2017-01-01 01:46:49 |2017-01-01 02:58:49
2017-01-01 01:49:34 |2017-01-01 02:15:34
2017-01-01 01:52:11 |2017-01-01 02:38:11
2017-01-01 01:52:45 |2017-01-01 03:31:45
2017-01-01 01:54:15 |2017-01-01 02:17:15
2017-01-01 01:55:14 |2017-01-01 02:40:14
这是一种使用以下方法计算每分钟新活动发生次数的简单方法:
select date_trunc('minute', timestamp_start) as minute, count(*) as count
from myTable
group by 1
同样计算活动的结束:
select date_trunc('minute', timestamp_end) as minute, count(*) as count
from myTable
group by 1
但是,如何计算“正在进行”的每分钟的活动[编辑:在某个范围内的每分钟]?即在分组01:01我们有一个新的活动开始。在分钟01:02我们有另一个新的活动开始,但01:01 的活动尚未完成,因此当前活动的数量是两个。相反,02:00的记录数必须不包括在该分钟之前完成的4条记录中的任何一条。
此外,解决方案还必须不“爆炸”数据,即将记录连接到“准备”分钟的不同表以提供多个记录副本,然后计算结果表的长度。
我尝试过以下方法:
SELECT
minute,
count(CASE WHEN timestamp_end > minute AND timestamp_start < minute) AS tmp
FROM (
SELECT minute
FROM (
(
SELECT date_trunc('minute', timestamp_start) AS minute
FROM myTable
GROUP BY 1
)
UNION ALL (
SELECT date_trunc('minute', timestamp_end) AS minute
FROM myTable
GROUP BY 1
)
) s1
GROUP BY 1)
但是我怀疑我的案例论证很糟糕,很可能会错过它。我也看过窗口函数,但是我看不出一种明显的方法来计算仅包含“当前活动”的记录。
答案 0 :(得分:1)
更新 - 最后的另一个建议,基于Stefano Zanini的想法...
原创想法
要生成一分钟的输出,您当然可以执行类似
的操作select count(*)
from myTable
where $minute between timestamp_start and timestamp_end
从SQL的角度来看,假设您希望在该事件的范围内为每分钟的输出行计算每条记录,而且您不希望&#34;爆炸&#34;针对分钟列表的数据,有点矛盾。我假设您关心的是运行查询的性能和/或资源使用情况;我认为这对于足够大的数据集来说可能是一个问题,但如果你还没有,那么它仍然值得测试这种方法。
可能有效的另一个选项
现在,当我对Stefano Zanini的回答发表评论时,他的建议并没有取得成功。但它确实提出了一种可行的方法:首先捕获计数发生变化的分钟数,然后进行后处理以获得每分钟的值。
获取计数可能更改的分钟列表
select distinct minute from (
select date_trunc('minute', timestamp_start) as minute from my_table
union all
select dateadd('m', 1, date_trunc('minute', timestamp_start)) as minute from my_table
)
这可能会让您在桌面上加入更少的会议记录。
你可以通过使用外部联接来获得更像他的查询的工作(并且做一些事情以避免重复记录;避免做出这样的事情),但是自联接会导致相同的级别&#34;数据爆炸&#34;作为原始方法的这个版本。
对于他的查询可能出现的问题存在争议,请考虑一下:
两种方法都试图限制采样时间,以免爆炸&#34;数据,然后尝试计算与每个选定的样本分钟对应的数据。
Stefano选择样本的方法存在的功能问题是他只选择事件的开始时间。但实际上,计数也会在事件结束时发生变化。所以考虑一下
Start Stop
10:00 10:15
10:05 10:20
现在,Stefano的查询将生成10:00和10:05的记录,您必须插入任何其他分钟的值。您将正确地推断1表格10:00到10:04的值,以及从10:05到10:15的值2。但是你将也在10:16推断出值为2,因为查询当时并没有告诉你任何改变。这是错的。
现在,您可以为查询添加更多复杂性以解决这个问题;但是计数逻辑在他的版本中已经更加间接/复杂了(并且仅仅因为他列出了不同计数方法的权衡并不意味着它们中的任何一个是精确的或适合于给定目的) 。或者你可以用简单直接的方式做到这一点。
如果这些都不起作用(或者你不想因为某些其他原因而使用它)那么我就不知道SQL是你完成这项工作所需的工具。也许一个迭代超过几分钟的过程并将计数累积到临时表中?或者,如果数据库和用户之间有服务层(java或其他),那么可能在那里进行计数?
答案 1 :(得分:0)
我认为可以使用自联接完成任务,左表的开始日期在右侧表的行的开始日期和结束日期之间。
select t1.timestamp_start, count(*)
from test t1
join test t2
on t1.timestamp_start >= t2.timestamp_start and
t1.timestamp_start < t2.timestamp_end
group by t1.timestamp_start
这样可以正常工作,但输出不是每分钟的计数,而是每timestamp_start
的计数。从该字段中提取分钟提出了一个新问题:第37分钟和第52分钟有更多的任务从它们开始,你应该为它们显示什么数量?
这是最大/最小选项
select date_trunc('minute', tt.timestamp_start),
max(tt.cnt) as max_cnt, /* this is probably the value you want */
min(tt.cnt) as min_cnt
from (
select t1.timestamp_start, count(*) as cnt
from test t1
join test t2
on t1.timestamp_start >= t2.timestamp_start and
t1.timestamp_start < t2.timestamp_end
group by t1.timestamp_start
) tt
group by date_trunc('minute', tt.timestamp_start)
sum选项更简单(请注意,下面的查询提供与上面使用sum
聚合的查询相同的结果,因为分组条件不太严格):
select date_trunc('minute', t1.timestamp_start), count(*)
from test t1
join test t2
on t1.timestamp_start >= t2.timestamp_start and
t1.timestamp_start < t2.timestamp_end
group by date_trunc('minute', t1.timestamp_start)
你可以找到一个有效的例子here; rextester没有redshift模拟器,但SQL Server具有执行相同任务的功能,所以没什么大不了的。