Вот план бронирования мест в кинотеатре.
Seat No Status 1 Booked 2 Available 3 Available 4 Available 5 Available 6 Available 7 Booked 8 Available 9 Available 10 Available
Если кто-то хочет забронировать 6 билетов, он получит место № 2 до 6 и место № 8. И если кто-то захочет забронировать только 5 билетов, он получит место № 2 до 6
Как узнать, используя SQL-запрос (или PHP-код), если соседние места доступны больше, чем запрашиваемые места?
Последовательный выбор места является основной целью, которую мне нужно достичь.
Попробуй это:
select seat, status from seats where seat >= ( select a.seat from seats a left join seats b on a.seat < b.seat and b.seat < a.seat + 4 and b.status = 'Available' where a.status = 'Available' group by a.seat having count(b.seat)+1 = 4 ) limit 4
Это позволяет выбрать четыре последовательных сиденья. Отрегулируйте все экземпляры «4» на нужное количество мест, чтобы получить то, что вы хотите.
лучше представлять Booked / Available в виде двоичных чисел (например, 1-бесплатно, 0-забронировано). Если вы это сделаете, вы можете элегантно использовать агрегатные функции:
select seat as n from seats where $num_seats = (select sum(status) from seats where seat between n and n + $num_seats - 1)
Один проход. Поместите вместо вас номер ?
, Дает вам номер места в первой последовательности, когда было выполнено ваше требование, или NULL
если последовательность не найдена.
SET @FOUND = 0; SET @SEAT_MATCHED = NULL; SELECT IF(@FOUND < ?, @FOUND := IF(status == 'Booked', 0, @FROM + 1), @SEAT_MATCHED := IFNULL(@SEAT_MATCHED, seat_no) ) FROM seats ORDER BY seat_no SELECT @SEAT_MATCHED;
Дополнительная информация: Функции потока управления , пользовательские переменные
NB! Этот подход применим только в том случае, если в анализируемом интервале имеется несколько записей!
Обновить. Возможно, вы можете сохранить битмаску забронированных мест в строке как целое число. Например, для 16- 36884
ряда номер 36884
( 1001000000010100
в двоичном виде) означает 3-е, 5-е, 13-е и 16-е места . Это сократило бы нагрузку MySQL. И тогда вы можете сделать код следующим образом:
<?php header('Content-Type: text/plain'); // data you get from DB $seats = bindec('1001000000010100'); $num_seats = 16; // calculate consecutive free seats $seats_info = array(); for ($i = 0; $i < $num_seats; $i++, $seats >>= 1) { if ($seats & 1) { if (isset($first)) { $seats_info[$first] = $i - $first; unset($first); } } else { if (!isset($first)) { $first = $i; } } } // output sequences var_export($seats_info); ?>
с<?php header('Content-Type: text/plain'); // data you get from DB $seats = bindec('1001000000010100'); $num_seats = 16; // calculate consecutive free seats $seats_info = array(); for ($i = 0; $i < $num_seats; $i++, $seats >>= 1) { if ($seats & 1) { if (isset($first)) { $seats_info[$first] = $i - $first; unset($first); } } else { if (!isset($first)) { $first = $i; } } } // output sequences var_export($seats_info); ?>
Эти результаты:
array ( 0 => 2, 3 => 1, 5 => 7, 13 => 2, )
0
– 1-е место.
SELECT a.seat_no SEAT1, a.seat_no + 1 SEAT2, a.seat_no + 2 SEAT3 FROM theater a WHERE a.availability = 'Y' AND seat_no + 1 = (SELECT b.seat_no FROM theater b WHERE b.seat_no = a.seat_no + 1 and b.availability = 'Y') AND seat_no + 2 = (SELECT b.seat_no FROM theater b WHERE b.seat_no = a.seat_no + 2 and b.availability = 'Y');
Я бы предложил один рекурсивный алгоритм с использованием SQL и PHP. Вам нужно X мест.
Выберите все доступные места с использованием SQL-запроса, вы получите N доступных мест (если N <X, ошибка)
В php анализируйте результаты и храните их, используя в качестве ключа размеры соседних мест (может быть более одной группы с одинаковым размером)
'5' => (2, 3, 4, 5, 6)
'2' => (8, 9)
Попробуйте найти группу с X местами
Если не найдено, выберите ближайшую группу с размером> X (для X = 4 это группа «5»)
Если не найдено более крупных групп, выньте самый большой доступный (группа с размером Y), затем повторите шаги 3 – 5 с X = X – Y
Изменить. Поскольку я неправильно понял вопросы здесь, SQL-заявление, которое вернет все первое свободное место и количество смежных мест с первого свободного. На первом месте стоит большее количество свободных мест.
SELECT count(1) free,( CASE status WHEN "Booked" THEN @prev:=NULL ELSE @prev:=COALESCE(cast(@prev as unsigned), seat_no) END) first FROM (SELECT @prev:=null) f, (SELECT seat_no, status FROM seats ORDER BY seat_no) seats GROUP BY first HAVING first>=0 ORDER BY 1 DESC, 2
Итак, для вашего примера он вернется:
free | first ----------- 5 2 3 8
Если вас интересует только первое сиквенное сиденье, которое может поместиться в ваш запрос, и не более просто добавьте условие на свободное количество мест, поэтому, если вы хотите, чтобы 3 места добавили free>=3
вы сделаете это:
SELECT count(1) free,( CASE status WHEN "Booked" THEN @prev:=NULL ELSE @prev:=COALESCE(cast(@prev as unsigned), seat_no) END) first FROM (SELECT @prev:=null) f, (SELECT seat_no, status FROM seats ORDER BY seat_no) seats GROUP BY first HAVING first>0 AND free>=3 LIMIT 1
это выведет:
free | first ------------ 5 2