J'ai une table qui stocke les rendez-vous disponibles pour les enseignants, permettant deux types d'insertions:
Horaire : avec une liberté totale d'ajouter des créneaux horaires illimités par jour et par enseignant (tant que les créneaux ne se chevauchent pas): le 15 / avril, un enseignant peut avoir des créneaux horaires à 10h00, 11h00, 12h00 et 16h00 . Une personne est servie après avoir choisi un horaire / créneau spécifique pour l'enseignant.
Période / plage horaire : le 15 / avril, un autre enseignant peut travailler de 10h00 à 12h00 puis de 14h00 à 18h00. Une personne est servie par ordre d'arrivée, donc si un enseignant travaille de 10h00 à 12h00, toutes les personnes qui arrivent pendant cette période seront suivies par ordre d'arrivée (file d'attente locale).
Étant donné que je dois renvoyer tous les enseignants disponibles dans une recherche, j'ai besoin que tous les créneaux soient enregistrés dans le même tableau que l'ordre des plages d'arrivée. De cette façon, je peux commander par date_from ASC, en affichant les premiers emplacements disponibles en premier dans les résultats de recherche.
Structure actuelle de la table
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;
Requête de recherche
J'ai besoin de filtrer par: datetime réel, city_id, subject_id et si un slot est disponible (status = 0).
Pour les horaires, je dois montrer tous les créneaux horaires disponibles pour le premier jour disponible le plus proche pour chaque enseignant (afficher tous les créneaux horaires d'un jour donné et ne peut pas afficher plus d'un jour pour le même enseignant). (J'ai reçu la requête avec l'aide de mattedgod ).
Pour la plage basée sur l'ordre (order_of_arrival = 1), je dois montrer la plage disponible la plus proche, une seule fois par enseignant.
La première requête s'exécute individuellement en environ 0,10 ms, la seconde requête 0,08 ms et l'UNION ALL en moyenne 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;
Question
Existe-t-il un moyen d'optimiser l'UNION, afin que je puisse obtenir une réponse raisonnable d'un maximum de ~ 20 ms ou même une plage de retour basée sur + toutes les heures en une seule requête (avec un IF, etc.)?
SQL Fiddle: http://www.sqlfiddle.com/#!2/59420/1/0
ÉDITER:
J'ai essayé une dénormalisation en créant un champ "only_date_from" où je stockais seulement la date, donc je pouvais changer cela ...
DATE(MIN(date_from)) as closestDay / DATE(teacher_slots.date_from) = closestDay
... pour ça
MIN(only_date_from) as closestDay / teacher_slots.only_date_from = closestDay
Cela m'a déjà fait gagner 100 ms! Toujours 200ms en moyenne.