1

我最近开始使用 postgres,我来自 oracle 背景。只是想知道我写的查询是否可以在 postgres 中以更好的方式实现。

问题详情:

我有两张桌子:

  1. 使用细节
  2. 节目信息

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 的数组和 unnest 功能。

谁能建议使用 Array/unnest 或 postgres 中可用的任何其他功能来解决此问题的最佳方法是什么?我不是在寻找确切的代码,只是在寻找方向。

4

1 回答 1

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_detailprogram_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)

*是范围的交集运算符。以上返回:

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

在线示例:http ://rextester.com/VNXIG64065

于 2016-10-31T10:00:19.410 回答