SQL:查找多行的连续日期范围?

时间:2013-05-16 19:36:48

标签: sql-server sql-server-2008 tsql sql-server-2008-r2

我正在努力获得一个开始和结束日期组合,以便连续工作时间跨度。跨距可以跨越多行,其中第一行的结束日期与下一行的结束日期相同。预期结果是显示连续日期范围,以及该范围的工作小时数。

person  startdate                enddate                   hours
------  -----------------------  -----------------------  ------
5163    2013-04-29 07:00:00.000  2013-04-29 11:00:00.000    4.00
5163    2013-04-29 11:30:00.000  2013-04-29 15:30:00.000    4.00
5163    2013-04-29 15:30:00.000  2013-04-29 19:06:00.000    3.60
5851    2013-05-02 19:00:00.000  2013-05-02 23:00:00.000    4.00
5851    2013-05-02 23:00:00.000  2013-05-03 00:00:00.000    1.00
5851    2013-05-03 00:00:00.000  2013-05-03 00:31:00.000    0.52

根据以上数据,我想要以下内容。

person  startdate                enddate                   hours
------  -----------------------  -----------------------  ------
5163    2013-04-29 07:00:00.000  2013-04-29 11:00:00.000    4.00
5163    2013-04-29 11:30:00.000  2013-04-29 19:06:00.000    7.60
5851    2013-05-02 19:00:00.000  2013-05-03 00:31:00.000    5.52

对于每个人和新的(非连续)日期范围,将当前行的enddate与下一行的startdate进行比较。如果它们相同,则累积小时数并继续处理行,直到enddate / startdate不相等。

环境是SQL Server 2008 R2。我使用row_number和partition()函数尝试了涉及自联接的查询,但是无法获得成功的解决方案。谢谢!

编辑:以下是RichardTheKiwi解决方案的数据流 - 我为一个人运行它,看看为一周的打击量产生了多少递归。

declare @startdate datetime;
    set @startdate = '20130429';
declare @enddate datetime;
    set @enddate = '20130506';

with tbl as (
select 
PERSONNUM,
STARTDTM,
ENDDTM,
convert(decimal(10,2),1.0 * TIMEINSECONDS / 3600) as timeinhours
from vp_totals
where paycodetype = 'p'
and applydate >= @startdate and APPLYDATE < @enddate 
and (paycodename like '%regular%'
     or paycodename like '%overtime%'
     or PAYCODENAME like '%double time%')
and (PAYCODENAME not like '%shift premium%')
and PERSONNUM = 'loh-5851'
)

select * from tbl order by startdtm -- 27 rows


PERSONNUM       STARTDTM            ENDDTM               timeinhours 
LOH-5851        2013-04-29 14:30:00 2013-04-29 18:30:00  4.0000 
LOH-5851        2013-04-29 19:00:00 2013-04-29 23:00:00  4.0000 
LOH-5851        2013-04-29 23:00:00 2013-04-30 00:00:00  1.0000 
LOH-5851        2013-04-30 00:00:00 2013-04-30 00:11:00  0.1800 
LOH-5851        2013-04-30 14:45:00 2013-04-30 18:45:00  4.0000 
LOH-5851        2013-04-30 19:15:00 2013-04-30 23:00:00  3.7500 
LOH-5851        2013-04-30 23:00:00 2013-04-30 23:15:00  0.2500 
LOH-5851        2013-04-30 23:15:00 2013-05-01 00:00:00  0.7500 
LOH-5851        2013-05-01 00:00:00 2013-05-01 00:11:00  0.1800 
LOH-5851        2013-05-01 14:30:00 2013-05-01 18:30:00  4.0000 
LOH-5851        2013-05-01 19:00:00 2013-05-01 23:00:00  4.0000 
LOH-5851        2013-05-01 23:00:00 2013-05-02 00:00:00  1.0000 
LOH-5851        2013-05-02 00:00:00 2013-05-02 00:22:00  0.3700 
LOH-5851        2013-05-02 14:30:00 2013-05-02 18:30:00  4.0000 
LOH-5851        2013-05-02 19:00:00 2013-05-02 23:00:00  4.0000 
LOH-5851        2013-05-02 23:00:00 2013-05-03 00:00:00  1.0000 
LOH-5851        2013-05-03 00:00:00 2013-05-03 00:31:00  0.5200 
LOH-5851        2013-05-03 14:45:00 2013-05-03 17:45:00  3.0000 
LOH-5851        2013-05-03 17:45:00 2013-05-03 18:45:00  1.0000 
LOH-5851        2013-05-03 19:15:00 2013-05-03 23:00:00  3.7500 
LOH-5851        2013-05-03 23:00:00 2013-05-03 23:15:00  0.2500 
LOH-5851        2013-05-03 23:15:00 2013-05-04 00:00:00  0.7500 
LOH-5851        2013-05-04 00:00:00 2013-05-04 00:15:00  0.2500 
LOH-5851        2013-05-04 14:00:00 2013-05-04 18:00:00  4.0000 
LOH-5851        2013-05-04 18:30:00 2013-05-04 22:30:00  4.0000 
LOH-5851        2013-05-04 22:30:00 2013-05-04 23:00:00  0.5000 
LOH-5851        2013-05-04 23:00:00 2013-05-04 23:30:00  0.5000 


,cte as (
    select personnum, startdtm, enddtm, timeinhours
    from tbl
    union all
    select t.personnum, cte.startdtm, t.enddtm, cast(cte.timeinhours + t.timeinhours as decimal(10,2))
    from cte
    join tbl t on cte.personnum = t.personnum and cte.enddtm = t.startdtm
)

select * from cte order by startdtm, timeinhours option (maxrecursion 32000) -- 52 rows



personnum       startdtm            enddtm               timeinhours 
LOH-5851        2013-04-29 14:30:00 2013-04-29 18:30:00  4.0000 
LOH-5851        2013-04-29 19:00:00 2013-04-29 23:00:00  4.0000 
LOH-5851        2013-04-29 19:00:00 2013-04-30 00:00:00  5.0000 
LOH-5851        2013-04-29 19:00:00 2013-04-30 00:11:00  5.1800 
LOH-5851        2013-04-29 23:00:00 2013-04-30 00:00:00  1.0000 
LOH-5851        2013-04-29 23:00:00 2013-04-30 00:11:00  1.1800 
LOH-5851        2013-04-30 00:00:00 2013-04-30 00:11:00  0.1800 
LOH-5851        2013-04-30 14:45:00 2013-04-30 18:45:00  4.0000 
LOH-5851        2013-04-30 19:15:00 2013-04-30 23:00:00  3.7500 
LOH-5851        2013-04-30 19:15:00 2013-04-30 23:15:00  4.0000 
LOH-5851        2013-04-30 19:15:00 2013-05-01 00:00:00  4.7500 
LOH-5851        2013-04-30 19:15:00 2013-05-01 00:11:00  4.9300 
LOH-5851        2013-04-30 23:00:00 2013-04-30 23:15:00  0.2500 
LOH-5851        2013-04-30 23:00:00 2013-05-01 00:00:00  1.0000 
LOH-5851        2013-04-30 23:00:00 2013-05-01 00:11:00  1.1800 
LOH-5851        2013-04-30 23:15:00 2013-05-01 00:00:00  0.7500 
LOH-5851        2013-04-30 23:15:00 2013-05-01 00:11:00  0.9300 
LOH-5851        2013-05-01 00:00:00 2013-05-01 00:11:00  0.1800 
LOH-5851        2013-05-01 14:30:00 2013-05-01 18:30:00  4.0000 
LOH-5851        2013-05-01 19:00:00 2013-05-01 23:00:00  4.0000 
LOH-5851        2013-05-01 19:00:00 2013-05-02 00:00:00  5.0000 
LOH-5851        2013-05-01 19:00:00 2013-05-02 00:22:00  5.3700 
LOH-5851        2013-05-01 23:00:00 2013-05-02 00:00:00  1.0000 
LOH-5851        2013-05-01 23:00:00 2013-05-02 00:22:00  1.3700 
LOH-5851        2013-05-02 00:00:00 2013-05-02 00:22:00  0.3700 
LOH-5851        2013-05-02 14:30:00 2013-05-02 18:30:00  4.0000 
LOH-5851        2013-05-02 19:00:00 2013-05-02 23:00:00  4.0000 
LOH-5851        2013-05-02 19:00:00 2013-05-03 00:00:00  5.0000 
LOH-5851        2013-05-02 19:00:00 2013-05-03 00:31:00  5.5200 
LOH-5851        2013-05-02 23:00:00 2013-05-03 00:00:00  1.0000 
LOH-5851        2013-05-02 23:00:00 2013-05-03 00:31:00  1.5200 
LOH-5851        2013-05-03 00:00:00 2013-05-03 00:31:00  0.5200 
LOH-5851        2013-05-03 14:45:00 2013-05-03 17:45:00  3.0000 
LOH-5851        2013-05-03 14:45:00 2013-05-03 18:45:00  4.0000 
LOH-5851        2013-05-03 17:45:00 2013-05-03 18:45:00  1.0000 
LOH-5851        2013-05-03 19:15:00 2013-05-03 23:00:00  3.7500 
LOH-5851        2013-05-03 19:15:00 2013-05-03 23:15:00  4.0000 
LOH-5851        2013-05-03 19:15:00 2013-05-04 00:00:00  4.7500 
LOH-5851        2013-05-03 19:15:00 2013-05-04 00:15:00  5.0000 
LOH-5851        2013-05-03 23:00:00 2013-05-03 23:15:00  0.2500 
LOH-5851        2013-05-03 23:00:00 2013-05-04 00:00:00  1.0000 
LOH-5851        2013-05-03 23:00:00 2013-05-04 00:15:00  1.2500 
LOH-5851        2013-05-03 23:15:00 2013-05-04 00:00:00  0.7500 
LOH-5851        2013-05-03 23:15:00 2013-05-04 00:15:00  1.0000 
LOH-5851        2013-05-04 00:00:00 2013-05-04 00:15:00  0.2500 
LOH-5851        2013-05-04 14:00:00 2013-05-04 18:00:00  4.0000 
LOH-5851        2013-05-04 18:30:00 2013-05-04 22:30:00  4.0000 
LOH-5851        2013-05-04 18:30:00 2013-05-04 23:00:00  4.5000 
LOH-5851        2013-05-04 18:30:00 2013-05-04 23:30:00  5.0000 
LOH-5851        2013-05-04 22:30:00 2013-05-04 23:00:00  0.5000 
LOH-5851        2013-05-04 22:30:00 2013-05-04 23:30:00  1.0000 
LOH-5851        2013-05-04 23:00:00 2013-05-04 23:30:00  0.5000 



,cte2 as (
    select *, rn = row_number() over (partition by personnum, enddtm order by startdtm)
    from cte
)

select * from cte2 order by startdtm, rn -- 52 rows


personnum       startdtm            enddtm               timeinhours        rn
LOH-5851        2013-04-29 14:30:00 2013-04-29 18:30:00  4.0000             1
LOH-5851        2013-04-29 19:00:00 2013-04-29 23:00:00  4.0000             1
LOH-5851        2013-04-29 19:00:00 2013-04-30 00:00:00  5.0000             1
LOH-5851        2013-04-29 19:00:00 2013-04-30 00:11:00  5.1800             1
LOH-5851        2013-04-29 23:00:00 2013-04-30 00:11:00  1.1800             2
LOH-5851        2013-04-29 23:00:00 2013-04-30 00:00:00  1.0000             2
LOH-5851        2013-04-30 00:00:00 2013-04-30 00:11:00  0.1800             3
LOH-5851        2013-04-30 14:45:00 2013-04-30 18:45:00  4.0000             1
LOH-5851        2013-04-30 19:15:00 2013-04-30 23:00:00  3.7500             1
LOH-5851        2013-04-30 19:15:00 2013-04-30 23:15:00  4.0000             1
LOH-5851        2013-04-30 19:15:00 2013-05-01 00:11:00  4.9300             1
LOH-5851        2013-04-30 19:15:00 2013-05-01 00:00:00  4.7500             1
LOH-5851        2013-04-30 23:00:00 2013-05-01 00:00:00  1.0000             2
LOH-5851        2013-04-30 23:00:00 2013-05-01 00:11:00  1.1800             2
LOH-5851        2013-04-30 23:00:00 2013-04-30 23:15:00  0.2500             2
LOH-5851        2013-04-30 23:15:00 2013-05-01 00:11:00  0.9300             3
LOH-5851        2013-04-30 23:15:00 2013-05-01 00:00:00  0.7500             3
LOH-5851        2013-05-01 00:00:00 2013-05-01 00:11:00  0.1800             4
LOH-5851        2013-05-01 14:30:00 2013-05-01 18:30:00  4.0000             1
LOH-5851        2013-05-01 19:00:00 2013-05-01 23:00:00  4.0000             1
LOH-5851        2013-05-01 19:00:00 2013-05-02 00:00:00  5.0000             1
LOH-5851        2013-05-01 19:00:00 2013-05-02 00:22:00  5.3700             1
LOH-5851        2013-05-01 23:00:00 2013-05-02 00:22:00  1.3700             2
LOH-5851        2013-05-01 23:00:00 2013-05-02 00:00:00  1.0000             2
LOH-5851        2013-05-02 00:00:00 2013-05-02 00:22:00  0.3700             3
LOH-5851        2013-05-02 14:30:00 2013-05-02 18:30:00  4.0000             1
LOH-5851        2013-05-02 19:00:00 2013-05-02 23:00:00  4.0000             1
LOH-5851        2013-05-02 19:00:00 2013-05-03 00:00:00  5.0000             1
LOH-5851        2013-05-02 19:00:00 2013-05-03 00:31:00  5.5200             1
LOH-5851        2013-05-02 23:00:00 2013-05-03 00:31:00  1.5200             2
LOH-5851        2013-05-02 23:00:00 2013-05-03 00:00:00  1.0000             2
LOH-5851        2013-05-03 00:00:00 2013-05-03 00:31:00  0.5200             3
LOH-5851        2013-05-03 14:45:00 2013-05-03 17:45:00  3.0000             1
LOH-5851        2013-05-03 14:45:00 2013-05-03 18:45:00  4.0000             1
LOH-5851        2013-05-03 17:45:00 2013-05-03 18:45:00  1.0000             2
LOH-5851        2013-05-03 19:15:00 2013-05-03 23:00:00  3.7500             1
LOH-5851        2013-05-03 19:15:00 2013-05-03 23:15:00  4.0000             1
LOH-5851        2013-05-03 19:15:00 2013-05-04 00:00:00  4.7500             1
LOH-5851        2013-05-03 19:15:00 2013-05-04 00:15:00  5.0000             1
LOH-5851        2013-05-03 23:00:00 2013-05-04 00:15:00  1.2500             2
LOH-5851        2013-05-03 23:00:00 2013-05-04 00:00:00  1.0000             2
LOH-5851        2013-05-03 23:00:00 2013-05-03 23:15:00  0.2500             2
LOH-5851        2013-05-03 23:15:00 2013-05-04 00:00:00  0.7500             3
LOH-5851        2013-05-03 23:15:00 2013-05-04 00:15:00  1.0000             3
LOH-5851        2013-05-04 00:00:00 2013-05-04 00:15:00  0.2500             4
LOH-5851        2013-05-04 14:00:00 2013-05-04 18:00:00  4.0000             1
LOH-5851        2013-05-04 18:30:00 2013-05-04 22:30:00  4.0000             1
LOH-5851        2013-05-04 18:30:00 2013-05-04 23:00:00  4.5000             1
LOH-5851        2013-05-04 18:30:00 2013-05-04 23:30:00  5.0000             1
LOH-5851        2013-05-04 22:30:00 2013-05-04 23:30:00  1.0000             2
LOH-5851        2013-05-04 22:30:00 2013-05-04 23:00:00  0.5000             2
LOH-5851        2013-05-04 23:00:00 2013-05-04 23:30:00  0.5000             3


select personnum, startdtm, max(enddtm) enddtm, max(timeinhours) timeinhours
from cte2
where rn=1
group by personnum, startdtm
order by personnum, startdtm
option (maxrecursion 32000) -- 12 rows


personnum       startdtm            enddtm               timeinhours 
LOH-5851        2013-04-29 14:30:00 2013-04-29 18:30:00  4.0000 
LOH-5851        2013-04-29 19:00:00 2013-04-30 00:11:00  5.1800 
LOH-5851        2013-04-30 14:45:00 2013-04-30 18:45:00  4.0000 
LOH-5851        2013-04-30 19:15:00 2013-05-01 00:11:00  4.9300 
LOH-5851        2013-05-01 14:30:00 2013-05-01 18:30:00  4.0000 
LOH-5851        2013-05-01 19:00:00 2013-05-02 00:22:00  5.3700 
LOH-5851        2013-05-02 14:30:00 2013-05-02 18:30:00  4.0000 
LOH-5851        2013-05-02 19:00:00 2013-05-03 00:31:00  5.5200 
LOH-5851        2013-05-03 14:45:00 2013-05-03 18:45:00  4.0000 
LOH-5851        2013-05-03 19:15:00 2013-05-04 00:15:00  5.0000 
LOH-5851        2013-05-04 14:00:00 2013-05-04 18:00:00  4.0000 
LOH-5851        2013-05-04 18:30:00 2013-05-04 23:30:00  5.0000 

该查询适用于少量数据,但在为付费期(通常为一周)的预期员工人口运行时,会出现丑陋的最大递归错误消息。

编辑编辑:查看Richard针对递归问题修复的注释。

1 个答案:

答案 0 :(得分:5)

示例数据

create table tbl (person int, startdate datetime, enddate datetime, hours decimal(10,2));
insert tbl values
(5163 ,'2013-04-29 07:00:00.000' ,'2013-04-29 11:00:00.000', 4.00),
(5163 ,'2013-04-29 11:30:00.000' ,'2013-04-29 15:30:00.000', 4.00),
(5163 ,'2013-04-29 15:30:00.000' ,'2013-04-29 19:06:00.000', 3.60),
(5851 ,'2013-05-02 19:00:00.000' ,'2013-05-02 23:00:00.000', 4.00),
(5851 ,'2013-05-02 23:00:00.000' ,'2013-05-03 00:00:00.000', 1.00),
(5851 ,'2013-05-03 00:00:00.000' ,'2013-05-03 00:31:00.000', 0.52);

查询

;with cte as (
    select person, startdate, enddate, hours
    from tbl
    union all
    select t.person, cte.startdate, t.enddate, cast(cte.hours + t.hours as decimal(10,2))
    from cte
    join tbl t on cte.person = t.person and cte.enddate = t.startdate
), cte2 as (
    select *, rn = row_number() over (partition by person, enddate order by startdate)
    from cte
)
select person, startdate, max(enddate) enddate, max(hours) hours
from cte2
where rn=1
group by person, startdate
order by person, startdate;

结果

person      startdate               enddate                 hours
----------- ----------------------- ----------------------- -------
5163        2013-04-29 07:00:00.000 2013-04-29 11:00:00.000 4.00
5163        2013-04-29 11:30:00.000 2013-04-29 19:06:00.000 7.60
5851        2013-05-02 19:00:00.000 2013-05-03 00:31:00.000 5.52