我正在尝试更新存储过程,该存储过程确定从收到故障单开始的响应时间。在表中,我有收到故障单时的时间戳(ref_dttm TIMESTAMP WITHOUT TIME ZONE)和首次响应故障单时的时间戳(first_action_dttm TIMESTAMP WITHOUT TIME ZONE)。在计算响应时间时,我需要考虑营业时间,周末和假期关闭。
目前该功能计算间隔并可以减去业务关闭的小时数,但我似乎无法找出排除周末和假期的方法。基本上我需要每周减去15小时(开放时间为0900-1800),每周末和假期减去24小时。
给出收到门票的星期几和时间跨度:
Select
extract(dow from ref_dttm) as dow,
extract(days from (ref_dttm - first_action_dttm) as days
有没有一种简单的方法可以确定已经过了多少个周末?
这是我到目前为止 - 它每天减去15小时而不考虑周末:
CREATE TEMP TABLE tmp_ticket_delta ON COMMIT DROP AS
SELECT id,ticket_id,ticket_num
,(ticket_dttm - first_action_dttm) as delta
,extract(days from (ticket_dttm - first_action_dttm)) as days
,ticket_descr
FROM t_tickets
WHERE ticket_action_by > 0
SELECT id,ticket_id,ticket_num,delta,days,ticket_descr,
CASE WHEN days = 0 THEN
CASE WHEN extract(hour from delta) > 15 THEN
--less than one day but outside of business hours so subtract 15 hrs
delta - INTERVAL '15:00:00.000'
ELSE
delta
END
ELSE
CASE WHEN extract(hour from delta) > 15 THEN
--take the total number of hours - closing hours + delta - closed hours
(((days * 24) - (days * 15)) * '1 hour'::INTERVAL) + delta - INTERVAL '15:00:00.000' - (days * '1 day'::INTERVAL)
ELSE
(((days * 24) - (days * 15)) * '1 hour'::INTERVAL) + delta - (days * '1 day'::INTERVAL)
END
END AS adj_diff
FROM tmp_ticket_delta
答案 0 :(得分:1)
我喜欢将重要的业务数据存储在表中。像这样的查询
select min(cal_date),
max(cal_date),
sum(hours_open) total_time_open,
sum(hours_closed) total_time_closed
from daily_hours_open_and_closed
where cal_date between '2013-08-28' and '2013-09-03';
当基于存储在简单表中的数据时,它们易于理解,维护和调试。
我从一个calendar table开始,并在你的地方打开的时候添加一张桌子。此表“open_times”是最简单的启动方式,但对您的业务来说可能过于简单。例如,您可能需要更严格的CHECK约束。此外,虽然最终查询在我的开发盒上只运行了12毫秒,但我没有尝试提高效率。
create table open_times (
bus_open timestamp primary key,
bus_closed timestamp not null
check (bus_closed > bus_open)
);
快速而肮脏的方式,以2013年的工作日时间填充该表。
with openings as (
select generate_series(timestamp '2013-01-01 09:00',
timestamp '2013-12-31 18:00', '1 day') bus_open
)
insert into open_times
select bus_open, bus_open + interval '9 hours' bus_closed
from openings
where extract(dow from bus_open) between 1 and 5
order by bus_open;
劳动节是这里的假期,所以9月2日星期一是假期。删除2013-09-02。
delete from open_times
where bus_open = '2013-09-02 09:00';
这是我唯一感兴趣的节日,目的是展示它是如何运作的。当然,你必须比我做得更好。
为了使事情更简单,创建一个视图,将每日操作时间显示为间隔。
create view daily_hours_open_and_closed as
select c.cal_date,
ot.bus_open,
ot.bus_closed,
coalesce(bus_closed - bus_open, interval '0 hours') as hours_open,
interval '24 hours' - (coalesce(bus_closed - bus_open, interval '0 hours')) as hours_closed
from calendar c
left join open_times as ot
on c.cal_date = cast(ot.bus_open as date);
现在,我们在2013-08-28和2013-09-03之间的7天内开放了多少小时以及关闭了多少小时?现在对原始数据的查询很简单。
select *
from daily_hours_open_and_closed
where cal_date between '2013-08-28' and '2013-09-03'
order by cal_date;
cal_date bus_open bus_closed hours_open hours_closed
--
2013-08-28 2013-08-28 09:00:00 2013-08-28 18:00:00 09:00:00 15:00:00
2013-08-29 2013-08-29 09:00:00 2013-08-29 18:00:00 09:00:00 15:00:00
2013-08-30 2013-08-30 09:00:00 2013-08-30 18:00:00 09:00:00 15:00:00
2013-08-31 00:00:00 24:00:00
2013-09-01 00:00:00 24:00:00
2013-09-02 00:00:00 24:00:00
2013-09-03 2013-09-03 09:00:00 2013-09-03 18:00:00 09:00:00 15:00:00
使用聚合函数进行算术运算。
select min(cal_date),
max(cal_date),
sum(hours_open) total_time_open,
sum(hours_closed) total_time_closed
from daily_hours_open_and_closed
where cal_date between '2013-08-28' and '2013-09-03'
min max total_time_open total_time_closed
--
2013-08-28 2013-09-03 36:00:00 132:00:00
答案 1 :(得分:0)
您可以通过此查询之类的方式计算周末:
select
*,
(extract(week from ref_dttm) - extract(week from first_action_dttm)) * 2 -
case extract(dow from first_action_dttm) when 0 then 1 else 0 end +
case extract(dow from ref_dttm) when 0 then 2 when 6 then 1 else 0 end
from t_tickets
上试试
或者如果您的日期可能有不同的年份:
select
*,
trunc(date_part('day', ref_dttm - first_action_dttm) / 7) * 2 +
case extract(dow from first_action_dttm) when 0 then 1 when 6 then 2 else 0 end +
case extract(dow from ref_dttm) when 6 then 1 when 0 then 2 else 0 end -
case when extract(dow from ref_dttm) = extract(dow from first_action_dttm) then 2 else 0 end as weekends
from t_tickets
上试试
答案 2 :(得分:0)
您可以使用generate_series
计算间隔周六和周日的数量:
-- sample data
with t as (
(select 1 as id, '2012-01-01'::timestamp as tstart, '2012-02-01'::timestamp as tend) union all -- 9
(select 2 as id, '2011-12-31'::timestamp as tstart, '2012-02-04'::timestamp as tend) union all -- 11
(select 3 as id, '2011-12-30'::timestamp as tstart, '2012-02-05'::timestamp as tend) union all -- 12
(select 4 as id, '2011-12-30'::timestamp as tstart, '2012-02-07'::timestamp as tend) -- 12
)
-- Calculate number of weekend days
select
id,
sum((dow not between 1 and 5)::int) number_of_weekend_days
from
(select id, extract(dow from generate_series(tstart,tend,'1 day')) as dow from t) x
group by
id
如果您有大量数据,我认为这会非常慢。