计算时间戳之间的营业时间

los*_*its 3 sql postgresql sqlalchemy

我正在使用Postgres 8.3(目前无法选择版本)。我的原始数据表如下:

ID  start_time               finish_time
01   2013-01-23 10:47:52-05  2013-02-25 11:18:36-05
Run Code Online (Sandbox Code Playgroud)

我可以在两个时间戳之间计数:

--relevant line in view creation query:
date_part('epoch',(finish_time - start_time)::interval)/3600 as hours
Run Code Online (Sandbox Code Playgroud)

我不想包括周末。另外,我只想计数09:00-17:30。

在一个理想的世界中,我每天也要减去一个小时的午餐时间,最终我还希望包括公司假期,但是我想首先解决这个工作时间部分。

有关如何处理此问题的任何建议?我对SQL很陌生。我也乐于使用SQLalchemy,但我也是那里的初学者,对直接SQL感到更自在。

Mik*_*ll' 5

想象一下,您有一张工作表。(或构建一个。这个未经测试,因此可能包含时区和围栏错误。)

create table work_minutes (
  work_minute timestamp primary key
);

insert into work_minutes
select work_minute
from 
  (select generate_series(timestamp '2013-01-01 00:00:00', timestamp '2013-12-31 11:59:00', '1 minute') as work_minute) t
where extract(isodow from work_minute) < 6
  and cast(work_minute as time) between time '09:00' and time '17:30'
Run Code Online (Sandbox Code Playgroud)

现在,您的查询可以数分钟了,那简直就是简单。

select count(*)/60.0 as elapsed_hrs
from work_minutes
where work_minute between '2013-01-23 10:47:52' and '2013-02-25 11:18:36'

elapsed_hours
--
196.4
Run Code Online (Sandbox Code Playgroud)

您可以决定用小数小时做什么。

根据分钟数和小时数的不同,以分钟为单位计算和以小时为单位计算可能会有很大的不同。基于小时的计算在一小时内超出停止时间的分钟可能不会占很多时间。是否重要取决于应用程序。

您可以使用generate_series()动态生成这样的虚拟表,但是这样的基本表只需要大约4百万行就可以覆盖30年,并且这种计算确实非常快。

以后。。。

我看到Erwin Brandstetter讨论了现代PostgreSQL对generate_series()的使用;它在8.3版中不起作用,因为8.3不支持通用表表达式或generate_series(timestamp,timestamp)。这是避免这些问题的Erwin查询版本。这不是一个完全忠实的翻译;计算相差一个小时。就我而言,这可能是一个栅栏错误,但是我现在没有时间深入研究细节。

select count(*) from 
(select timestamp '2013-01-23 10:47:52-05' + (n || ' hours')::interval
from generate_series(  0
                     , (extract(days from timestamp '2013-02-25 11:18:36-05' 
                                        - timestamp '2013-01-23 10:47:52-05')::integer * 24) ) n
where extract(isodow from (timestamp '2013-01-23 10:47:52-05' + (n || ' hours')::interval)) < 6
  and (timestamp '2013-01-23 10:47:52-05' + (n || ' hours')::interval)::time >= '09:00'::time
  and (timestamp '2013-01-23 10:47:52-05' + (n || ' hours')::interval)::time <  '17:30'::time
 ) t
Run Code Online (Sandbox Code Playgroud)

基于表的解决方案具有易于处理管理异想天开的优势。“嘿!我们的狗有七只小狗!今天半天!” 它也可以很好地扩展,并且无需修改即可在几乎所有平台上运行。

如果使用generate_series(),则将其包装在视图中。这样,可以在一个地方管理对规则的任意更改。而且,如果规则变得太复杂而无法在视图中轻松维护,则可以用具有相同名称的表替换视图,所有应用程序代码,SQL以及存储过程和函数都将起作用。