У мене є таблиця, в якій зберігаються доступні зустрічі для вчителів, що дозволяють два види вставок:
Погодинна основа : з цілковитою свободою додавати необмежену кількість слотів на день на кожного вчителя (доки слоти не перетинаються): 15 квітня у вчителя можуть бути слоти на 10:00, 11:00, 12:00 та 16:00 . Людина обслуговується після вибору конкретного часу / слоту викладача.
Період часу / діапазон : 15 квітня інший викладач може працювати з 10:00 до 12:00, а потім з 14:00 до 18:00. Людину обслуговують за наказом про прибуття, тому якщо вчитель працює з 10:00 до 12:00, усі особи, які приїхали в цей період, будуть відвідувати замовлення на прибуття (місцева черга).
Оскільки я повинен повернути всіх доступних викладачів у пошуку, мені потрібно зберегти всі слоти в тій самій таблиці, що і порядок прильоту. Таким чином я можу замовити дату з ASC, показуючи перші доступні слоти спочатку в результатах пошуку.
Поточна структура таблиці
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;
Пошуковий запит
Мені потрібно фільтрувати за: фактичним датою, city_id, subject_id та якщо слот доступний (статус = 0).
Для погодинної основи я повинен показати всі доступні слоти для першого найближчого доступного дня для кожного вчителя (показати все тимчасові інтервали в певний день і не можу показати більше , ніж один день для того ж самого вчителя). (Я отримав запит за допомогою від mattedgod ).
Для діапазону (order_of_arrival = 1) я повинен показати найближчий доступний діапазон, лише один раз на кожного вчителя.
Перший запит виконується індивідуально приблизно за 0,10 мс, другий запит 0,08 мс, а СПІЛЬ ВСІ В середньому 300 мс.
(
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;
Питання
Чи є спосіб оптимізувати Спілкування, щоб я міг отримати розумну відповідь максимум ~ 20 мс або навіть діапазон повернення на основі + погодинний, що базується лише на одному запиті (з ПЧ тощо)?
SQL Fiddle: http://www.sqlfiddle.com/#!2/59420/1/0
Редагувати:
Я спробував деяку денормалізацію, створивши поле "only_date_from", де я зберігав лише дату, так що я міг змінити це ...
DATE(MIN(date_from)) as closestDay / DATE(teacher_slots.date_from) = closestDay
... до цього
MIN(only_date_from) as closestDay / teacher_slots.only_date_from = closestDay
Це вже врятувало мене 100 мс! Ще в середньому 200 мс.