我有一个postgresql 9.1表,其中包含开始和结束时间戳,用于记录可能与userid重叠的时间段。例如:
Userid Begin End
1 2014-01-19 21:14:59+00 2014-01-19 21:30:00+00
2 2014-01-19 21:19:29+00 2014-01-19 21:40:30+00
1 2014-01-19 21:16:29+00 2014-01-19 21:31:30+00
3 2014-01-19 21:15:22+00 2014-01-19 21:30:29+00
2 2014-01-19 21:29:59+00 2014-01-19 21:45:00+00
3 2014-01-19 21:15:25+00 2014-01-19 21:35:10+00
4 2014-01-19 22:00:01+00 2014-01-19 22:05:20+00
我需要获得这些行中每个Userid的所有分钟数的总和,确保单个Userid的时间不会重复计算,因此输出将为:
Userid Hour Total Minutes
1 21 n
2 21 n
3 21 n
4 22 n
答案 0 :(得分:1)
这是一个难题,但至少Postgres具有lag()
功能。
这是个主意。假设你有重叠。找到没有重叠的第一个元素。给它一个标志值1.然后做这个值的累积和。结果是,现在根据不同的“岛屿”为不同的时间段分配值。然后简单的聚合工作。
以下内容适用于许多情况,使用lag()
:
select userid, sum(secs) / 60 as minutes
from (select userid, Island, min(begin) as begin, max(end) as end,
extract(epoch from max(end) - min(begin)) as secs
from (select t.*,
sum(IslandBegin) over (partition by userid order by begin) as Island
from (select t.*,
(case when lag(end) over (partition by userid order by begin) >= begin
then 0
else 1
end) as IslandBegin
from table t
) t
) t
group by userid, Island
) t
group by userid;
请注意,end
是保留字,因此请相应地调整代码。
以上并不总是奏效。它假设重叠与前一个开始,并且可能不是这种情况。考虑{(1,100),(2,5),(8,10)}。我认为正确的逻辑仍然需要相关的子查询。最里面的查询需要从:
更改 from (select t.*,
(case when lag(end) over (partition by userid order by begin) >= begin
then 0
else 1
end) as IslandBegin
from table t
) t
为:
from (select t.*,
coalesce((select 1
from table t2
where t2.end >= t.begin and
t2.begin < t.begin
limit 1
), 0
) as IslandBegin
from table t
) t
实际上,这并不是那么糟糕,我了解到lag()
不能用于所有这些情况。