¿Cómo hacer múltiples LEFT JOINs con O usar completamente un índice compuesto? (parte 2)
Es para un sistema que calcula cómo los usuarios escanean sus huellas digitales cuando entran / salen del lugar de trabajo. No sé cómo se llama en inglés. Necesito determinar si el usuario llega tarde por la mañana y si el usuario deja el trabajo temprano.
Estatb_scan
La tabla contiene la fecha y la hora en que un usuario escanea una huella digital.
CREATE TABLE `tb_scan` (
`scpercode` varchar(6) DEFAULT NULL,
`scyear` varchar(4) DEFAULT NULL,
`scmonth` varchar(2) DEFAULT NULL,
`scday` varchar(2) DEFAULT NULL,
`scscantime` datetime,
KEY `all` (`scyear`,`scmonth`,`scday`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
Tiene más de 100,000 filas, algo como esto
scpercode scyear scmonth scday scdateandtime
000001 2010 10 10 2016-01-10 08:02:00
000001 2010 10 10 2016-01-02 17:33:00
000001 2010 10 11 2016-01-11 07:48:00
000001 2010 10 11 2016-01-11 17:29:00
000002 2010 10 10 2016-01-10 17:31:00
000002 2010 10 10 2016-01-02 17:28:00
000002 2010 10 11 2016-01-11 05:35:00
000002 2010 10 11 2016-01-11 05:29:00
Y estotb_workday
la tabla contiene cada fecha
CREATE TABLE `tb_workday` (
`wdpercode` varchar(6) DEFAULT NULL,
`wdshift` varchar(1) DEFAULT NULL,
`wddate` date DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
Tiene filas con una secuencia de fechas como esta:
wdpercode wdshift wddate
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
Hay otrotb_shift
tabla que contiene el tiempo de turno
CREATE TABLE `tb_shift` (
`shiftcode` varchar(1) DEFAULT NULL,
`shiftbegin2` varchar(4) DEFAULT NULL,
`shiftbegin` varchar(4) DEFAULT NULL,
`shiftmid` varchar(4) DEFAULT NULL,
`shiftend` varchar(4) DEFAULT NULL,
`shiftend2` varchar(4) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
shiftcode shiftbegin2 shiftbegin shiftmid shiftend shiftend2
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
Quiero determinar que cada día, si el empleado llega tarde al trabajo o sale temprano del trabajo, ya qué hora.
SELECT wdpercode,wddate,shiftbegin,shiftend,time(tlate.scscantime) wdlate,time(tearly.scscantime) wdearly
FROM tb_workday
LEFT JOIN tb_shift
ON wdshift=shiftcode
LEFT JOIN tb_scan tlate
ON wdpercode=tlate.scpercode
AND tlate.scyear=year(wddate)
AND tlate.scmonth=month(wddate)
AND (tlate.scday=day(wddate)
OR tlate.scday=day(wddate)+1)
AND tlate.scscantime>=ADDDATE(CONCAT(wddate,' ',shiftbegin),INTERVAL IF(shiftbegin2>shiftbegin,1,0) DAY)
AND tlate.scscantime<=ADDDATE(CONCAT(wddate,' ',shiftmid),INTERVAL IF(shiftbegin2>shiftmid,1,0) DAY)
LEFT JOIN tb_scan tearly
ON wdpercode=tearly.scpercode
AND tearly.scyear=year(wddate)
AND tearly.scmonth=month(wddate)
AND (tearly.scday=day(wddate)
OR tearly.scday=day(wddate)+1)
AND tearly.scscantime>ADDDATE(CONCAT(wddate,' ',shiftmid),INTERVAL IF(shiftbegin2>shiftmid,1,0) DAY)
AND tearly.scscantime<ADDDATE(CONCAT(wddate,' ',shiftend),INTERVAL IF(shiftbegin2>shiftend,1,0) DAY)
Aquí está el ejemplo de una salida:
wdpercode wddate shiftbegin shiftend wdlate wdearly
000001 2016-01-10 08:00:00 17:30:00 08:02:00 (null)
000001 2016-01-11 08:00:00 17:30:00 (null) 17:29:00
000002 2016-01-11 17:30:00 05:30:00 17:31:00 (null)
000002 2016-01-11 17:30:00 05:30:00 (null) 05:29:00
estaADDDATE(CONCAT(wddate,' ',shiftbegin),INTERVAL IF(shiftbegin2>shiftbegin,1,0) DAY)
es para empleados que trabajan en turno de noche, por lo que tiene que agregar 1 día en el turno
El problema es si creo un índice parascscantime
, MySQL se niega a usarlo para comparar (>=
,<=
,>
,<
) Por favor vea este hilo¿Por qué MySQL no utiliza un índice para una comparación mayor que?
Debido a esto, creé elscyear
, scmonth
yscday
campos y combinarlos en un índice junto conscpercode
. Y tengo que asegurarme de que también se calcule para los trabajadores que trabajan en turno de noche, así que tengo que agregarlo conOR scday=day(wddate)+1
condición.
Antes de agregar la condición OR, elEXPLAIN
El resultado fue de 52 filas. Pero cuando agregué elOR scday=day(wddate)+1
condición, elEXPLAIN
El resultado se convirtió en 364 filas, lo que significa que MySQL no usó parte scday del índice. ¿Hay alguna forma de usar todo el índice, por lo que elEXPLAIN
resultado se vuelve más eficiente como 52 filas? También intenté eliminar el+1
parte y el resultado es también 52.