Похоже на эту распространенную проблему интервью: https://www.geeksforgeeks.org/find-the-point-where-maximum-intervals-overlap/
Если ваши «максимальные вещи» жестко запрограммированы, тогда да, я думаю, вы найдете максимальное резервирование вещей в течение заданного времени и вычтете из максимального, чтобы получить минимальную доступность в диапазоне. Для этого вам необходимо учитывать все заказы, которые начинаются или заканчиваются в вашем диапазоне. Как показывает эффективное решение по этой ссылке, вы сортируете начало и конец, а затем просматриваете их, чтобы увидеть текущее использование.
Чтобы обработать граничный случай, о котором вы говорили, где окончание бронирования совпадает с началом бронирования, убедитесь, что сортировка «начинается» и «заканчивается» всегда сортирует «конец» перед «началом», когда время то же самое, чтобы у вас не возникало совпадения, когда его не существует на самом деле.
Я предполагаю, что вы работаете за таким столом:
CREATE TABLE `bookings` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`start` datetime NOT NULL,
`end` datetime NOT NULL,
PRIMARY KEY (`id`)
);
И у вас есть такие данные:
INSERT INTO `bookings` (`id`, `start`, `end`)
VALUES
(1, '2019-05-05 09:00:00', '2019-05-05 11:00:00'),
(2, '2019-05-05 11:00:00', '2019-05-05 13:00:00'),
(3, '2019-05-05 07:00:00', '2019-05-05 10:00:00'),
(4, '2019-05-05 12:00:00', '2019-05-05 14:00:00'),
(5, '2019-05-05 14:00:00', '2019-05-05 17:00:00'),
(6, '2019-05-05 10:30:00', '2019-05-05 11:30:00');
В этих данных есть все соответствующие случаи, которые необходимо рассмотреть:
- Бронирования, заканчивающиеся до вашего диапазона
- Бронирования, начинающиеся после вашего диапазона
- Бронирования, которые пересекают начало вашего диапазона
- Бронирования, которые пересекают конец вашего диапазона
- Заказы содержатся полностью в пределах вашего диапазона
- Бронирования, которые "передаются" в пределах вашего диапазона
Я посмотрел на создание SQL-запроса для этого, но остановился, когда он стал более сложным, чем вы на самом деле хотели бы кодировать в SQL. Я думаю, что это, вероятно, выполнимо, но требует действительно ужасного SQL. Определенно сделайте это в коде. Я думаю, что алгоритм состоит в том, чтобы загрузить все заказы, относящиеся к вашему диапазону, с помощью запроса вроде:
SELECT * FROM bookings
WHERE start BETWEEN :start AND :end
OR end BETWEEN :start AND :end
Это все заказы, которые могут иметь значение. Затем вы сортируете начальные и конечные события как отдельные события, как описано ранее, и перебираете список событий, сохраняя счетчик хода и максимальное значение, видимое до сих пор. Счетчик хода сначала увеличивается, а в конце возвращается к нулю, но может иметь несколько пиков в середине. Таким образом, вы не можете просто остановить первый раз, когда он выходит из строя, вы должны продолжать сканирование до тех пор, пока не пройдете все события (или, по крайней мере, до вашего конечного времени, но проще и без реальных затрат, чтобы просто пройти весь путь через список). Когда вы закончите, вы получите максимальное количество одновременных заказов.
Я не проверял и не компилировал это, поэтому воспринимайте это как псевдо-код. Я бы изменил ваш код на что-то вроде этого:
//assumedly, prior to this point you have a
//$startTime and $endTime that you used to do the query mentioned above
//and the results of the query are in $results
$times = array();
foreach($results as $result){
$time = array();
$time['time'] = DateTime::createFromFormat("Y-m-d H:i:s", $result['start'])->getTimestamp();
$time['change'] = $result['cnt'];
$times[] = $time;
$time['time'] = DateTime::createFromFormat("Y-m-d H:i:s", $result['end'])->getTimestamp();
$time['change'] = -1 * $result['cnt'];
$times[] = $time;
}
usort($times, function($lh, $rh) {
if ($lh['time'] === $rh['time']) {
return $lh['change'] - $rh['change']
} else {
return $lh['time'] < $rh['time'] ? -1 : 1;
}
}
$maxPeak = 0;
$curVal = 0;
foreach($times as $time){
//breaking early here isn't so much about optimization as it is about
//dealing with the instantaneous overlap problem where something starts
//right at the end time. You don't want that to look like a utilization
//that counts against you.
if ($time['time'] === $endTime) {
break;
}
$curVal += $time['change'];
if ($curVal > $maxPeak) {
$maxPeak = $curVal;
}
}
//$maxPeak is the max number of things in use during the period