2013-08-27 112 views
2

我試圖更新一個存儲過程,該過程確定從接收票證時的響應時間。在表格中,我有收到票證時的時間戳(ref_dttm TIMESTAMP WITHOUT TIME ZONE)和第一次響應票證時的時間戳(first_action_dttm TIMESTAMP WITHOUT TIME ZONE)。在計算響應時間時,我需要考慮運營時間,週末和假期關閉。Postgres函數來確定一個時間間隔內的週末數

目前該函數計算的時間間隔,可以減去他們的業務關閉的時間,但我似乎無法找出排除週末和節假日的方法。基本上我需要在每個週末日和假期中每週減去15小時(打開0900-1800)和24小時。

鑑於票收到星期和時間跨度:

Select 
    extract(dow from ref_dttm) as dow, 
    extract(days from (ref_dttm - first_action_dttm) as days 

有沒有一種簡單的方法來確定有多少個週末已經過去了?

這是我迄今爲止 - 它減去15個小時每天,不考慮週末:

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 

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

每星期一到星期五的9個小時算不算容易嗎? – dcaswell

回答

0

我喜歡在表格中存儲重要的業務數據。像這樣的查詢

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; 

勞動節是這裏的節日,所以週一,09月02日是一個節日。刪除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年8月28日和2013年9月3日之間的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 
0

你可以通過這樣的查詢次數,週末試穿sql fiddle demo

或者如果你的日期可能有不同的年份:

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 

試試sql fiddle demo

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 

我認爲這將是很慢的,如果你有大量的數據。

相關問題