Mam tabelę, która przechowuje dostępne terminy dla nauczycieli, pozwalając na dwa rodzaje wstawek:
Oparte na godzinach: z całkowitą swobodą dodawania nieograniczonej liczby miejsc dziennie na nauczyciela (o ile miejsca nie pokrywają się): w dniu 15 kwietnia kwietnia nauczyciel może mieć miejsca o 10:00, 11:00, 12:00 i 16:00 . Osoba jest obsługiwana po wybraniu określonego czasu nauczyciela / przedziału czasowego.
Przedział czasu / zakres : 15 kwietnia kwietnia inny nauczyciel może pracować od 10:00 do 12:00, a następnie od 14:00 do 18:00. Osoba jest obsługiwana według kolejności przyjazdu, więc jeśli nauczyciel pracuje od 10:00 do 12:00, wszystkie osoby, które przybędą w tym okresie, będą uczęszczane według kolejności przyjazdu (kolejka lokalna).
Ponieważ muszę zwrócić wszystkich dostępnych nauczycieli w poszukiwaniu, potrzebuję zapisać wszystkie miejsca w tej samej tabeli co zakresy kolejności przybycia. W ten sposób mogę zamówić według daty_z ASC, pokazując pierwsze dostępne automaty jako pierwsze w wynikach wyszukiwania.
Obecna struktura tabeli
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;
Wyszukiwana fraza
Muszę filtrować według: aktualnej daty / godziny, city_id, subject_id i czy dostępny jest slot (status = 0).
Na podstawie godzinowej muszę pokazać wszystkie dostępne przedziały dla pierwszego najbliższego dostępnego dnia dla każdego nauczyciela (pokaż wszystkie przedziały czasowe danego dnia i nie mogę pokazać więcej niż jednego dnia dla tego samego nauczyciela). (Otrzymałem zapytanie z pomocą Mattedgod ).
W przypadku zakresu (order_of_arrival = 1) muszę pokazać najbliższy dostępny zakres, tylko jeden raz na nauczyciela.
Pierwsze zapytanie uruchamia się indywidualnie w około 0,10 ms, drugie zapytanie 0,08 ms, a UNION ALL średnio 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;
Pytanie
Czy istnieje sposób na zoptymalizowanie UNION, aby uzyskać rozsądną odpowiedź wynoszącą maksymalnie ~ 20 ms lub nawet zwrot oparty na podstawie + co godzinę na podstawie tylko jednego zapytania (z IF, itp.)?
SQL Fiddle: http://www.sqlfiddle.com/#!2/59420/1/0
EDYTOWAĆ:
Próbowałem trochę denormalizacji, tworząc pole „only_date_from”, w którym zapisałem tylko datę, więc mogłem to zmienić ...
DATE(MIN(date_from)) as closestDay / DATE(teacher_slots.date_from) = closestDay
... do tego
MIN(only_date_from) as closestDay / teacher_slots.only_date_from = closestDay
To już zaoszczędziło mi 100ms! Średnio nadal 200 ms.