Tengo una tabla que almacena citas disponibles para maestros, permitiendo dos tipos de inserciones:
Basado en la hora : con total libertad para agregar espacios ilimitados por día por maestro (siempre y cuando los espacios no se superpongan): el 15 / Abr un maestro puede tener espacios a las 10:00, 11:00, 12:00 y 16:00 . Una persona es atendida después de elegir un horario / horario de maestro específico.
Periodo / rango de tiempo : el 15 / Abr otro maestro puede trabajar de 10:00 a 12:00 y luego de 14:00 a 18:00. Una persona es atendida por orden de llegada, por lo que si un maestro trabaja de 10:00 a 12:00, todas las personas que lleguen en este período serán atendidas por orden de llegada (cola local).
Como tengo que devolver a todos los maestros disponibles en una búsqueda, necesito que todos los espacios se guarden en la misma tabla que el rango de orden de llegada. De esta manera puedo ordenar por fecha_desde ASC, mostrando primero los primeros espacios disponibles en los resultados de búsqueda.
Estructura de la tabla actual
CREATE TABLE `teacher_slots` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`teacher_id` mediumint(8) unsigned NOT NULL,
`city_id` smallint(5) unsigned NOT NULL,
`subject_id` smallint(5) unsigned NOT NULL,
`date_from` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`date_to` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`status` tinyint(4) NOT NULL DEFAULT '0',
`order_of_arrival` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `by_hour_idx` (`teacher_id`,`order_of_arrival`,`status`,`city_id`,`subject_id`,`date_from`),
KEY `order_arrival_idx` (`order_of_arrival`,`status`,`city_id`,`subject_id`,`date_from`,`date_to`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Consulta de busqueda
Necesito filtrar por: fecha y hora real, city_id, subject_id y si hay un espacio disponible (estado = 0).
Para cada hora , tengo que mostrar todos los espacios disponibles para el primer día disponible más cercano para cada maestro (mostrar todos los espacios de tiempo de un día determinado y no puedo mostrar más de un día para el mismo maestro). (Recibí la consulta con la ayuda de mattedgod ).
Para el rango basado (order_of_arrival = 1), tengo que mostrar el rango disponible más cercano, solo una vez por maestro.
La primera consulta se ejecuta individualmente en alrededor de 0,10 ms, la segunda consulta 0,08 ms y la UNION ALL un promedio de 300 ms.
(
SELECT id, teacher_slots.teacher_id, date_from, date_to, order_of_arrival
FROM teacher_slots
JOIN (
SELECT DATE(MIN(date_from)) as closestDay, teacher_id
FROM teacher_slots
WHERE date_from >= '2014-04-10 08:00:00' AND order_of_arrival = 0
AND status = 0 AND city_id = 6015 AND subject_id = 1
GROUP BY teacher_id
) a ON a.teacher_id = teacher_slots.teacher_id
AND DATE(teacher_slots.date_from) = closestDay
WHERE teacher_slots.date_from >= '2014-04-10 08:00:00'
AND teacher_slots.order_of_arrival = 0
AND teacher_slots.status = 0
AND teacher_slots.city_id = 6015
AND teacher_slots.subject_id = 1
)
UNION ALL
(
SELECT id, teacher_id, date_from, date_to, order_of_arrival
FROM teacher_slots
WHERE order_of_arrival = 1 AND status = 0 AND city_id = 6015 AND subject_id = 1
AND (
(date_from <= '2014-04-10 08:00:00' AND date_to >= '2014-04-10 08:00:00')
OR (date_from >= '2014-04-10 08:00:00')
)
GROUP BY teacher_id
)
ORDER BY date_from ASC;
Pregunta
¿Hay alguna manera de optimizar UNION, para que pueda obtener una respuesta razonable de un máximo de ~ 20 ms o incluso un rango de retorno basado en + por hora en una sola consulta (con un IF, etc.)?
SQL Fiddle: http://www.sqlfiddle.com/#!2/59420/1/0
EDITAR:
Intenté alguna desnormalización creando un campo "only_date_from" donde almacené solo la fecha, para poder cambiar esto ...
DATE(MIN(date_from)) as closestDay / DATE(teacher_slots.date_from) = closestDay
... a esto
MIN(only_date_from) as closestDay / teacher_slots.only_date_from = closestDay
¡Ya me salvó 100ms! Todavía 200ms en promedio.