sql >> データベース >  >> RDS >> Mysql

ORを使用して複数のLEFTJOINを作成し、複合インデックスを完全に使用するにはどうすればよいですか? (パート2)

    まず、他の人からこの質問を投稿することをお勧めします。複数のレコードを取得した理由は、シフトに基づいて、同じ日に複数回出勤および退勤する可能性があるためです。さて、これを解決する方法。

    MySQLでは、select FROM句の一部として「@」変数を使用して、インライン変数の宣言と割り当てを行うことができます。私が始めているのは、いくつかの@変数を使用した、就業日からシフトテーブルへの単純な結合です(そして私はこれを今理解していると思います)。

    シフトに参加している各人について、当日と翌日など、シフトの途中で発生する場所を事前に計算しています。また、begin2とend2は、クロックインとクロックアウトの可能性について外れ値であるように見えます。例:人1はシフト1で働いています。シフト1は、特定の日の仕事に対して

    として定義されています。
    shiftcode   shiftbegin2  shiftbegin  shiftmid  shiftend  shiftend2
            1     04:00:00     08:00:00  12:00:00  17:30:00  21:30:00 
    

    ですから、私はこれを6月28日のシフト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
    

    同様に、一晩をラップするシフト2の場合

    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
    

    したがって、これがすべて正しければ、私の内部クエリは各人のこれらすべての範囲を事前に決定しているので、以下を介したスキャンの数に関係なく、1日あたり1人あたり1つのレコードしかありません。

    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
    

    @midDayと@endDayのインライン計算はそうなので、スキャンされたタイムレコーダーテーブルに参加することを心配する必要はなく、他のすべてが考慮されている中で1日を追加し続ける必要はありません。したがって、このクエリの最後に、次のようになります...人1の通常シフトと人2の夜のシフトの間の通知、計算された終了日にはロールオーバー日も表示されます

    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
    

    このクエリから余分な列を削除することもできますが、すべてを含めたので、各行とスケジュールされた作業の日付を考慮するための値を確認/確認できます。私がまだ必要とする省略リストは

    です
    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
    

    したがって、上記が正確である場合は、このクエリから計算された最大範囲に基づいて、各人の時計を出し入れする必要があります。これにより、日付ごとに複数のレコードが作成される可能性があります

    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
    

    そこで、ここでは、最も早いクロックインと最大クロックアウト内の任意の日付のスキャン時間に参加し、ミッドシフトを使用して、遅い時間にクロックインしたか、早い時間にクロックアウトしたかを判断します。特定の人/シフトの到着と出発に追加のMIN()とMAX()を追加して、あなたが何をし、何を見るべきかを確認しました。

    MAX(IF())の目的は、レイト/アーリーステータスが発生した場合にのみキャプチャすることです。 group byはシフトごとであるため、最初のレコード(クロックイン)が遅れてその時間が必要になる場合がありますが、クロックアウトの2番目のレコードはシフト中の時間では適用できず、空白になります。同様に、シフトからの早期の逸脱を検出します。

    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;
    

    私はあなたが提供したものからテーブルとサンプルデータを作成しました(そのうちのいくつかのデータはサンプルの日付と範囲と一致しなかったので、そうするように調整しました)。

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

    サンプルデータは、1:遅れて到着、2:早く出発、3:遅く到着し、早く出発する各人を示しています。

    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
    

    クエリを最適化するために、スキャンテーブルのインデックスを変更します

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



    1. MySQLでrow_numberを取得する方法

    2. Round()がSQLiteでどのように機能するか

    3. MySQLでインデックスを作成または削除するために存在する場合、どのように使用できますか?

    4. Exachkユーティリティを使用したExadataのヘルスチェック