Po pierwsze, lepiej opublikuj to pytanie od swojego drugiego. Powodem, dla którego otrzymywałeś wiele rekordów, jest możliwość wielokrotnego wejścia i wyjścia osoby w ciągu tego samego dnia na podstawie ich zmian. Teraz, jak to rozwiązać.
W MySQL można wykonać deklarację i przypisania zmiennych wbudowanych za pomocą zmiennych „@” jako części klauzuli select FROM. To, od czego zaczynam, to proste połączenie z dnia pracy do stołu zmianowego (i myślę, że teraz to rozumiem) z kilkoma @zmiennymi.
Dla każdej osoby przyłączonej do zmiany wstępnie obliczam, gdzie występuje środek zmiany, np. ten sam dzień vs następny dzień. Ponadto, begin2 i end2 wydają się być wartościami odstającymi dla możliwego wejścia lub wyłączenia. Przykład:Osoba 1 pracuje na zmianę 1. Zmiana 1 jest zdefiniowana na dany dzień pracy jako
shiftcode shiftbegin2 shiftbegin shiftmid shiftend shiftend2
1 04:00:00 08:00:00 12:00:00 17:30:00 21:30:00
Interpretuję to tak, jakbym pracował 28 czerwca, zmiana 1,
June 28 @ 4am Earliest allowed clock-in time
June 28 @ 8am Actual beginning of shift
June 28 @ 12pm (afternoon) is the middle of the work day
June 28 @ 5:30pm is the end of the work day
June 28 @ 9:30pm is the max expected clock-out recognized for the shift
Podobnie w przypadku zmiany 2, która zawinie całą noc
shiftcode shiftbegin2 shiftbegin shiftmid shiftend shiftend2
2 12:00:00 17:30:00 21:00:00 05:30:00 09:30:00
June 28 @ 12pm (afternoon) Earliest allowed clock-in time
June 28 @ 5:30pm Actual beginning of shift
June 28 @ 9pm is the middle of the shift
June 29 @ 5:30am (day roll-over) is the end of the work day
June 29 @ 9:30am (day roll-over) is the max expected clock-out for the shift
Tak więc, jeśli wszystko się zgadza, moje wewnętrzne zapytanie wstępnie określa wszystkie te zakresy dla każdej osoby, więc będę mieć tylko 1 rekord na osobę na dzień roboczy, niezależnie od liczby skanów poniżej.
select
wd.wdpercode,
wd.wdshift,
wd.wddate,
s.shiftbegin,
s.shiftend,
s.shiftbegin2,
s.shiftmid,
s.shiftend2,
@midDay := if( s.shiftbegin < s.shiftmid, wd.wddate, date_add( wd.wddate, interval 1 day )) as NewMidDay,
@endDay := if( s.shiftbegin < s.shiftend, wd.wddate, date_add( wd.wddate, interval 1 day )) as NewEndDay,
cast( concat(wd.wddate, ' ', s.shiftbegin2 ) as DateTime ) as EarliestClockIn,
cast( concat(wd.wddate, ' ', s.shiftbegin ) as DateTime ) as BeginShift,
cast( concat(@midDay, ' ', s.shiftmid ) as DateTime ) as MidShift,
cast( concat( @endDay, ' ', s.shiftend ) as DateTime ) as EndShift,
cast( concat( @endDay, ' ', s.shiftend2 ) as DateTime ) as MaxClockOut
from
( select
@endDay := '',
@midDay := '' ) sqlvars,
tb_workday wd
join tb_shift s
on wd.wdshift = s.shiftcode
Obliczenia inline @midDay i @endDay są tak, że nie muszę się martwić dołączaniem do zeskanowanej tabeli zegara czasu i dodawaniem 1 dnia w środku wszystkich innych rozważanych kwestii. Tak więc na końcu tego zapytania otrzymałbym coś takiego... Uwaga między normalną zmianą osoby 1 a zmianą nocną osoby 2, obliczona data końcowa pokazuje również daty przeniesienia
wdpercode wdshift wddate shiftbegin shiftend shiftbegin2 shiftmid shiftend2 NewMidDay NewEndDay EarliestClockIn BeginShift MidShift EndShift MaxClockOut
000001 1 2010-10-10 08:00 17:30 04:00 12:00 21:30 2010-10-10 2010-10-10 2010-10-10 04:00 2010-10-10 08:00 2010-10-10 12:00 2010-10-10 17:30 2010-10-10 21:30:00
000001 1 2010-10-11 08:00 17:30 04:00 12:00 21:30 2010-10-11 2010-10-11 2010-10-11 04:00 2010-10-11 08:00 2010-10-11 12:00 2010-10-11 17:30 2010-10-11 21:30:00
000001 1 2010-10-12 08:00 17:30 04:00 12:00 21:30 2010-10-12 2010-10-12 2010-10-12 04:00 2010-10-12 08:00 2010-10-12 12:00 2010-10-12 17:30 2010-10-12 21:30:00
000001 1 2010-10-13 08:00 17:30 04:00 12:00 21:30 2010-10-13 2010-10-13 2010-10-13 04:00 2010-10-13 08:00 2010-10-13 12:00 2010-10-13 17:30 2010-10-13 21:30:00
000002 2 2010-10-10 17:30 05:30 12:00 21:00 09:30 2010-10-10 2010-10-11 2010-10-10 12:00 2010-10-10 17:30 2010-10-10 21:00 2010-10-11 05:30 2010-10-11 09:30:00
000002 2 2010-10-11 17:30 05:30 12:00 21:00 09:30 2010-10-11 2010-10-12 2010-10-11 12:00 2010-10-11 17:30 2010-10-11 21:00 2010-10-12 05:30 2010-10-12 09:30:00
000002 2 2010-10-12 17:30 05:30 12:00 21:00 09:30 2010-10-12 2010-10-13 2010-10-12 12:00 2010-10-12 17:30 2010-10-12 21:00 2010-10-13 05:30 2010-10-13 09:30:00
000002 2 2010-10-13 17:30 05:30 12:00 21:00 09:30 2010-10-13 2010-10-14 2010-10-13 12:00 2010-10-13 17:30 2010-10-13 21:00 2010-10-14 05:30 2010-10-14 09:30:00
Możesz usunąć dodatkowe kolumny z tego zapytania, ale uwzględniłem wszystkie, abyś mógł zobaczyć / potwierdzić, jakie wartości są do rozważenia dla każdego wiersza i daty zaplanowanej pracy. Skrócona lista, której nadal potrzebuję, to
select
wd.wdpercode,
@midDay := if( s.shiftbegin < s.shiftmid, wd.wddate, date_add( wd.wddate, interval 1 day )) as NewMidDay,
@endDay := if( s.shiftbegin < s.shiftend, wd.wddate, date_add( wd.wddate, interval 1 day )) as NewEndDay,
cast( concat(wd.wddate, ' ', s.shiftbegin2 ) as DateTime ) as EarliestClockIn,
cast( concat(wd.wddate, ' ', s.shiftbegin ) as DateTime ) as BeginShift,
cast( concat(@midDay, ' ', s.shiftmid ) as DateTime ) as MidShift,
cast( concat( @endDay, ' ', s.shiftend ) as DateTime ) as EndShift,
cast( concat( @endDay, ' ', s.shiftend2 ) as DateTime ) as MaxClockOut
Tak więc, jeśli powyższe jest dokładne, musimy teraz wprowadzić i wyłączyć zegar dla każdej osoby na podstawie MAKSYMALNEGO zakresu obliczonego na podstawie tego zapytania, które MOŻE więcej niż jeden rekord na datę
wdpercode EarliestClockIn MidShift MaxClockOut
000001 2010-10-10 04:00 2010-10-10 12:00 2010-10-10 21:30:00
000002 2010-10-10 12:00 2010-10-10 21:00 2010-10-11 09:30:00
Więc tutaj robię dołączenie do czasów skanowania dla dowolnych dat w najwcześniejszym zegarze i maksymalnym zegarze oraz używam midshift jako podstawy do określenia, czy taktowano późno, czy wyjeżdżając wcześnie. Dodałem dodatkowe MIN() i MAX() dla przyjazdu i wyjazdu dla danej osoby / zmiany tylko po to, aby potwierdzić, co ROBISZ I powinieneś widzieć.
Celem funkcji MAX( IF() ) jest uchwycenie późnego/wczesnego stanu TYLKO JEŚLI tak się stało. Ponieważ grupowanie według odbywa się na zmianę, pierwszy rekord (wchodzący zegar) może być spóźniony i chcesz o ten czas, ale drugi rekord do wyrejestrowania nie ma zastosowania do czasu w połowie zmiany i dlatego byłby pusty. Podobnie do wykrywania wcześniejszego wyjścia ze zmiany.
select
perPerson.wdPerCode,
perPerson.BeginShift,
perPerson.EndShift,
min( TS.scScanTime ) as Arrival,
max( TS.scScanTime ) as Departure,
max( IF( TS.scScanTime > perPerson.BeginShift
AND TS.scScanTime <= perPerson.MidShift, TS.scScanTime, "" )) as LateArrival,
max( IF( TS.scScanTime > perPerson.MidShift
AND TS.scScanTime < perPerson.EndShift, TS.scScanTime, "" )) as EarlyDepart
from
( select
wd.wdpercode,
@midDay := if( s.shiftbegin < s.shiftmid, wd.wddate,
date_add( wd.wddate, interval 1 day )) as NewMidDay,
@endDay := if( s.shiftbegin < s.shiftend, wd.wddate,
date_add( wd.wddate, interval 1 day )) as NewEndDay,
cast( concat(wd.wddate, ' ', s.shiftbegin2 ) as DateTime ) as EarliestClockIn,
cast( concat(wd.wddate, ' ', s.shiftbegin ) as DateTime ) as BeginShift,
cast( concat(@midDay, ' ', s.shiftmid ) as DateTime ) as MidShift,
cast( concat( @endDay, ' ', s.shiftend ) as DateTime ) as EndShift,
cast( concat( @endDay, ' ', s.shiftend2 ) as DateTime ) as MaxClockOut
from
( select
@endDay := '',
@midDay := '' ) sqlvars,
tb_workday wd
join tb_shift s
on wd.wdshift = s.shiftcode ) perPerson
JOIN tb_scan TS
on perPerson.wdpercode = TS.scpercode
AND TS.scScanTime >= perPerson.EarliestClockIn
AND TS.scScanTime <= perPerson.MaxClockOut
group by
perPerson.wdPerCode,
perPerson.BeginShift;
Stworzyłem tabele i przykładowe dane z tego, co dostarczyłeś (z których niektóre z Twoich danych nie pasują do przykładowych dat i zakresów, więc dostosowałem się do tego).
CREATE TABLE `tb_scan` (
`scpercode` varchar(6) DEFAULT NULL,
`scscantime` datetime,
KEY `all` (`scyear`,`scmonth`,`scday`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
insert into tb_scan
( scpercode, scscantime )
values
( '000001', '2010-10-10 08:02:00' ),
( '000001', '2010-10-10 17:33:00' ),
( '000001', '2010-10-11 07:48:00' ),
( '000001', '2010-10-11 17:29:00' ),
( '000001', '2010-10-12 08:04:00' ),
( '000001', '2010-10-12 17:28:00' ),
( '000002', '2010-10-10 17:31:00' ),
( '000002', '2010-10-11 05:35:00' ),
( '000002', '2010-10-11 17:28:00' ),
( '000002', '2010-10-12 05:29:00' ),
( '000002', '2010-10-12 17:32:00' ),
( '000002', '2010-10-13 05:27:00' );
CREATE TABLE `tb_workday` (
`wdpercode` varchar(6) DEFAULT NULL,
`wdshift` varchar(1) DEFAULT NULL,
`wddate` date DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
insert into tb_workday
( wdpercode, wdshift, wddate )
values
( '000001', '1', '2010-10-10' ),
( '000001', '1', '2010-10-11' ),
( '000001', '1', '2010-10-12' ),
( '000001', '1', '2010-10-13' ),
( '000002', '2', '2010-10-10' ),
( '000002', '2', '2010-10-11' ),
( '000002', '2', '2010-10-12' ),
( '000002', '2', '2010-10-13' );
CREATE TABLE `tb_shift` (
`shiftcode` varchar(1) DEFAULT NULL,
`shiftbegin2` varchar(8) DEFAULT NULL,
`shiftbegin` varchar(8) DEFAULT NULL,
`shiftmid` varchar(8) DEFAULT NULL,
`shiftend` varchar(8) DEFAULT NULL,
`shiftend2` varchar(8) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
insert into tb_shift
( shiftcode, shiftbegin2, shiftbegin, shiftmid, shiftend, shiftend2 )
values
( '1', '04:00:00', '08:00:00', '12:00:00', '17:30:00', '21:30:00' ),
( '2', '12:00:00', '17:30:00', '21:00:00', '05:30:00', '09:30:00' );
Przykładowe dane pokazują, że każda osoba ma 1:przybycie późno, 2:odjazd wcześnie, 3:przybycie późno ORAZ odjazd wcześnie.
wdPerCode BeginShift EndShift Arrival Departure LateArrival EarlyDepart
000001 2010-10-10 08:00 2010-10-10 17:30 2010-10-10 08:02 2010-10-10 17:33 2010-10-10 08:02
000001 2010-10-11 08:00 2010-10-11 17:30 2010-10-11 07:48 2010-10-11 17:29 2010-10-11 17:29
000001 2010-10-12 08:00 2010-10-12 17:30 2010-10-12 08:04 2010-10-12 17:28 2010-10-12 08:04 2010-10-12 17:28
000002 2010-10-10 17:30 2010-10-11 05:30 2010-10-10 17:31 2010-10-11 05:35 2010-10-10 17:31
000002 2010-10-11 17:30 2010-10-12 05:30 2010-10-11 17:28 2010-10-12 05:29 2010-10-12 05:29
000002 2010-10-12 17:30 2010-10-13 05:30 2010-10-12 17:32 2010-10-13 05:27 2010-10-12 17:32 2010-10-13 05:27
Aby zoptymalizować zapytanie, zmieniłbym indeks w tabeli skanowania
CREATE TABLE `tb_scan` (
`scpercode` varchar(6) DEFAULT NULL,
`scscantime` datetime,
KEY `personDate` (`scpercode`, `scscantime` )