2013-05-16 3 views
5

Я пытаюсь получить комбинацию начала и конца даты для непрерывных промежутков времени. Пролеты могут пересекать несколько строк, где конечная дата первой строки совпадает с датой окончания следующей строки. Предполагаемый результат - показать непрерывный диапазон дат с суммой отработанных часов для этого диапазона.SQL: найти непрерывные диапазоны дат для нескольких строк?

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. Я попробовал запросы, связанные с self-join, используя функции 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 

Запрос отлично работает для небольших объемов данных, но при запуске для ожидаемого населения работника за период заработной платы (обычно одна недели), появляется уродливого Max рекурсии сообщений об ошибке.

Редактировать редактировать: см. Комментарии к исправлению Ричарда для проблемы рекурсии.

ответ

4

Выборочные данные

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 
+0

Привет Ричард, спасибо за это решение! Я хочу отметить это как ответ ... Хотя он работает с данными примера, при запуске над производственными данными я получаю сообщение об ошибке «Оператор завершен. Максимальная рекурсия 100 исчерпана до завершения заявки». Даже получил ошибку после использования подсказки «option (maxrecursion 32000)». Производственные данные составляют около 600 строк, и я ожидаю до 1500 строк для запроса. Есть ли альтернативный метод, который работает вокруг проблемы рекурсии? – user2391335

+0

Я думаю, что у вас есть запись, где дата окончания = дата начала. Это единственное, о чем я могу думать (на данный момент), что приведет к бесконечной рекурсии. Вы можете предотвратить это, используя 'from cte join tbl t на cte.person = t.person и cte.enddate = t.startdate и t.enddate! = T.startdate' – RichardTheKiwi

+0

Еще раз спасибо, Ричард! В предлагаемом изменении исправляется проблема рекурсии, и результат для моего образца выглядит правильно. – user2391335

Смежные вопросы