Демонстрационная база данных

Схема маршрутов демобазы

Представляем вам демонстрационную базу данных для СУБД PostgreSQL. В качестве предметной области мы используем авиаперевозки.

База данных может использоваться, например,

  • для самостоятельного изучения языка запросов SQL,
  • для подготовки книг, пособий и учебных курсов по языку SQL,
  • для демонстрации возможностей PostgreSQL в статьях и заметках.

При разработке демонстрационной базы данных мы преследовали несколько целей:

  • схема данных должна быть достаточно простой, чтобы быть понятной без особых пояснений,
  • в то же время схема данных должна быть достаточно сложной, чтобы позволять строить осмысленные запросы,
  • база данных должна быть наполнена данными, напоминающими реальные, с которыми будет интересно работать.

Демонстрационная база данных распространяется под лицензией MIT.
Свои замечания и пожелания направляйте нам по адресу [email protected].

Версия от 01.09.2025

Установка

Демонстрационная база данных доступна в четырёх версиях, которые отличаются объёмом данных:

  • demo-20250901-3m.sql.gz (133 Мбайт) — данные по полётам за три месяца (размер БД примерно 1,3 Гбайт),
  • demo-20250901-6m.sql.gz (276 МБ) — данные по полётам за шесть месяцев (размер БД примерно 2,7 Гбайт),
  • demo-20250901-1y.sql.gz (558 Мбайт) — данные по полётам за год (размер БД примерно 5,4 Гбайт).
  • demo-20250901-2y.sql.gz (1137 Мбайт) — данные по полётам за два года (размер БД примерно 11 Гбайт).

Небольшая база годится для того, чтобы писать запросы, и при этом не займёт много места на диске. База большого размера позволит почувствовать, как ведут себя запросы на больших объёмах данных, и задуматься об оптимизации.

Файлы содержат SQL-скрипт, создающий базу данных demo и наполняющий её данными (фактически, это резервная копия, созданная утилитой pg_dump). Владельцем базы данных demo станет пользователь СУБД, выполнявший скрипт. Например, чтобы создать небольшую базу данных, запустите скрипт от имени пользователя postgres в программе psql:

gunzip -c demo-20250901-3m.sql.gz | psql -U postgres

Обратите внимание, что если база данных demo уже существует, она будет удалена и создана заново!

Минимальная поддерживаемая версия — PostgreSQL 15.

Создать собственный вариант демобазы можно с помощью генератора.

Отличия от предыдущей версии

  • Маршрутная сеть расширена на 17 стран: Россия, Китай, Индия, США, Канада, Япония, Франция, Германия, Италия, Великобритания, Чили, Швеция, Непал, Финляндия, Новая Зеландия, Австрия и Чехия. В этот список вошли страны ведущих разработчиков PostgreSQL, а также несколько других стран, имеющих определенное отношение к PostgreSQL.
  • Количество аэропортов в таблице airports_data увеличено до примерно 5500. В маршрутной сети участвует менее сотни аэропортов, но большое количество строк в таблице позволяет демонстрировать индексирование точек методами GiST и SP-GiST.
  • В таблицу airports_data добавлена страна: столбец country типа jsonb с переводами, аналогично airport_name и city. Отдельного справочника стран в демобазе нет.
  • Таблица aircrafts переименована в airplanes. Прежнее название было неудачным, поскольку в английском языке слово aircraft не меняется во множественном числе.
  • Изменился авиапарк: используются более новые и более крупные самолёты. Из десяти моделей две являются грузовыми и не участвуют в пассажирских авиаперевозках.
  • Из таблицы tickets убран столбец contact_info, поскольку его простая структура не позволяла демонстрировать средства работы с JSON. Разнообразный JSON можно получить, например, агрегацией бронирований со вложенными билетами и перелетами.
  • Номер документа tickets.passenger_id однозначно определяет пассажира (одному номеру документа всегда соответствует одно и то же имя passenger_name, хотя в разных документах имена, конечно, могут совпадать). В дополнение к цифрам номер документа содержит код страны.
  • Регистрация выполняется только на первый рейс в билете. При этом выдаются посадочные талоны сразу на все входящие в билет рейсы.
  • На обратный рейс пассажиру выдается отдельный билет. Прямые рейсы отмечены в столбце tickets.outbound типа boolean значением true, а обратные — false. В таблицу tickets добавлено ограничение уникальности на столбцы book_ref, passenger_id и outbound (на одного пассажира можно забронировать только два билета — прямой и обратный).
  • Таблица ticket_flights переименована в segments, а столбец amount — в более точное price.
  • Число пересадок ограничено четырьмя.
  • Представление routes стало полноценной таблицей, определяющей маршруты. Маршруты меняются со временем, поэтому первичный ключ является темпоральным. Его составляют номер маршрута route_no (ранее называвшийся номером рейса flight_no) и интервал действия validity. Ключ реализован ограничением-исключением (полноценная поддержка темпоральных ключей появится в PostgreSQL 18).
  • Из таблицы flights в таблицу routes перенесены столбцы departure_airport, arrival_airport, airplane_code. Таблицы соединяются по условию routes.route_no = flights.route_no AND routes.validity @> flights.scheduled_departure.
  • Статус рейса flights.status принимает новое значение Boarding во время посадки пассажиров.
  • В таблицу boarding_passes добавлен столбец boarding_time — время посадки в самолёт. Вместе со столбцом boarding_no может иметь неопределенное значение в промежутке между регистрацией и посадкой на рейс.
  • Новое представление timetable скрывает темпоральное соединение routes и flights, а также выводит время в часовом поясе аэропортов. Чтобы облегчить переход к новой версии демобазы, можно использовать это представление вместо старой таблицы flights или удалённого представления flights_v.
  • Ряд столбцов использует тип text вместо различных модификаций char и varchar: номер документа passenger_id, статус рейса status, номер маршрута route_no, номер места seat_no и класс обслуживания fare_conditions.
  • Пассажиры, опоздавшие на стыковочный рейс (прибывшие в аэропорт менее чем за 40 минут до вылета), не попадают ни на этот рейс, ни на следующие рейсы в том же билете.

Схема данных

Диаграмма схемы данных

Основной сущностью является бронирование (bookings).

В одно бронирование можно включить несколько пассажиров, каждому из которых выписывается билет (tickets) на поездку в прямом направлении, а также отдельный обратный билет, если билеты приобретаются «туда и обратно». Билет имеет уникальный номер и содержит информацию о пассажире. Для пассажира нет отдельной сущности, но можно считать, что номер документа уникально идентифицирует пассажира.

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

Маршруты (routes) проложены из одного аэропорта (airports) в другой. Каждый рейс (flights) следует по определенному маршруту, так что рейсы с одним номером маршрута имеют одинаковые пункты вылета и назначения, но отличаются датой отправления.

Маршрутная сеть меняется раз в месяц, поэтому рейсы и маршруты связаны темпоральным внешним ключом: при соединении следует учитывать не только номер маршрута, но и соответствие времени отправления интервалу действия маршрута. Для упрощения запросов можно использовать представление timetable, скрывающее соединение этих таблиц.

Все перелеты считаются стыковочными: пассажир проходит регистрацию на первый рейс и сразу получает посадочные талоны (boarding_passes) на все рейсы в своем билете. В посадочном талоне указано место, а также фиксируется время посадки в самолёт. Пассажир может зарегистрироваться только на тот рейс, который есть у него в билете. Комбинация рейса и места в самолёте уникальна, чтобы не допустить выдачу двух посадочных талонов на одно место.

Количество мест (seats) в самолёте и их распределение по классам обслуживания зависит от модели самолёта (airplanes), выполняющего рейс. Предполагается, что каждая модель самолёта имеет только одну компоновку салона. Схема данных не контролирует, что места в посадочных талонах соответствуют имеющимся в самолёте (такая проверка может быть сделана с использованием табличных триггеров или в приложении).

Объекты схемы

Схема bookings содержит все объекты демонстрационной базы:

          Имя          |        Тип         |       Описание  
-----------------------+--------------------+----------------------
 airplanes             | представление      | Самолёты
 airplanes_data        | таблица            | Самолёты (переводы)
 airports              | представление      | Аэропорты
 airports_data         | таблица            | Аэропорты (переводы)
 boarding_passes       | таблица            | Посадочные талоны
 bookings              | таблица            | Бронирования
 flights               | таблица            | Рейсы
 flights_flight_id_seq | последовательность | 
 routes                | таблица            | Маршруты
 seats                 | таблица            | Места
 segments              | таблица            | Перелёты
 tickets               | таблица            | Билеты
 timetable             | представление      | Расписание

При подключении к базе параметр конфигурации search_path автоматически принимает значение bookings,"$user",public, так что явно указывать имя схемы необязательно.

Однако для функций bookings.now и bookings.version в любом случае необходимо явно указывать схему, чтобы отличать их от одноимённых стандартных функций.

Представление bookings.airplanes

Каждая модель воздушного судна идентифицируется трёхзначным кодом ИАТА (airplane_code). Указывается также название модели (model), максимальная дальность полета в километрах (range) и крейсерская скорость в километрах в час (speed).

Значение поля model определяется в зависимости от выбранного языка (параметр bookings.lang).

    Столбец    |   Тип   |              Описание
---------------+---------+------------------------------------
 airplane_code | char(3) |  Код самолёта, ИАТА
 model         | text    |  Модель самолёта
 range         | integer |  Максимальная дальность полета, км
 speed         | integer |  Крейсерская скорость, км/ч
Определение представления:
 SELECT airplane_code,
    model ->> lang() AS model,
    range,
    speed
   FROM airplanes_data ml;

 

Таблица bookings.airplanes_data

Это базовая таблица для представления airplanes. Поле model этой таблицы содержит переводы моделей самолётов на разные языки, в формате JSONB. В большинстве случаев к этой таблице не следует обращаться напрямую.

    Столбец    |   Тип   | Допустимость NULL |             Описание
---------------+---------+-------------------+-----------------------------------
 airplane_code | char(3) | not null          | Код самолёта, ИАТА
 model         | jsonb   | not null          | Модель самолёта
 range         | integer | not null          | Максимальная дальность полета, км
 speed         | integer | not null          | Крейсерская скорость, км/ч
Индексы:
    PRIMARY KEY, btree (airplane_code)
Ограничения-проверки:
    CHECK (range > 0)
    CHECK (speed > 0)
Ссылки извне:
    TABLE "routes" FOREIGN KEY (airplane_code)
        REFERENCES airplanes_data(airplane_code)
    TABLE "seats" FOREIGN KEY (airplane_code)
        REFERENCES airplanes_data(airplane_code) ON DELETE CASCADE

 

Представление bookings.airports

Аэропорт идентифицируется трёхбуквенным кодом ИАТА (airport_code) и имеет своё имя (airport_name).

Для города и страны не предусмотрено отдельных сущностей, но введены поля city и country, позволяющее найти аэропорты одного города или страны. Это представление также включает координаты аэропорта (coordinates) и часовой пояс (timezone).

Значения полей airport_name, city и country определяются в зависимости от выбранного языка (параметр bookings.lang).

   Столбец    |   Тип   |                  Описание
--------------+---------+------------------------------------------
 airport_code | char(3) |  Код аэропорта, ИАТА
 airport_name | text    |  Название аэропорта
 city         | text    |  Город
 country      | text    |  Страна
 coordinates  | point   |  Координаты аэропорта (долгота и широта)
 timezone     | text    |  Часовой пояс аэропорта
Определение представления:
 SELECT airport_code,
    airport_name ->> lang() AS airport_name,
    city ->> lang() AS city,
    country ->> lang() AS country,
    coordinates,
    timezone
   FROM airports_data ml;

 

Таблица bookings.airports_data

Это базовая таблица для представления airports. Она содержит переводы значений airport_name, city и country на разные языки, в формате JSONB. В большинстве случаев к этой таблице не следует обращаться напрямую.

   Столбец    |   Тип   | Допустимость NULL |                 Описание
--------------+---------+-------------------+-----------------------------------------
 airport_code | char(3) | not null          | Код аэропорта, ИАТА
 airport_name | jsonb   | not null          | Название аэропорта
 city         | jsonb   | not null          | Город
 country      | jsonb   | not null          | Страна
 coordinates  | point   | not null          | Координаты аэропорта (долгота и широта)
 timezone     | text    | not null          | Часовой пояс аэропорта
Индексы:
    PRIMARY KEY, btree (airport_code)
Ссылки извне:
    TABLE "routes" FOREIGN KEY (arrival_airport)
        REFERENCES airports_data(airport_code)
    TABLE "routes" FOREIGN KEY (departure_airport)
        REFERENCES airports_data(airport_code)

 

Таблица bookings.boarding_passes

При регистрации на первый рейс, которая возможна за сутки до плановой даты отправления, пассажиру выдаются посадочные талоны на все рейсы в билете. В посадочном талоне указывается номер места (seat_no). Посадочный талон идентифицируется также, как и перелёт — номером билета и идентификатором рейса.

При посадке пассажиров в самолёт посадочным талонам присваиваются последовательные номера (boarding_no, этот номер уникален только в пределах данного рейса) и фиксируется время посадки (boarding_time).

    Столбец    |     Тип     | Допустимость NULL |         Описание
---------------+-------------+-------------------+--------------------------
 ticket_no     | text        | not null          | Номер билета
 flight_id     | integer     | not null          | Идентификатор рейса
 seat_no       | text        | not null          | Номер места
 boarding_no   | integer     |                   | Номер посадочного талона
 boarding_time | timestamptz |                   | Время посадки
Индексы:
    PRIMARY KEY, btree (ticket_no, flight_id)
    UNIQUE CONSTRAINT, btree (flight_id, boarding_no)
    UNIQUE CONSTRAINT, btree (flight_id, seat_no)
Ограничения внешнего ключа:
    FOREIGN KEY (ticket_no, flight_id)
        REFERENCES segments(ticket_no, flight_id)

 

Таблица bookings.bookings

Продажа билетов начинается за 60 дней до рейса. Пассажир заранее (book_date) бронирует билет себе и, возможно, нескольким другим пассажирам. Бронирование идентифицируется шестизначной комбинацией букв и цифр (book_ref).

Поле total_amount хранит общую стоимость включённых в бронирование перелетов всех пассажиров.

   Столбец    |      Тип      | Модификаторы |         Описание
--------------+---------------+--------------+---------------------------
 book_ref     | char(6)       | not null     | Номер бронирования
 book_date    | timestamptz   | not null     | Дата бронирования
 total_amount | numeric(10,2) | not null     | Полная сумма бронирования
Индексы:
    PRIMARY KEY, btree (book_ref)
Ссылки извне:
    TABLE "tickets" FOREIGN KEY (book_ref) REFERENCES bookings(book_ref)

 

Таблица bookings.flights

Естественный ключ таблицы рейсов состоит из двух полей — номера маршрута (route_no) и плановой даты отправления (scheduled_departure). Чтобы сделать внешние ключи на эту таблицу компактнее, в качестве первичного используется суррогатный ключ (flight_id).

У каждого рейса есть запланированные дата и время вылета (scheduled_departure) и прибытия (scheduled_arrival). Реальные время вылета (actual_departure) и прибытия (actual_arrival) могут отличаться: обычно не сильно, но иногда и на несколько часов, если рейс задержан.

Статус рейса (status) может принимать одно из следующих значений:

  • Scheduled. Рейс доступен для бронирования. Это происходит за 60 дней до плановой даты вылета; до этого запись о рейсе не существует в базе данных.
  • On Time. Открыта регистрация (за сутки до плановой даты вылета) и рейс не задержан.
  • Delayed. Открыта регистрация (за сутки до плановой даты вылета), но рейс задержан.
  • Boarding. Идет посадка пассажиров в самолёт.
  • Departed. Самолёт вылетел и находится в воздухе.
  • Arrived. Самолёт прибыл в пункт назначения.
  • Cancelled. Рейс отменён.
       Столбец       |     Тип     | Допустимость NULL |          Описание
---------------------+-------------+-------------------+-----------------------------
 flight_id           | integer     | not null          | Идентификатор рейса
 route_no            | text        | not null          | Номер маршрута
 status              | text        | not null          | Статус рейса
 scheduled_departure | timestamptz | not null          | Время вылета по расписанию
 scheduled_arrival   | timestamptz | not null          | Время прилёта по расписанию
 actual_departure    | timestamptz |                   | Фактическое время вылета
 actual_arrival      | timestamptz |                   | Фактическое время прилёта
Индексы:
    PRIMARY KEY, btree (flight_id)
    UNIQUE CONSTRAINT, btree (route_no, scheduled_departure)
Ограничения-проверки:
    CHECK (scheduled_arrival > scheduled_departure)
    CHECK (actual_arrival IS NULL
       OR  ((actual_departure IS NOT NULL AND actual_arrival IS NOT NULL)
            AND (actual_arrival > actual_departure)))
    CHECK (status IN ( 'Scheduled', 'On Time', 'Delayed', 'Boarding',
                      'Departed', 'Arrived', 'Cancelled'))
Ссылки извне:
    TABLE "segments" FOREIGN KEY (flight_id)
        REFERENCES flights(flight_id)

 

Таблица routes

Маршрут всегда соединяет две точки &mddash; аэропорты вылета (departure_airport) и прибытия (arrival_airport). Такое понятие, как «маршрут с пересадками» отсутствует: если из одного аэропорта до другого нет прямого рейса, в билет просто включаются несколько перелетов.

Маршрут имеет период действия (validity) длиной в месяц. Маршрут между теми же аэропортами может повторяться неоднократно; он будет иметь тот же номер route_no, однако в разные периоды может обслуживаться разными самолётами и отправляться в разные дни.

Рейсы, следующие данным маршрутом, обслуживается определенным типом самолёта (airplane_code) и вылетают в одно и то же время (scheduled_time, местное время в аэропорту отправления) в разные дни недели (массив days_of_week, 1 — понедельник, 7 — воскресенье).

       Столбец       |     Тип     | Допустимость NULL |          Описание
---------------------+-------------+-------------------+-----------------------------
 route_no            | text        | not null          | Номер маршрута
 validity            | tstzrange   | not null          | Период действия
 departure_airport   | char(3)     | not null          | Аэропорт отправления
 arrival_airport     | char(3)     | not null          | Аэропорт прибытия
 airplane_code       | char(3)     | not null          | Код самолёта, ИАТА
 days_of_week        | integer[]   | not null          | Дни недели, когда выполняются рейсы
 scheduled_time      | time        | not null          | Местное время вылета по расписанию
 duration            | interval    | not null          | Планируемая длительность полёта
Индексы:
    btree (departure_airport, lower(validity))
    EXCLUDE USING gist (route_no WITH =, validity WITH &&)
Ограничения внешнего ключа:
    FOREIGN KEY (airplane_code) REFERENCES airplanes_data(airplane_code)
    FOREIGN KEY (arrival_airport) REFERENCES airports_data(airport_code)
    FOREIGN KEY (departure_airport) REFERENCES airports_data(airport_code)

 

Таблица bookings.seats

Места определяют схему салона каждой модели. Каждое место определяется своим номером (seat_no) и имеет закреплённый за ним класс обслуживания (fare_conditions) — Economy, Comfort или Business.

     Столбец     |     Тип     | Допустимость NULL |      Описание
-----------------+-------------+-------------------+--------------------
 airplane_code   | char(3)     | not null          | Код самолёта, ИАТА
 seat_no         | text        | not null          | Номер места
 fare_conditions | text        | not null          | Класс обслуживания
Индексы:
    PRIMARY KEY, btree (airplane_code, seat_no)
Ограничения-проверки:
    CHECK (fare_conditions IN ('Economy', 'Comfort', 'Business'))
Ограничения внешнего ключа:
    FOREIGN KEY (airplane_code)
        REFERENCES airplanes_data(airplane_code) ON DELETE CASCADE

 

Таблица bookings.segments

Перелёт соединяет билет с рейсом и идентифицируется их номерами.

Для каждого перелета указываются его стоимость (price) и класс обслуживания (fare_conditions).

     Столбец     |     Тип       | Допустимость NULL |    Описание
-----------------+---------------+-------------------+---------------------
 ticket_no       | text          | not null          | Номер билета
 flight_id       | integer       | not null          | Идентификатор рейса
 fare_conditions | text          | not null          | Класс обслуживания
 price           | numeric(10,2) | not null          | Стоимость перелета
Индексы:
    PRIMARY KEY, btree (ticket_no, flight_id)
    btree (flight_id)
Ограничения-проверки:
    CHECK (price >= 0)
    CHECK (fare_conditions IN ('Economy', 'Comfort', 'Business'))
Ограничения внешнего ключа:
    FOREIGN KEY (flight_id) REFERENCES flights(flight_id)
    FOREIGN KEY (ticket_no) REFERENCES tickets(ticket_no)
Ссылки извне:
    TABLE "boarding_passes" FOREIGN KEY (ticket_no, flight_id)
        REFERENCES segments(ticket_no, flight_id)

 

Таблица bookings.tickets

Билет имеет уникальный номер (ticket_no), состоящий из 13 цифр.

Билет содержит информацию о пассажире: номер документа, удостоверяющего личность (passenger_id), а также его полное имя (passenger_name). Номер документа состоит из кода страны и цифрового идентификатора, а полное имя — из имени и фамилии (имя указывается первым даже для тех стран, в которых принят обратный порядок, как, например, в Китае).

Для пассажира нет отдельной сущности, но пассажир однозначно идентифицируется номером документа. Гарантируется, что одному номеру документа всегда соответствует одно и то же имя пассажира (в разных документах имена могут совпадать). Один и тот же пассажир не может участвовать в одном бронировании несколько раз и не может следовать разными рейсами в одно и то же время (хотя последнее условие не проверяется на уровне базы данных).

Признак прямого билета (outbound) имеет истинное значение для билета «туда» и ложное — для билета «обратно». Конечные пункты перелётов, включенных в прямой и обратный билеты, совпадают, но пути следования могут отличаться.

     Столбец    |     Тип     | Допустимость NULL |        Описание
----------------+-------------+-------------------+-------------------------
 ticket_no      | text        | not null          | Номер билета
 book_ref       | char(6)     | not null          | Номер бронирования
 passenger_id   | text        | not null          | Номер документа
 passenger_name | text        | not null          | Полное имя пассажира
 outbound       | boolean     | not null          | Является ли рейс прямым
Индексы:
    PRIMARY KEY, btree (ticket_no)
    UNIQUE CONSTRAINT, btree (book_ref, passenger_id, outbound)
Ограничения внешнего ключа:
    FOREIGN KEY (book_ref) REFERENCES bookings(book_ref)
Ссылки извне:
    TABLE "segments" FOREIGN KEY (ticket_no) REFERENCES tickets(ticket_no)

 

Представление bookings.timetable

Над таблицами routes и flights создано представление timetable, скрывающее темпоральное соединение и добавляющее информацию о местном времени вылета и прибытия. Представление позволяет упростить многие запросы, что особенно важно для тех, кто только начинает изучать язык SQL. Однако применение этого представления в сложных запросах может ухудшить производительность из-за потенциально лишних соединений с таблицами аэропортов.

          Столбец          |     Тип     |              Описание
---------------------------+-------------+--------------------------------------
 flight_id                 | integer     | Идентификатор рейса
 route_no                  | text        | Номер маршрута
 departure_airport         | char(3)     | Код аэропорта отправления
 arrival_airport           | char(3)     | Код аэропорта прибытия
 status                    | status      | Статус рейса
 airplane_code             | char(3)     | Код самолёта, ИАТА
 scheduled_departure       | timestamptz | Время вылета по расписанию
 scheduled_departure_local | timestamp   | Время вылета по расписанию,
                           |             | местное время в пункте отправления
 actual_departure          | timestamptz | Фактическое время вылета
 actual_departure_local    | timestamp   | Фактическое время вылета,
                           |             | местное время в пункте отправления
 scheduled_arrival         | timestamptz | Время прилёта по расписанию
 scheduled_arrival_local   | timestamp   | Время прилёта по расписанию,
                           |             | местное время в пункте прибытия
 actual_arrival            | timestamptz | Фактическое время прилёта
 actual_arrival_local      | timestamp   | Фактическое время прилёта,
                           |             | местное время в пункте прибытия
Определение представления:
 SELECT f.flight_id,
    f.flight_no,
    r.departure_airport,
    r.arrival_airport,
    f.status,
    r.airplane_code,
    f.scheduled_departure,
    (f.scheduled_departure AT TIME ZONE dep.timezone) AS scheduled_departure_local,
    f.actual_departure,
    (f.actual_departure AT TIME ZONE dep.timezone) AS actual_departure_local,
    f.scheduled_arrival,
    (f.scheduled_arrival AT TIME ZONE arr.timezone) AS scheduled_arrival_local,
    f.actual_arrival,
    (f.actual_arrival AT TIME ZONE arr.timezone) AS actual_arrival_local
   FROM flights f
     JOIN routes r ON r.flight_no = f.flight_no AND r.validity @> f.scheduled_departure
     JOIN airports_data dep ON dep.airport_code = r.departure_airport
     JOIN airports_data arr ON arr.airport_code = r.arrival_airport;

 

Функция bookings.now

Демонстрационная база содержит временной «срез» данных — так, как будто в некоторый момент была сделана резервная копия реальной системы. Например, если некоторый рейс имеет статус Departed, это означает, что в момент резервного копирования самолёт вылетел и находился в воздухе.

Позиция «среза» сохранена в функции bookings.now. Ей можно пользоваться в запросах там, где в обычной жизни использовалась бы функция now.

Функция bookings.lang

Некоторые поля в демонстрационной базе содержат текст на английском и русском языках. Функция bookings.lang возвращает значение параметра bookings.lang, то есть язык, на котором будут выдаваться значения этих полей.

Эта функция используется в представлениях airplanes и airports и не предназначена для непосредственного использования в запросах.

Функция bookings.version

Функция bookings.version возвращает версию демонстрационной базы данных. Версия состоит из названия воображаемой авиакомпании и даты начала авиаперевозок, а также сообщает охваченный интервал времени. Актуальная версия на текущий момент — PostgresPro 2025-09-01.

Использование

По умолчанию значения различных переводимых полей выдаются на английском языке. Это поля airport_name, city и country представления airports, а также поле model представления airplanes.

Вы можете выбрать другой язык для отображения этих полей. Для переключения на русский (единственный перевод, представленный в данной базе) установите для параметра bookings.lang значение ru:

SET bookings.lang = 'ru';

Также может быть удобно выбрать язык на уровне базы данных:

ALTER DATABASE demo SET bookings.lang = 'ru';

Чтобы это изменение вступило в силу, необходимо повторно подключиться к базе данных.

Посмотрим на результаты нескольких простых запросов, чтобы лучше познакомиться с содержимым демонстрационной базы данных.

Результаты, представленные ниже, были получены для версии PostgresPro 2025-09-01 (91 days). Если в вашей системе запросы выдают другие данные, проверьте версию демонстрационной базы (функция bookings.version). Незначительные отклонения могут быть связаны с местным временем, отличным от московского, и настройками локализации.

Все рейсы выполняются несколькими типами самолётов:

SELECT * FROM airplanes;
 airplane_code |         model          | range | speed
---------------+------------------------+-------+-------
 32N           | Аэробус A320neo        |  6500 |   830
 339           | Аэробус A330-900neo    | 13300 |   870
 351           | Аэробус A350-1000      | 16700 |   913
 35X           | Аэробус A350F          |  8700 |   903
 76F           | Боинг 767-300F         |  6000 |   850
 77W           | Боинг 777-300ER        | 14600 |   905
 789           | Боинг 787-9 Dreamliner | 14000 |   913
 7M7           | Боинг 737 MAX 7        |  7000 |   840
 CR7           | Бомбардье CRJ700       |  3100 |   829
 E70           | Эмбраэр E170           |  4000 |   800
(10 строк)

Для каждого типа самолёта хранится список мест в салоне. Например, вот где можно разместиться в бизнес-классе Эмбраэра E170:

SELECT *
FROM seats
WHERE airplane_code = 'E70' AND fare_conditions = 'Business';
 airplane_code | seat_no | fare_conditions
---------------+---------+-----------------
 E70           | 1A      | Business
 E70           | 1C      | Business
 E70           | 1D      | Business
 E70           | 2A      | Business
 E70           | 2C      | Business
 E70           | 2D      | Business
(6 строк)

Самолёты большего размера имеют больше посадочных мест с разными классами обслуживания:

SELECT
    s.airplane_code,
    string_agg (s.fare_conditions || '(' || s.num || ')', ', ') as fare_conditions
FROM (
        SELECT airplane_code, fare_conditions, count(*)::text as num
        FROM seats
        GROUP BY airplane_code, fare_conditions
     ) s
GROUP BY s.airplane_code
ORDER BY s.airplane_code;
 airplane_code |             fare_conditions
---------------+-----------------------------------------
 32N           | Business(28), Economy(138)
 339           | Business(29), Economy(224), Comfort(28)
 351           | Economy(281), Business(44)
 77W           | Economy(326), Business(30), Comfort(48)
 789           | Economy(188), Business(48), Comfort(21)
 7M7           | Business(16), Economy(144)
 CR7           | Business(6), Economy(52), Comfort(12)
 E70           | Business(6), Economy(72)
(8 строк)

Два типа самолётов (Аэробус A350F и Боинг 767-300F) являются грузовыми и не участвуют в пассажирских авиаперевозках.

База данных содержит список практически всех относительно крупных аэропортов:

SELECT
    count(*) airports,
    count(distinct country||','||city) cities,
    count(distinct country) countries
FROM airports;
 airports | cities | countries
----------+--------+-----------
     5501 |   5157 |       230
(1 строка)

Например, к Москве относятся пять аэропортов:

SELECT airport_code, airport_name, coordinates, timezone
FROM airports
WHERE country = 'Россия' AND city = 'Москва';
 airport_code | airport_name |    coordinates    |   timezone
--------------+--------------+-------------------+---------------
 BKA          | Быково       | (38.06,55.6172)   | Europe/Moscow
 DME          | Домодедово   | (37.9063,55.4088) | Europe/Moscow
 OSF          | Остафьево    | (37.5072,55.5117) | Europe/Moscow
 SVO          | Шереметьево  | (37.4146,55.9726) | Europe/Moscow
 VKO          | Внуково      | (37.2615,55.5915) | Europe/Moscow
(5 rows)

Маршрутная сеть хранится в таблице routes. Вот, например, куда, в какие дни недели и за какое время можно долететь из Рима первого ноября 2025 года:

SELECT r.route_no, a.airport_code, a.airport_name, a.city, a.country, r.days_of_week, r.duration
FROM routes r
    JOIN airports a ON a.airport_code = r.arrival_airport
WHERE departure_airport = (SELECT airport_code FROM airports WHERE airport_name = 'Фьюмичино')
    AND validity @> '2025-11-01 00:00:00CET'::timestamptz;
 route_no | airport_code | airport_name |  city   |      country      |  days_of_week   | duration
----------+--------------+--------------+---------+-------------------+-----------------+----------
 PG0086   | BGY          | Бергамо      | Милан   | Италия            | {1,2,3,4,5,6,7} | 01:05:00
 PG0176   | ORY          | Орли         | Париж   | Франция           | {2,4,6,7}       | 01:55:00
 PG0228   | HGH          | Сяошань      | Ханчжоу | Китай             | {7}             | 11:30:00
 PG0233   | MXP          | Мальпенса    | Милан   | Италия            | {1,2,3,4,5,6,7} | 01:05:00
 PG0235   | ORD          | О'Хара       | Чикаго  | Соединенные Штаты | {2}             | 09:50:00
(5 строк)

Обратите внимание, что время указано в часовом поясе аэропорта вылета (CET, Central European Time).

База данных была сформирована на момент, возвращаемый функцией bookings.now:

SELECT bookings.now();
          now
------------------------
 2025-12-01 00:00:00+03
(1 строка)

Относительно именно этого момента все рейсы делятся на прошедшие и будущие.

SELECT
    status,
    count(*) as count,
    min(scheduled_departure) as min_scheduled_departure,
    max(scheduled_departure) as max_scheduled_departure
FROM flights
GROUP BY status
ORDER BY min_scheduled_departure;
  status   | count | min_scheduled_departure | max_scheduled_departure
-----------+-------+-------------------------+-------------------------
 Arrived   | 10966 | 2025-10-01 03:00:00+03  | 2025-12-01 02:10:00+03
 Cancelled |   121 | 2025-10-01 15:25:00+03  | 2026-01-29 19:20:00+03
 Departed  |    20 | 2025-11-30 15:50:00+03  | 2025-12-01 02:50:00+03
 Boarding  |     4 | 2025-12-01 02:55:00+03  | 2025-12-01 03:25:00+03
 Delayed   |    10 | 2025-12-01 03:30:00+03  | 2025-12-02 01:00:00+03
 On Time   |   157 | 2025-12-01 03:35:00+03  | 2025-12-02 02:55:00+03
 Scheduled | 10480 | 2025-12-02 03:10:00+03  | 2026-01-30 02:55:00+03
(7 строк)

Найдем ближайший рейс, вылетающий из Екатеринбурга (аэропорт SVX) в Ухань (аэропорт WUH). Воспользуемся представлением timetable, чтобы не соединять таблицы routes и flights:

SELECT *
FROM timetable t
WHERE t.departure_airport = 'SVX'
  AND t.arrival_airport = 'WUH'
  AND t.scheduled_departure > bookings.now()
ORDER BY t.scheduled_departure
LIMIT 1 \gx
-[ RECORD 1 ]-------------+-----------------------
flight_id                 | 11465
route_no                  | PG0522
departure_airport         | SVX
arrival_airport           | WUH
status                    | Scheduled
airplane_code             | 7M7
scheduled_departure       | 2025-12-03 10:30:00+03
scheduled_departure_local | 2025-12-03 12:30:00
actual_departure          |
actual_departure_local    |
scheduled_arrival         | 2025-12-03 17:30:00+03
scheduled_arrival_local   | 2025-12-03 22:30:00
actual_arrival            |
actual_arrival_local      |

Обратите внимание, что в представлении timetable указано не только локальное (московское) время, но и местное время в аэропортах вылета и прилёта.

Каждое бронирование может включать несколько билетов, по одному на каждого пассажира. Билет, в свою очередь, может включать несколько перелётов. Полная информация о бронировании находится в трёх таблицах: bookings, tickets и segments.

Посмотрим на бронирование с кодом JU35I4:

SELECT * FROM bookings WHERE book_ref = 'JU35I4';
 book_ref |           book_date           | total_amount
----------+-------------------------------+--------------
 JU35I4   | 2025-10-09 06:53:16.710703+03 |     86750.00
(1 строка)

Вот из каких билетов оно состоит:

SELECT *
FROM tickets
WHERE book_ref = 'JU35I4';
   ticket_no   | book_ref |   passenger_id   |  passenger_name   | outbound
---------------+----------+------------------+-------------------+----------
 0005433348362 | JU35I4   | RU 2714075620824 | Nadezhda Sergeeva | t
 0005433348356 | JU35I4   | RU 8692103212506 | Artur Isakov      | t
(2 строки)

Как мы видим, в бронирование входят билеты двух пассажиров. Узнаем, какие перелёты включены в билет Надежды Сергеевой:

SELECT r.route_no,
    dep.airport_code dep_airport, dep.country dep_country, dep.city dep_city,
    arr.airport_code arr_airport, arr.country arr_country, arr.city arr_city
FROM segments s
    JOIN flights f ON f.flight_id = s.flight_id
    JOIN routes r ON r.route_no = f.route_no AND r.validity @> f.scheduled_departure
    JOIN airports dep ON dep.airport_code = r.departure_airport
    JOIN airports arr ON arr.airport_code = r.arrival_airport
WHERE s.ticket_no = '0005433348362'
ORDER BY f.scheduled_departure;
 route_no | dep_airport | dep_country |      dep_city      | arr_airport | arr_country |      arr_city
----------+-------------+-------------+--------------------+-------------+-------------+--------------------
 PG0370   | OVB         | Россия      | Новосибирск        | SVO         | Россия      | Москва
 PG0179   | SVO         | Россия      | Москва             | FRA         | Германия    | Франкфурт-на-Майне
 PG0408   | FRA         | Германия    | Франкфурт-на-Майне | FCO         | Италия      | Рим
 PG0482   | FCO         | Италия      | Рим                | HEL         | Финляндия   | Хельсинки
(3 строки)

Как видим, Наталья летит из Новосибирска в Хельсенки с пересадками в Москвве, Франкфурте-на-Майне и Риме.

Часть перелётов в этом билете уже выполнены на момент, возвращаемый функцией bookings.now, а часть — ещё нет. После регистрации на первый рейс пассажиру выдаются посадочные талоны с указанием мест. В первых двух посадочных талонах Надежды указаны номер и время посадки в самолёт, а на следующие два рейса посадка ещё предстоит:

SELECT f.route_no, f.flight_id, f.status, bp.seat_no, bp.boarding_no, bp.boarding_time
FROM flights f
    JOIN boarding_passes bp ON bp.flight_id = f.flight_id
WHERE bp.ticket_no = '0005433348362'
ORDER BY f.scheduled_departure;
 route_no | flight_id |  status   | seat_no | boarding_no |         boarding_time
----------+-----------+-----------+---------+-------------+-------------------------------
 PG0370   |     10817 | Arrived   | 17B     |          45 | 2025-11-29 18:18:42.972147+03
 PG0179   |     10935 | Arrived   | 16A     |          33 | 2025-11-30 11:54:41.297034+03
 PG0408   |     11108 | On Time   | 24C     |             |
 PG0482   |     11264 | Scheduled | 4B      |             |
(4 строки)

Надеемся, что эти несколько простых примеров помогли составить представление о содержимом демонстрационной базы данных.

Версия от 15.08.2017

Версия от 15.08.2017

Файлы

  • demo-small.zip (21 МБ) — данные по полетам за один месяц (размер БД примерно 300 МБ),
  • demo-medium.zip (62 МБ) — данные по полетам за три месяца (размер БД примерно 700 МБ),
  • demo-big.zip (232 МБ) — данные по полетам за год (размер БД примерно 2,5 ГБ).

Описание

Отличия от предыдущей версии

  • Таблица airports заменена на одноименное представление над таблицей airports_data, в которой поля airport_name и city содержат значения как на русском, так и на английском языках. Представление показывает данные на языке, установленном в конфигурационном параметре bookings.lang (по умолчанию — русский).
  • Аналогично таблица aircrafts заменена на одноименное представление над таблицей aircrafts_data. Переведено поле model.
  • Материализованное представление routes заменено на обычное, чтобы не требовалось обновление при изменении параметра bookings.lang.
  • В представлении airports и таблице airports_data поля longitude и latitude заменены на одно поле coordinates типа point.
  • При подключении к демонстрационной базе данных правильное значение параметра search_path устанавливается автоматически.
Версия от 13.10.2016

Версия от 13.10.2016

Файлы

  • demo-small-20161013.zip (21 МБ) — данные по полетам за один месяц (размер БД примерно 300 МБ),
  • demo-medium-20161013.zip (62 МБ) — данные по полетам за три месяца (размер БД примерно 700 МБ),
  • demo-big-20161013.zip (232 МБ) — данные по полетам за год (размер БД примерно 2,5 ГБ).

Описание

То же самое одним PDF-файлом.