Агрегатная функция SQL в соотношении n:m

#sql #entity-relationship

Вопрос:

Домен представляет собой систему бронирования авиабилетов. Пользователи могут забронировать рейс. Полет состоит из одной или нескольких ног.

   ┌─────┐        ┌──────┐      ┌───────┐
  │ Leg ├────────┤Flight├──────┤Booking│
  └─────┘  *   * └──────┘1   * └───────┘
 

Чтобы показать пользователю список действительных рейсов, система должна выяснить, есть ли на рейсе свободные места. Максимальное количество доступных мест сохраняется в виде поля в Leg таблице. Забронированные места хранятся в Booking .

Полеты могут состоять из нескольких этапов. Например, может быть 3 ноги A -> B ->> C. И два рейса, которые должны быть забронированы пользователями:

  1. A -> B: Перелет из пункта А в пункт Б.
  2. A -> B ->> C: Перелет из A в C через B.

Я создал dbfiddle с попыткой группировки по ногам, которая дает мне количество забронированных мест на одну ногу. Но мне нужно сгенерировать список рейсов с наибольшим количеством забронированных пассажиров на один рейс. В приведенном выше примере: На рейсе 1 может быть меньше свободных мест, если пассажиры забронированы на рейс 2, потому что они оба «разделяют» первый этап.

 select l.id as Leg, sum(b.number_passengers) as BookedSeats
FROM flight f
     JOIN flight_legs fl on f.id = fl.flight_id
     JOIN leg l on fl.legs_id = l.id
     JOIN booking b on f.id = b.flight_id
GROUP BY l.id;
 

Как я могу создать список рейсов с доступными местами?

Пример

Схема

Нога

ID места_доступны
1 2
2 2

Полет

ID
1
2

Flight_Leg

flight_id leg_id порядок ног
1 1 0
1 2 1
2 1 0

Примечание: У рейса 1 две ноги, а у рейса 2 только одна нога. Оба рейса «разделяют» один и тот же первый этап.

Бронирование

ID flight_id number_passengers
1 1 1
2 2 1

Примечание: Один человек забронировал рейс 1 и летит обеими ногами. Другой человек забронировал рейс 2 только с первым этапом. Это означает, что два пассажира на первом этапе и один пассажир на оставшемся этапе.

Ожидаемый результат

Найдите все рейсы с доступными местами. Места могут быть заняты и другими рейсами.

flight_id свободные места
1 0
2 0

Примечание: На рейс 2 нет свободных мест, так как первый рейс полностью забронирован

Диаграмма

введите описание изображения здесь

Комментарии:

1. Введите желаемые выходные данные и, если возможно, примерные данные

2. @LucasEthen Добавил объяснение примера dbfiddle.

Ответ №1:

Следующий запрос реализует желаемый результат. Я использовал эту Lag функцию, чтобы перейти к предыдущей строке, если предыдущая часть была заполнена.

 SELECT Leg,
   case
     when Free_Seats = 0 then 0 
        else (case when PrevLeg = 0 then 0 else Free_Seats end) end as Free_Seats

FROM (SELECT Leg,Free_Seats,LAG(Free_Seats,1) OVER(ORDER BY Leg) PrevLeg
  FROM (SELECT l.id AS Leg,l.seats_available,b.number_passengers,fl.flight_id,
             (max(l.seats_available) OVER(PARTITION BY fl.flight_id) - sum(b.number_passengers) OVER(PARTITION BY fl.flight_id)) AS Free_Seats,
             ROW_NUMBER() OVER(PARTITION BY fl.flight_id ORDER BY fl.flight_id) AS seq
         FROM flight f
          JOIN flight_legs fl ON f.id = fl.flight_id
          JOIN leg l ON fl.legs_id = l.id
          JOIN booking b ON f.id = b.flight_id) T
 WHERE t.seq=1) T
 

Комментарии:

1. Спасибо. Это дает мне свободные места на каждую ногу. Но мне нужны свободные места на рейс .

2. Я понимаю, но ваш желаемый результат неверен, потому что flight_id = 1 содержит две записи о ногах и в общей сложности четыре места, из которых осталось два места.

3. Тогда я не был ясен с моим примером: на первом этапе есть 1 пассажир из Бронирования 1 (забронированный рейс 1) и 1 пассажир из бронирования 2 (забронированный рейс 2). Поэтому рейс 1 должен предоставить 0 свободных мест, потому что на первом этапе забронировано два человека (но для разных рейсов). Рейс 2 также должен предоставить 0 свободных мест, потому что первый этап заполнен, хотя на втором этапе будет доступно свободное место. Обновлен ожидаемый результат.

4. Я реализовал ваш вывод. Я надеюсь, что это соответствует вашему запросу.

5. К сожалению, это не работает: независимо от того, сколько мест установлено для leg1, свободные места всегда равны 0 ( демо ). И это не дает мне свободных мест на рейс. Вместо этого он предоставляет бесплатные места для ног.