2012-01-19 12 views
6

Sto cercando di eseguire alcuni rapporti e di dover affrontare l'intera questione delle ore di lavoro dei dipendenti che attraversano la mezzanotte. Mi viene in mente di poter dividere i record che attraversano la mezzanotte in due record, come se l'impiegato fosse scaduto a mezzanotte e contemporaneamente tornasse indietro a mezzanotte, evitando così il problema di mezzanotte.Record di ore intermedie a mezzanotte

Quindi, se ho:

EmployeeId InTime      OutTime 
---   ----------------------- ----------------------- 
1   2012-01-18 19:50:04.437 2012-01-19 03:30:02.433 

Cosa pensi che sarebbe il modo più elegante per dividere questo record in questo modo:

EmployeeId InTime      OutTime 
---   ----------------------- ----------------------- 
1   2012-01-18 19:50:04.437 2012-01-19 00:00:00.000 
1   2012-01-19 00:00:00.000 2012-01-19 03:30:02.433 

E sì, ho accuratamente pensato attraverso effetti quali questo potrebbe avere sulle funzionalità esistenti ... ed è per questo che sto optando per farlo in una tabella temporanea che non influirà sulle funzionalità esistenti.

+1

Controlla http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=42516&bcsi_scan_0070CC03028EB70D=JJykjVHNGapjNgIKjEMdLQkAAADDDLIH&bcsi_scan_filename=topic.asp non è esattamente simile. Più sulla sua soluzione basata su SQL 2k. Ma spero che possa darti alcuni suggerimenti. – vmvadivel

+0

Quindi la sua traversata di mezzanotte ... qual è il problema ... ore totali lavorate in un dato turno? o se incrocia entrambe le date, in quale data dovrebbe apparire? – DRapp

+0

@DRapp Ai fini di questo "rapporto" (anche se non tutti), la manodopera dovrebbe comparire nella data in cui effettivamente appartiene ... non necessariamente la data in cui il dipendente ha iniziato il proprio turno. Ecco perché penso che questa sia la soluzione migliore per farlo in un modo che i dati hanno più senso lavorare. –

risposta

6

Questo potrebbe aiutare:

DECLARE @tbl TABLE 
    (
     EmployeeId INT, 
     InTime DATETIME, 
     OutTime DATETIME 
    ) 

INSERT INTO @tbl(EmployeeId,InTime,OutTime) VALUES (1,'2012-01-18 19:50:04.437','2012-01-19 03:30:02.433') 
INSERT INTO @tbl(EmployeeId,InTime,OutTime) VALUES (2,'2012-01-18 19:50:04.437','2012-01-18 20:30:02.433') 
INSERT INTO @tbl(EmployeeID,InTime,OutTime) VALUES (3,'2012-01-18 16:15:00.000','2012-01-19 00:00:00.000') 
INSERT INTO @tbl(EmployeeID,InTime,OutTime) VALUES (4,'2012-01-18 00:00:00.000','2012-01-18 08:15:00.000') 
SELECT 
    tbl.EmployeeId, 
    tbl.InTime, 
    DATEADD(dd, DATEDIFF(dd, 0, tbl.OutTime), 0) AS OutTime 
FROM 
    @tbl AS tbl 
WHERE 
    DATEDIFF(dd,tbl.InTime,tbl.OutTime)=1 
UNION ALL 
SELECT 
    tbl.EmployeeId, 
    CASE WHEN DATEDIFF(dd,tbl.InTime,tbl.OutTime)=1 
     THEN DATEADD(dd, DATEDIFF(dd, 0, tbl.OutTime), 0) 
     ELSE tbl.InTime 
    END AS InTime, 
    tbl.OutTime 
FROM @tbl AS tbl 
ORDER BY EmployeeId 
0

Provare questo, perché è possibile eseguire un inserimento di una selezione e all'interno della selezione è possibile impostare i valori da utilizzare per diversi giorni.

per aggiungere la nuova riga:

insert into table ("EMPLOYEE_ID","INTIME","OUTTIME") values 
SELECT EMPLOYEE_ID,date(INTIME),OUTTIME 
FROM table 
where date(intime) < date(outtime) 

Aggiornamento riga originale:

update table 
set outtime =date(outtime) 
where date(intime)= date(outtime) 
1

Se per il rapporto, allora si dovrebbe essere solo in grado di fare una query/unione che dà due record durante quelle condizioni da quella originale che inizia ... Senza avere SQL-Se rver 2008, posso solo offrire una query di pseudo-codice per te.

La prima parte ottiene tutti i record in base a qualsiasi condizione di intervallo da mostrare. Il valore di "OutTime" è condizionale ... se è lo stesso giorno, quindi non crossover, usa solo il tempo di uscita. Se è il giorno successivo, utilizzare la trasmissione per creare dinamicamente una data "AAAA-MM-GG" (che verrà impostata automaticamente sull'ora 00:00:00) come si desidera come ora di uscita.

L'UNIONE catturerà SOLO quegli stessi record qualificati nel PRIMO dove le date di ingresso/uscita sono DIVERSE. In quanto tale, SAPPIAMO che vogliamo qualunque cosa l'OutTime dovesse agire come InTime, ma in base al tempo "00:00:00", quindi viene eseguito lo stesso casting di un campo data/ora, e per questi record, solo usa il valore finale "OutTime" così com'è.

La colonna aggiuntiva per "TimeSplit" di "1" o "2" è per assicurarsi che possiamo ancora raggruppare per ID dipendente, ma da quello, assicurarsi che le voci "1" (spostamento iniziale) siano le prime, seguito da qualsiasi per la rispettiva stessa persona hanno una "2" entrata per il giorno si sovrappongono nel loro turno.

select 
     tc.EmployeeID, 
     '1' as TimeSplit, 
     tc.InTime, 
     case when datepart(dd, tc.InTime) = datepart(dd, tc.OutTime) 
     then tc.OutTime 
     else CAST(CAST(datepart(yyyy, tc.OutTime) AS varchar) 
       +'-'+ CAST(datepart(mm, tc.OutTime) AS varchar) 
       +'-'+ CAST(datepart(dd, tc.OutTime) AS varchar) AS DATETIME) 
     end as OutTime 
    from 
     TimeCard tc 
    where 
     YourDateRangeConditions... 
    ORDER BY 
     tc.EmployeeID, 
     TimeSplit 
UNION ALL 
select 
     tc.EmployeeID, 
     '2' as TimeSplit, 
     CAST( CAST(datepart(yyyy, tc.OutTime) AS varchar) 
     +'-'+ CAST(datepart(mm, tc.OutTime) AS varchar) 
     +'-'+ CAST(datepart(dd, tc.OutTime) AS varchar) AS DATETIME) 
     end as InTime 
     tc.OutTime 
    from 
     TimeCard tc 
    where 
     YourDateRangeConditions... 
     AND NOT datepart(dd, tc.InTime) = datepart(dd, tc.OutTime) 
2

La seguente soluzione utilizza una tabella di numeri (nella forma di un sottoinsieme della tabella di sistema master..spt_values) per dividere le fasce orarie. Può dividere intervalli che abbracciano un numero arbitrario di giorni (fino a 2048 con spt_values, ma con la tabella dei numeri personale è possibile impostare un massimo diverso). I casi specifici di gamme che coprono 1 e 2 giorni non vengono affrontati qui, ma credo che il metodo è abbastanza leggero per provare:

; 
WITH LaborHours (EmployeeId, InTime, OutTime) AS (
    SELECT 
    1, 
    CAST('2012-01-18 19:50:04.437' AS datetime), 
    CAST('2012-01-18 03:30:02.433' AS datetime) 
), 
HoursSplit AS (
    SELECT 
    h.*, 
    SubInTime = DATEADD(DAY, DATEDIFF(DAY, 0, h.InTime) + v.number + 0, 0), 
    SubOutTime = DATEADD(DAY, DATEDIFF(DAY, 0, h.InTime) + v.number + 1, 0) 
    FROM LaborHours h 
    INNER JOIN master..spt_values v 
     ON number BETWEEN 0 AND DATEDIFF(DAY, h.InTime, h.OutTime) 
    WHERE v.type = 'P' 
), 
HoursSubstituted AS (
    SELECT 
    EmployeeId, 
    InTime = CASE WHEN InTime > SubInTime THEN InTime ELSE SubInTime END, 
    OutTime = CASE WHEN OutTime < SubOutTime THEN OutTime ELSE SubOutTime END 
    FROM HoursSplit 
) 
SELECT * 
FROM HoursSubstituted 

In sostanza, si tratta di un metodo in due fasi.

Per prima cosa utilizziamo la tabella dei numeri per duplicare ogni riga tante volte il numero di giorni dell'intervallo e preparare i sottointervalli "standard" a partire dalla mezzanotte e terminare alla mezzanotte successiva.

Successivamente, confrontiamo l'inizio di un sottointervallo con l'inizio dell'intervallo per vedere se è il primo sotto-intervallo, nel qual caso utilizziamo InTime come inizio. Allo stesso modo, confrontiamo le desinenze per vedere se dovremmo usare OutTime o solo la mezzanotte come la fine di tale subrange.

Problemi correlati