2013-05-16 15 views
5

Sto cercando di ottenere una combinazione di date di inizio e fine per intervalli di tempo continui. Gli span possono attraversare più righe, dove la data di fine della prima riga è uguale alla data di fine della riga successiva. Il risultato voluto è mostrare un intervallo di date continuo con la somma delle ore lavorate per quell'intervallo.SQL: trova intervalli di date continue su più righe?

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 

Dai dati di cui sopra, voglio quanto segue.

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 

Per ogni persona e nuovo (non continuo) data campata, confronta enddate della riga corrente startdate della prossima riga. Se sono uguali, accumula le ore e continua l'elaborazione delle righe fino a quando l'enddate/startdate non è uguale.

L'ambiente è SQL Server 2008 R2. Ho provato le query che coinvolgono auto join, utilizzando le funzioni row_number e partition(), ma non sono riuscito a ottenere una soluzione di successo. Grazie!

modifica: Ecco il flusso di dati per la soluzione di RichardTheKiwi: l'ho eseguito per una persona per vedere quanta ricorsione viene generata per un valore di una settimana di punch.

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 

La query funziona perfettamente per piccole quantità di dati, ma quando viene eseguito per la popolazione dipendente previsto per un periodo di paga (di solito una settimana), viene visualizzato il messaggio di errore il brutto max ricorsioni.

modifica modifica: vedere i commenti per la correzione di Richard per il problema di ricorsione.

risposta

4

dati campione

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

la query

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

Risultati

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

Ciao Richard, grazie per questa soluzione! Voglio contrassegnare questo come risposta .... Mentre funziona con i dati di esempio forniti, quando viene eseguito sui dati di produzione, ottengo l'errore "L'istruzione terminata. La ricorsione massima 100 è stata esaurita prima del completamento dell'istruzione.". Anche ricevuto l'errore dopo aver usato l'opzione "suggerimento (maxrecursion 32000)". I dati di produzione sono circa 600 righe e mi aspetto fino a 1.500 righe per la query. Esiste un metodo alternativo che risolva il problema della ricorsione? – user2391335

+0

Penso che tu abbia qualche record in cui la data di fine = data di inizio. Questa è l'unica cosa che riesco a pensare (al momento) che causerebbe una ricorsione infinita. Puoi prevenirlo usando 'da cte join tbl t on cte.person = t.person e cte.enddate = t.startdate e t.enddate! = T.startdate' – RichardTheKiwi

+0

Grazie ancora, Richard! La modifica proposta sembra risolvere il problema della ricorsione e l'output per la mia persona campione sembra corretto. – user2391335

Problemi correlati