Ho una tabella che memorizza gli appuntamenti disponibili per gli insegnanti, consentendo due tipi di inserimenti:
Base oraria : con totale libertà di aggiungere slot illimitati al giorno per insegnante (purché gli slot non si sovrappongano): il 15 aprile, un insegnante può avere slot alle 10:00, 11:00, 12:00 e 16:00 . Una persona viene servita dopo aver scelto un orario / orario specifico dell'insegnante.
Periodo / intervallo : il 15 aprile apre un altro insegnante che può lavorare dalle 10:00 alle 12:00 e poi dalle 14:00 alle 18:00. Una persona è servita per ordine di arrivo, quindi se un insegnante lavora dalle 10:00 alle 12:00, tutte le persone che arrivano in questo periodo saranno seguite dall'ordine di arrivo (coda locale).
Dato che devo restituire tutti gli insegnanti disponibili in una ricerca, ho bisogno di salvare tutti gli slot nella stessa tabella dell'ordine degli intervalli di arrivo. In questo modo posso ordinare per data_da ASC, mostrando prima i primi slot disponibili sui risultati della ricerca.
Struttura della tabella corrente
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;
Query di ricerca
Devo filtrare per: data effettiva, city_id, subject_id e se è disponibile uno slot (status = 0).
Per ogni ora devo mostrare tutte le fasce orarie disponibili per il primo giorno disponibile più vicino per ogni insegnante (mostra tutte le fasce orarie di un determinato giorno e non posso mostrare più di un giorno per lo stesso insegnante). (Ho ricevuto la domanda con l'aiuto di mattedgod ).
Per il range based (order_of_arrival = 1), devo mostrare il range più vicino disponibile, solo una volta per insegnante.
La prima query viene eseguita individualmente in circa 0,10 ms, la seconda query 0,08 ms e UNION ALL in media 300ms.
(
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;
Domanda
Esiste un modo per ottimizzare UNION, in modo che io possa ottenere una risposta ragionevole di un massimo di ~ 20 ms o persino un intervallo di ritorno basato su + ogni ora in una sola query (con un IF, ecc.)?
SQL Fiddle: http://www.sqlfiddle.com/#!2/59420/1/0
MODIFICARE:
Ho provato un po 'di denormalizzazione creando un campo "only_date_from" in cui ho memorizzato solo la data, quindi ho potuto cambiare questo ...
DATE(MIN(date_from)) as closestDay / DATE(teacher_slots.date_from) = closestDay
... a questo
MIN(only_date_from) as closestDay / teacher_slots.only_date_from = closestDay
Mi ha già salvato 100ms! Ancora 200ms in media.