【问题标题】:use generate series使用生成系列
【发布时间】:2021-04-17 13:15:03
【问题描述】:

我正在编写一个 psql 过程来读取源表,然后聚合并写入聚合表。 我的表源包含 2 列 beg,end 是指客户端连接到网站,客户端断开连接。 我想为每个客户计算他花费的时间。使用生成系列的目的是当事件超过一天时。

我的伪代码如下

execute $$SELECT MAX(date_) FROM $$||aggregate_table INTO max_date;
IF max_date is not NULL THEN


execute $$DELETE FROM $$||aggregate_table||$$ WHERE date_ >= $$||quote_literal(max_date);
ELSE
  max_date := 'XXXXXXX';
end if;


SELECT * from (
   select
   Id, gs.due_date,
  (case
     When TRIM(set) ~ '^OPT[0-9]{3}/MINUTE/$'
     Then 'minute'
     When TRIM(set) ~ '^OPT[0-9]{3}/SECOND/$'
     Then 'second'
     as TIME, 
  sum(extract(epoch from (least(s.end, gs.date_ + interval '1 day') -
                           greatest(s.beg, gs.date_)
                          )
              ) / 60) as Timing
 from source s cross join lateral
generate_series(date_trunc(‘day’, s.beg), date_trunc('day',
     least(s.end,
     CASE WHEN $$||quote_literal(max_date)||$$ = ‘XXXXXXX’
          THEN (current_date)
          ELSE $$||quote_literal(max_date)||$$
     END)
  ), interval '1 day’) gs(date_)
  where ( (beg, end) overlaps ($$||quote_literal(max_date)||$$'00:00:00',    $$||quote_literal(max_date)||$$'23:59:59’))
group by id, gs.date_, TIME
 ) as X
where ($$||quote_literal(max_date)||$$ = X.date_  and $$||quote_literal(max_date)||$$ != ‘XXXXXXX’)
OR  ($$||quote_literal(max_date)||$$ ='XXXXXXX')

表源数据

number, beg, end, id, set
(10, '2019-10-25 13:00:00', '2019-10-25 13:30:00', 1234, 'OPT111/MINUTE/'),
(11, '2019-10-25 13:00:00', '2019-10-25 14:00:00', 1234, 'OPT111/MINUTE/'),
(12, '2019-11-04 09:19:00', '2019-11-04 09:29:00', 1124, 'OPT111/SECOND/'),
(13, '2019-11-04 22:00:00', '2019-11-05 02:00:00', 1124, 'OPT111/MINUTE/')

Expected_output 聚合表

2019-10-25, 1234, MINUTE, 90(1h30)
2019-11-04, 1124, SECOND, 10
2019-11-04, 1124, MINUTE, 120
2019-11-05, 1124, MINUTE, 120

我的代码的问题是,如果我有明天将添加的新行,例如 (14, '2019-11-06 12:00:00', '2019-11- 06 13:00:00',1124,'OPT111/MINUTE/')。

请哪位大神帮忙?

谢谢

【问题讨论】:

  • 对不起它的 psql @jarlh 它的我。
  • 请解释逻辑。真的不清楚你要做什么。
  • @GordonLinoff 谢谢我添加描述。如果还不清楚,请告诉我。丹克
  • @GordonLinoff 我清楚了吗?如果不告诉我,请告诉我
  • 将跨越午夜的每条记录拆分为两条记录(一条记录直到午夜,另一条记录在午夜之后)为UNION CTE,然后进行聚合。

标签: sql postgresql plpgsql dynamic-sql


【解决方案1】:

这是我的解决方案。为了避免保留字,我更改了列名。您可能需要触摸duration 的格式。

with mycte as
(
 select -- the first / first and only days
    id, col_beg,
    case when col_beg::date = col_end::date then col_end else date_trunc('day', col_end) end as col_end
 from mytable 

 union all
 select -- the last days of multi-day periods
    id, date_trunc('day', col_end) as col_beg, col_end
 from mytable 
 where col_end::date > col_beg::date

 union all
 select -- the middle days of multi-day periods 
    id, rd as col_beg, rd::date + 1 as col_end
 from mytable
    cross join lateral generate_series(col_beg::date + 1, col_end::date - 1, interval '1 day') g(rd)
 where col_end::date > col_beg::date + 1
)
 select 
    col_beg::date as start_time, id, sum(col_end - col_beg) as duration
 from mycte group by 1, 2 order by 1;

【讨论】:

  • 谢谢你,我会试试的,会告诉你的。请问你的mycte是什么?
  • 我应该把这个 case 放在哪里当 TRIM(set) ~ '^OPT[0-9]{3}/MINUTE/$' 然后 'minute' 当 TRIM(set) ~ '^OPT[0-9]{3}/SECOND/$' 然后 'second' 作为 TIME ?
  • mycte 是一个common table expression,它是一种临时内联表。更好地使用to_char 函数将时间间隔格式化为文本
  • 好的,更清楚了,谢谢! @Stefanov.sm 我可以在您的代码行上方插入吗?插入 $$||aggregate_table||$$ (vh_nbr, start_time, duration,TIME)
  • 您的解决方案中的问题是,如果我有这种情况 (13, '2019-11-04 22:00:00', '2019-11-07 02:00:00', 1124 , 'OPT111/分钟/') .我应该有 4 行。你明白我的意思吗?
猜你喜欢
  • 2021-10-30
  • 2015-05-01
  • 2021-03-31
  • 1970-01-01
  • 2011-10-31
  • 1970-01-01
  • 1970-01-01
  • 2018-08-22
  • 2016-02-14
相关资源
最近更新 更多