2013-05-16 8 views
5

Próbuję uzyskać kombinację daty rozpoczęcia i zakończenia dla nieprzerwanych zakresów czasu. Rozpiętości mogą przekraczać wiele wierszy, gdzie data zakończenia pierwszego wiersza jest taka sama jak data zakończenia następnego wiersza. Celem jest pokazanie ciągłego zakresu dat z sumą godzin przepracowanych dla tego zakresu.SQL: znaleźć ciągłe zakresy dat w wielu wierszach?

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 

Z powyższych danych chcę następujące.

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 

Dla każdej osoby i nowego (nieciągłej) zakresu dat, porównaj ENDDATE wybranym wierszu, aby STARTDATE następnego rzędu jest. Jeśli są takie same, akumuluj godziny i kontynuuj przetwarzanie wierszy, dopóki data końcowa/data początkowa nie będą takie same.

Środowisko to SQL Server 2008 R2. Próbowałem kwerendy z udziałem self join, używając funkcji row_number i partition(), ale nie udało się uzyskać udanego rozwiązania. Dzięki!

edytuj: Oto przepływ danych dla rozwiązania RichardTheKiwi - uruchomiłem go dla jednej osoby, aby sprawdzić, ile rekursji generuje tygodniowy cios.

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 

Kwerenda działa idealnie dla małych ilości danych, ale kiedy uruchomić dla oczekiwanego populacji pracowników na okres wypłaty (zwykle jeden tydzień), pojawia się brzydki maks rekursji komunikat o błędzie.

edytuj edytuj: zobacz komentarze do poprawki Richarda dla problemu rekursji.

Odpowiedz

4

dane Przykładowe

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); 

zapytaniu

;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; 

Wyniki

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

Cześć Richard, dziękuję za to rozwiązanie! Chcę oznaczyć to jako udzielone .... Chociaż działa z przykładowymi danymi dostarczonymi, po uruchomieniu nad danymi produkcyjnymi, otrzymuję komunikat "Zakończono instrukcję Maksymalna rekurencja 100 została wyczerpana przed zakończeniem instrukcji.". Otrzymał nawet błąd po użyciu podpowiedzi "opcja (maxrecursion 32000)". Dane produkcyjne to około 600 wierszy i oczekuję do 1500 wierszy dla zapytania. Czy istnieje alternatywna metoda, która działa wokół problemu rekursji? – user2391335

+0

Myślę, że masz jakiś rekord, w którym data końcowa = data rozpoczęcia. Jest to jedyna rzecz, o której mogę myśleć (w tej chwili), która spowodowałaby nieskończoną rekursję. Możesz temu zapobiec używając 'z cte join tbl t na cte.person = t.person i cte.enddate = t.startdate i t.enddate! = T.startdate' – RichardTheKiwi

+0

Jeszcze raz dziękuję, Richard! Proponowana zmiana wygląda na naprawienie problemu rekursji, a wynik dla mojej przykładowej osoby wygląda poprawnie. – user2391335