Sql 日期范围为多个记录的工作日之和(重叠)
假设有如下记录:Sql 日期范围为多个记录的工作日之和(重叠),sql,oracle,sum,overlap,Sql,Oracle,Sum,Overlap,假设有如下记录: Employee_id, work_start_date, work_end_date 1, 01-jan-2014, 07-jan-2014 1, 03-jan-2014, 12-jan-2014 1, 23-jan-2014, 25-jan-2014 2, 15-jan-2014, 25-jan-2014 2, 07-jan-2014, 15-jan-2014 2, 09-jan-2014, 12-jan-2014 要求是编写一个SQL select语句,该语句将汇总按
Employee_id, work_start_date, work_end_date
1, 01-jan-2014, 07-jan-2014
1, 03-jan-2014, 12-jan-2014
1, 23-jan-2014, 25-jan-2014
2, 15-jan-2014, 25-jan-2014
2, 07-jan-2014, 15-jan-2014
2, 09-jan-2014, 12-jan-2014
要求是编写一个SQL select语句,该语句将汇总按员工id分组的工作日,但不包括重叠的时段,即只计算一次
所需的输出将是:
Employee_id, worked_days
1, 13
2, 18
日期范围内工作日的计算如下所示:
如果工作开始日期=5,工作结束日期=9,则工作天数=49-5
我可以编写一个pl/sql函数来解决这个问题,手动迭代记录并进行计算,但我相信可以使用sql来实现更好的性能
有人能给我指一下正确的方向吗
谢谢 这是一个棘手的问题。例如,您不能使用滞后,因为重叠时段可能不是上一个时段。或者,不同的时段可以在同一天开始或停止 我们的想法是重建这些时期。如何做到这一点?查找周期开始的记录-也就是说,与任何其他周期都没有重叠。然后将其用作标志,并累计计算该标志以计算重叠组。那么,从那里获得工作日只是一个聚合:
with ps as (
select e.*,
(case when exists (select 1
from emps e2
where e2.employee_id = e.employee_id and
e2.work_start_date <= e.work_start_date and
e2.work_end_date >= e.work_end_date
)
then 0 else 1
) as IsPeriodStart
from emps e
)
select employee_id, sum(work_end_date - work_start_date) as Days_Worked
from (select employee_id, min(work_start_date) as work_start_date,
max(work_end_date) as work_end_date
from (select ps.*,
sum(IsPeriod_Start) over (partition by employee_id
order by work_start_date
) as grp
from ps
) ps
group by employee_id, grp
) ps
group by employee_id;
日期类型
函数结果为一个日期介于两个参数之间的表
SQL:
这是对类似问题的一个稍加修改的查询:
演示:员工1不应该是15=1月1日至12日加1月23日至25日吗?谢谢戈登。这可以用多种方式来解释,包括/排除,所以我清楚地说明了计算方法。我最喜欢这一种。另外,您为解决方案提供了演示也很好。顺便说一句,如果我只想计算重叠的工作日,解决方案会是什么样的?例如,员工id=1的重叠天数为2014年1月3日-2014年1月7日=4天…?是否为潜在客户。。。只是在所有日期之间创建相邻段?如果是,WHERE将检查这些段中的每一段,以查看是否包括或排除?在这种情况下,如果有一个不相关的较高范围,因为第一个和第二个集合一直都满足,那么不是每个低段都会被包括在内吗?测试范围:“2016-01-01”至“2016-01-31”,“2016-02-10”至“2016-02-20”,“2016-03-01”至“2016-03-31”-1月31日至2月10日应排除在外,但是否包括在内?
create or replace package RG_TYPE is
type date_tbl is table of date;
end;
create or replace function dates
(
p_from date,
p_to date
) return rg_type.date_tbl pipelined
is
l_idx date:=p_from;
begin
loop
if l_idx>nvl(p_to,p_from) then
exit;
end if;
pipe row(l_idx);
l_idx:=l_idx+1;
end loop;
return;
end;
select employee_id,sum(c)
from
(select e.employee_id,d.column_value,count(distinct w.employee_id) as c
from (select distinct employee_id from works) e,
table(dates((select min(work_start_date) as a from works),(select max(work_end_date) as b from works))) d,
works w
where e.employee_id=w.employee_id
and d.column_value>=w.work_start_date
and d.column_value<w.work_end_date
group by e.employee_id,d.column_value) Sub
group by employee_id
order by 1,2
SELECT "Employee_id",
SUM( "work_end_date" - "work_start_date" )
FROM(
SELECT "Employee_id",
"work_start_date" ,
lead( "work_start_date" )
over (Partition by "Employee_id"
Order by "Employee_id", "work_start_date" )
As "work_end_date"
FROM (
SELECT "Employee_id", "work_start_date"
FROM Table1
UNION
SELECT "Employee_id","work_end_date"
FROM Table1
) x
) x
WHERE EXISTS (
SELECT 1 FROM Table1 t
WHERE t."work_start_date" > x."work_end_date"
AND t."work_end_date" > x."work_start_date"
OR t."work_start_date" = x."work_start_date"
AND t."work_end_date" = x."work_end_date"
)
GROUP BY "Employee_id"
;