выбор последовательных чисел с использованием SQL-запроса

Вот план бронирования мест в кинотеатре.

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, ) 

01-е место.

 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 мест.

  1. Выберите все доступные места с использованием SQL-запроса, вы получите N доступных мест (если N <X, ошибка)

  2. В php анализируйте результаты и храните их, используя в качестве ключа размеры соседних мест (может быть более одной группы с одинаковым размером)

    '5' => (2, 3, 4, 5, 6)

    '2' => (8, 9)

  3. Попробуйте найти группу с X местами

  4. Если не найдено, выберите ближайшую группу с размером> X (для X = 4 это группа «5»)

  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