我最近开始使用postgres,我来自oracle背景。只是想知道我写的查询是否可以在postgres中以更好的方式实现。
问题详情:
我有两张桌子:
Usage_detail包含有关观看频道的任何用户的信息。例如,用户A的会话长度为1小时10分0秒,从今天下午1:15开始
User start_time end_time
A 2016-10-31 13:15:00 2016-10-31 14:25:00
Program_info表包含预定的程序详细信息和相应的类别。
例如:
Program_id program_category week_day start_time end_time
1 News Monday 13:00 13:30
2 Sports Monday 13:30 14:30
我正在寻找的输出是:
User program_category start_time duration (in seconds)
A News 2016-10-31 13:15:00 900
A Sports 2016-10-31 13:30:00 3300
我目前的做法:
我将start_time和end_time持续时间分为30分钟(因为程序类别可能每30分钟更改一次)。就像我提到的例子一样,我首先创建了3条记录(从下午1:15到下午1:30,下午1:30到下午2:00,下午2:00到下午2:25),然后根据program_category对持续时间求和
我编写了一个不太可读的代码,它可以动态生成一条记录中的多条记录而不使用postgres的数组和不需要的功能。
有人建议使用Array / unnest或postgres中可用的任何其他功能来解决此问题的最佳方法是什么?我不是在寻找确切的代码,只是方向会做。
答案 0 :(得分:1)
我认为你不需要生成任何行。根据您的示例数据,您可以简单地加入这两个表。
select *
from program_info pi
join usage_detail ud
on to_char(ud.start_time, 'FMday') = lower(pi.week_day)
and (pi.start_time, pi.end_time) overlaps (ud.start_time::time, ud.end_time::time)
(我使用user_name
代替user
,因为user
是保留关键字)
请注意,使用to_char(ud.start_time, 'FMday') = lower(pi.week_day)
的联接要求工作日以与to_char()
相同的语言存储,并将其返回。最好将其存储为数字,而不是字符串。
通过该结果,可以计算每个节目的实际开始和结束时间。这可以通过复杂的case when
语句来完成,该语句将usage_detail
中存储的时间信息与来自program_info
的时间信息进行比较,以检查哪个开始时间是较大的,哪个结束时间是较小的一个。
然而,这可以使用时间范围来简化。遗憾的是,内置的时间范围没有,但它很容易创建:
create type timerange as range (subtype = time);
可以使用两个范围的交集来计算实际的开始和结束时间:
select ud.user_name,
pi.program_id,
pi.program_category,
ud.start_time::date as start_day,
timerange(pi.start_time, pi.end_time) * timerange(ud.start_time::time, ud.end_time::time) as view_interval
from program_info pi
join usage_detail ud
on to_char(ud.start_time, 'FMday') = lower(pi.week_day)
and (pi.start_time, pi.end_time) overlaps (ud.start_time::time, ud.end_time::time)
*
是范围的intersection operator。以上回复:
user_name | program_id | program_category | start_day | view_interval
----------+------------+------------------+------------+--------------------
A | 1 | News | 2016-10-31 | [13:15:00,13:30:00)
A | 2 | Sports | 2016-10-31 | [13:30:00,14:25:00)
现在可以使用实际观看时间作为范围来获得所需的最终显示:
with view_times as (
select ud.user_name,
pi.program_id,
pi.program_category,
ud.start_time::date as start_day,
timerange(pi.start_time, pi.end_time) * timerange(ud.start_time::time, ud.end_time::time) as view_interval
from program_info pi
join usage_detail ud
on to_char(ud.start_time, 'FMday') = lower(pi.week_day)
and (pi.start_time, pi.end_time) overlaps (ud.start_time::time, ud.end_time::time)
)
select user_name, program_id, program_category,
start_day + lower(view_interval) as actual_start_time,
extract(epoch from (upper(view_interval) - lower(view_interval))) as duration
from view_times
返回:
user_name | program_id | program_category | actual_start_time | duration
----------+------------+------------------+---------------------+---------
A | 1 | News | 2016-10-31 13:15:00 | 900
A | 2 | Sports | 2016-10-31 13:30:00 | 3300