sql >> Database >  >> RDS >> Mysql

Hoe maak je meerdere LEFT JOINs met OR volledig gebruik van een samengestelde index? (deel 2)

Ten eerste, het is beter om deze vraag van je andere te plaatsen. De reden dat u meerdere records kreeg, is de mogelijkheid dat een persoon meerdere keren op dezelfde dag in- en uitklokt op basis van hun diensten. Hoe dit nu op te lossen.

In MySQL kunt u inline variabele declaratie en toewijzingen doen met behulp van "@" variabelen als onderdeel van de select FROM-clausule. Waar ik mee begin is een simpele join van de werkdag naar de ploegentabel (en ik denk dat ik dit nu begrijp), met wat @variabelen.

Voor elke persoon die deel uitmaakt van de ploeg, bereken ik vooraf waar het midden van de ploeg plaatsvindt, zoals dezelfde dag versus de volgende dag. Ook lijken begin2 en end2 uitschieters te zijn voor een mogelijke klok-in vs klok-uit. Voorbeeld:Persoon 1 werkt in ploegendienst 1. Ploeg 1 wordt voor een bepaalde werkdag gedefinieerd als

shiftcode   shiftbegin2  shiftbegin  shiftmid  shiftend  shiftend2
        1     04:00:00     08:00:00  12:00:00  17:30:00  21:30:00 

Dus ik interpreteer dit alsof ik werk op 28 juni, ploeg 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

Evenzo voor dienst 2, die een nacht zal duren

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

Dus, als dit allemaal correct is, is mijn innerlijke vraag het vooraf bepalen van al deze bereiken voor elke persoon, dus ik zal altijd maar 1 record per persoon per werkdag hebben, ongeacht het aantal scans via hieronder.

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

Dankzij de inline computing van @midDay en @endDay hoef ik me geen zorgen te maken over deelname aan de gescande tijdkloktabel en blijf ik 1 dag toevoegen te midden van al het andere dat wordt overwogen. Dus aan het einde van deze vraag zou ik eindigen met iets als... Merk op tussen persoon 1 normale dienst en persoon 2 nachtdienst, de berekende einddatum toont ook de doorroldatums

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

U kunt de extra kolommen uit deze query verwijderen, maar ik heb ze allemaal opgenomen zodat u kunt zien/bevestigen wat de waarden zijn voor elke rij en geplande werkdatum. De verkorte lijst die ik nog nodig zou hebben is

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

Dus als het bovenstaande klopt, moeten we nu de klok in- en uitschakelen voor elke persoon op basis van het MAXIMALE bereik berekend op basis van deze zoekopdracht, wat meer dan één record per datum KAN

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

Dus hier doe ik mee aan de scantijden voor alle datums binnen de vroegste klok in en maximale klok uit en gebruik midshift als basis om te bepalen of ze laat inklokken of vroeg vertrekken. Ik heb de extra MIN() en MAX() toegevoegd voor de aankomst en vertrek voor een bepaalde persoon / dienst om te bevestigen wat je DOEN EN zou moeten zien.

Het doel van de MAX( IF() ) is om de late / vroege status ALLEEN vast te leggen ALS ze zijn gebeurd. Aangezien de group by per ploeg is, kan het eerste record (inklokken) te laat zijn en wilt u die tijd hebben, maar het tweede record voor uitklokken is niet van toepassing via de middenploeg en zou dus blanco zijn. Evenzo voor het detecteren van vroegtijdig vertrek uit een dienst.

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;

Ik heb tabellen en voorbeeldgegevens gemaakt op basis van wat u heeft verstrekt (waarvan sommige van uw gegevens niet overeenkwamen met de voorbeelddatums en -bereiken, dus heb ik dit aangepast).

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

De voorbeeldgegevens tonen elke persoon met een 1:laat aankomen, 2:vroeg vertrekken, 3:laat aankomen EN vroeg vertrekken.

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

Voor het optimaliseren van de zoekopdracht zou ik uw index op de scantabel wijzigen

CREATE TABLE `tb_scan` (
  `scpercode` varchar(6) DEFAULT NULL,
  `scscantime` datetime,
  KEY `personDate` (`scpercode`, `scscantime` )



  1. Eclipse - voeg .jar toe aan Dynamic Web Project

  2. Wanneer moet u SQL Server met Access gebruiken? (Hint:bijna altijd)

  3. Verschil tussen schema / database in MySQL

  4. Hoe voeg ik meerdere rijen in een enkele invoeginstructie in?