Главная страница
Навигация по странице:

  • SELECT airport_name, city, round( latitude::numeric, 2 ) AS ltd, timezone, rank() OVER ( PARTITION BY timezone ORDER BY latitude DESC )

  • SELECT airport_name, city, timezone, latitude, first_value( latitude ) OVER tz AS first_in_timezone

  • WINDOW tz AS ( PARTITION BY timezone ORDER BY latitude DESC ) ORDER BY timezone, rank;

  • SELECT count( * ) FROM bookings WHERE total_amount > ( SELECT avg( total_amount ) FROM bookings );

  • SELECT airport_name, city, longitude FROM airports WHERE longitude IN ( ( SELECT max( longitude ) FROM airports ), ( SELECT min( longitude ) FROM airports )

  • WHERE NOT EXISTS ( SELECT 1 FROM routes r ...

  • AND s.fare_conditions = Comfort ) AS comfort, ( SELECT count( * ) FROM seats s WHERE s.aircraft_code = a.aircraft_code

  • Учебнопрактическое пособие москва 2017 удк 004. 655 Ббк 32. 973. 26018. 2 М79 Моргунов, Е. П


    Скачать 0.9 Mb.
    НазваниеУчебнопрактическое пособие москва 2017 удк 004. 655 Ббк 32. 973. 26018. 2 М79 Моргунов, Е. П
    Дата14.11.2022
    Размер0.9 Mb.
    Формат файлаpdf
    Имя файла4_1_1_SQL_Postgres_Bazovy_kurs_2017.pdf
    ТипУчебно-практическое пособие
    #787182
    страница16 из 28
    1   ...   12   13   14   15   16   17   18   19   ...   28

    PARTITION BY date_trunc( 'month', b.book_date )
    ORDER BY b.book_date
    ) AS count
    FROM ticket_flights tf
    JOIN tickets t ON tf.ticket_no = t.ticket_no
    JOIN bookings b ON t.book_ref = b.book_ref
    WHERE tf.flight_id = 1
    ORDER BY b.book_date;
    Рассмотрим конструкцию, предназначенную для вызова оконной функции:
    count( * ) OVER (
    PARTITION BY date_trunc( 'month', b.book_date )
    ORDER BY b.book_date
    ) AS count
    В этой конструкции обязательным является ключевое слово OVER. Функция count —
    это обычная агрегатная функция, но если вслед за ней идет это ключевое слово, то она становится оконной функцией. Предложение PARTITION BY задает правило раз- биения строк выборки на разделы. Предложение ORDER BY предписывает порядок сортировки строк в разделах.
    Обобщая приведенные объяснения, можно сказать, что раздел включает в себя все строки выборки, имеющие в некотором смысле одинаковые свойства, например,
    одинаковые значения определенных выражений, задаваемых с помощью предложе- ния PARTITION BY. Это могут быть выражения, построенные на основе одного или нескольких столбцов таблицы (или таблиц, участвующих в соединении). Оконный кадр состоит из подмножества строк данного раздела и привязан к текущей стро- ке. Для определения границ кадра важным является наличие предложения ORDER
    BY при формировании раздела. В рассмотренном примере границы оконного кад- ра определялись по умолчанию. Однако для указания этих границ предусмотрены различные способы. Подробно о них сказано в разделе документации 4.2.8 «Вызовы оконных функций».
    Не только функция count, но и другие агрегатные функции (например, sum, avg) то- же могут применяться в качестве оконных функций. Полный перечень собственно оконных функций приведен в документации в разделе 9.21 «Оконные функции».
    Оконные функции, в отличие от обычных агрегатных функций, не требуют группи- ровки строк, а работают на уровне отдельных (несгруппированных) строк. Однако
    132
    если в запросе присутствуют предложения GROUP BY и HAVING, тогда оконные функ- ции вызываются уже после них. В таком случае оконные функции будут работать со строками, являющимися результатом группировки.
    Рассмотрим еще один пример. Покажем, как с помощью оконной функции rank мож- но проранжировать аэропорты в пределах каждого часового пояса на основе их гео- графической широты. Причем будем присваивать более высокий ранг тому аэропор- ту, который находится севернее.
    SELECT airport_name, city,
    round( latitude::numeric, 2 ) AS ltd, timezone,
    rank() OVER (
    PARTITION BY timezone
    ORDER BY latitude DESC
    )
    FROM airports
    WHERE timezone IN ( 'Asia/Irkutsk', 'Asia/Krasnoyarsk' )
    ORDER BY timezone, rank;
    В этом запросе в предложении OVER ( PARTITION BY timezone ... ) указывается,
    что строки относятся к одному разделу на основе совпадения значений в столбце timezone. Обратите внимание, что хотя в предложении OVER задан порядок сорти- ровки, действующий в пределах каждого окна, тем не менее, с помощью предложе- ния ORDER BY указан также и порядок сортировки на уровне всего запроса.
    airport_name |
    city
    | ltd |
    timezone
    | rank
    ---------------+---------------+-------+------------------+------
    Усть-Илимск
    | Усть-Илимск
    | 58.14 | Asia/Irkutsk
    |
    1
    Усть-Кут
    | Усть-Кут
    | 56.85 | Asia/Irkutsk
    |
    2
    Братск
    | Братск
    | 56.37 | Asia/Irkutsk
    |
    3
    Иркутск
    | Иркутск
    | 52.27 | Asia/Irkutsk
    |
    4
    Байкал
    | Улан-Удэ
    | 51.81 | Asia/Irkutsk
    |
    5
    Норильск
    | Норильск
    | 69.31 | Asia/Krasnoyarsk |
    1
    Стрежевой
    | Стрежевой
    | 60.72 | Asia/Krasnoyarsk |
    2
    Богашёво
    | Томск
    | 56.38 | Asia/Krasnoyarsk |
    3
    Емельяново
    | Красноярск
    | 56.18 | Asia/Krasnoyarsk |
    4
    Абакан
    | Абакан
    | 53.74 | Asia/Krasnoyarsk |
    5
    Барнаул
    | Барнаул
    | 53.36 | Asia/Krasnoyarsk |
    6
    Горно-Алтайск | Горно-Алтайск | 51.97 | Asia/Krasnoyarsk |
    7
    Кызыл
    | Кызыл
    | 51.67 | Asia/Krasnoyarsk |
    8
    (13 строк)
    Усложним запрос — для каждого аэропорта будем вычислять разницу между его гео- графической широтой и широтой, на которой находится самый северный аэропорт в этом же часовом поясе. Поскольку в запросе используются три конструкции с окон- ными функциями и при этом способ формирования разделов и порядок сортировки строк в разделах один и тот же, то вводится предложение WINDOW. Оно позволяет создать определение раздела, а затем ссылаться на него при вызове оконных функ- ций. Самый северный аэропорт в каждом часовом поясе, т. е. самая первая строка в каждом разделе, выбирается с помощью оконной функции first_value. Строго говоря,
    эта функция получает доступ к первой строке оконного кадра, а не раздела. Однако когда используются правила формирования оконного кадра по умолчанию, тогда его начало совпадает с началом раздела.
    133

    Обратите внимание, что в этом запросе в каждой конструкции OVER используется ссылка на одно и то же окно, т. е. имеет место один и тот же порядок разбиения на разделы и сортировки строк, поэтому данные будут обработаны за один проход по таблице.
    SELECT
    airport_name,
    city,
    timezone,
    latitude,
    first_value( latitude )
    OVER tz AS first_in_timezone,
    latitude - first_value( latitude ) OVER tz AS delta,
    rank()
    OVER tz
    FROM airports
    WHERE timezone IN ( 'Asia/Irkutsk', 'Asia/Krasnoyarsk' )
    WINDOW tz AS ( PARTITION BY timezone ORDER BY latitude DESC )
    ORDER BY timezone, rank;
    --[ RECORD 4 ]-----+----------------- airport_name
    | Иркутск city
    | Иркутск timezone
    | Asia/Irkutsk latitude
    | 52.268028
    first_in_timezone | 58.135
    delta
    | -5.866972
    rank
    | 4
    --[ RECORD 5 ]-----+----------------- airport_name
    | Байкал city
    | Улан-Удэ timezone
    | Asia/Irkutsk latitude
    | 51.807764
    first_in_timezone | 58.135
    delta
    | -6.327236
    rank
    | 5
    --[ RECORD 6 ]-----+----------------- airport_name
    | Норильск city
    | Норильск timezone
    | Asia/Krasnoyarsk latitude
    | 69.311053
    first_in_timezone | 69.311053
    delta
    | 0
    rank
    | 1
    --[ RECORD 7 ]-----+----------------- airport_name
    | Стрежевой city
    | Стрежевой timezone
    | Asia/Krasnoyarsk latitude
    | 60.716667
    first_in_timezone | 69.311053
    delta
    | -8.594386
    rank
    | 2
    --[ RECORD 8 ]-----+----------------- airport_name
    | Богашёво city
    | Томск
    134
    timezone
    | Asia/Krasnoyarsk latitude
    | 56.380278
    first_in_timezone | 69.311053
    delta
    | -12.930775
    rank
    | 3
    Более подробно использование оконных функций описано в документации. Мы ре- комендуем начать с раздела 3.5 «Оконные функции», в котором приводятся приме- ры их использования. В разделе 9.21 «Оконные функции» приводятся описания всех оконных функций, предлагаемых PostgreSQL. В разделе 4.2.8 «Вызовы оконных функ- ций» детально рассматривается синтаксис вызова оконных функций. В разделе 7.2.5
    «Обработка оконных функций» говорится о том, на каком этапе выполнения запроса производится обработка этих функций.
    6.4 Подзапросы
    Прежде чем приступить к рассмотрению столь сложной темы, как подзапросы, опи- шем, как в общем случае работает команда SELECT. Согласно описанию этой коман- ды, приведенному в документации на PostgreSQL, дело, в несколько упрощенном ви- де, обстоит так.
    1. Сначала вычисляются все элементы, приведенные в списке после ключевого слова FROM. Под такими элементами подразумеваются не только реальные таб- лицы, но также и виртуальные таблицы, создаваемые с помощью ключевого слова VALUES. Если таблиц больше одной, то формируется декартово произве- дение из множеств их строк. Например, в случае двух таблиц будут сформиро- ваны попарные комбинации каждой строки из одной таблицы с каждой строкой из другой таблицы. При этом в комбинированных строках сохраняются все ат- рибуты из каждой исходной таблицы.
    2. Если в команде присутствует условие WHERE, то из полученного декартова про- изведения исключаются строки, которые этому условию не соответствуют. Та- ким образом, первоначальное множество строк, сформированное без всяких условий, сужается.
    3. Если присутствует предложение GROUP BY, то результирующие строки группи- руются на основе совпадения значений одного или нескольких атрибутов, а за- тем вычисляются значения агрегатных функций. Если присутствует предложе- ние HAVING, то оно отфильтровывает результирующие строки (группы), не удо- влетворяющие критерию.
    4. Ключевое слово SELECT присутствует всегда. Но в списке выражений, идущих после него, могут быть не только простые имена атрибутов, но и их комбина- ции, созданные с использованием арифметических и других операций, а также вызовы функций. Причем эти функции могут быть не только встроенные, но и созданные пользователем. В списке выражений не обязаны присутствовать все
    135
    атрибуты, представленные в строках используемых таблиц. Например, атрибу- ты, на основе которых формируются условия в предложении WHERE, могут от- сутствовать в списке выражений после ключевого слова SELECT. Предложение
    SELECT DISTINCT удаляет дубликаты строк.
    5. Если присутствует предложение ORDER BY, то результирующие строки сорти- руются на основе значений одного или нескольких атрибутов. По умолчанию сортировка производится по возрастанию значений.
    6. Если присутствует предложение LIMIT или OFFSET, то возвращается только под- множество строк из выборки.
    Приведенная схема описывает работу команды SELECT на логическом уровне, а на уровне реализации запросов в дело вступает планировщик, который и формирует план выполнения запроса.
    А теперь перейдем непосредственно к теме этого параграфа — подзапросам.
    Предположим, что сотрудникам аналитического отдела потребовалось провести ста- тистическое исследование финансовых результатов работы авиакомпании. В каче- стве первого шага они решили подсчитать количество операций бронирования, в ко- торых общая сумма превышает среднюю величину по всей выборке.
    SELECT count( * ) FROM bookings
    WHERE total_amount >
    ( SELECT avg( total_amount ) FROM bookings );
    count
    -------
    87224
    (1 строка)
    В приведенном запросе присутствует два предложения SELECT, но при этом толь- ко одно из них является главным в этом запросе, а другое представляет собой под-
    запрос
    . Он заключается в круглые скобки и является частью более общего запроса.
    Подзапросы могут присутствовать в предложениях SELECT, FROM, WHERE и HAVING,
    а также в предложении WITH, о котором мы расскажем позднее.
    В приведенном примере в предложении WHERE используется так называемый ска-
    лярный подзапрос
    . Это означает, что в результате его выполнения возвращается только одно скалярное значение (один столбец и одна строка), с которым можно срав- нивать другие скалярные значения.
    Продолжим рассмотрение примеров использования подзапросов в предложении
    WHERE.
    Если подзапрос выдает множество скалярных значений (или даже только одно), то можно использовать такой подзапрос в предикате IN. Этот предикат позволяет ор- ганизовать проверку на предмет принадлежности какого-либо значения определен- ному множеству значений.
    В качестве примера давайте выясним, какие маршруты существуют между городами часового пояса Asia/Krasnoyarsk. Подзапрос будет выдавать список городов из этого часового пояса, а в предложении WHERE главного запроса с помощью предиката IN
    136
    будет выполняться проверка на принадлежность города этому списку. При этом под- запрос выполняется только один раз для всего внешнего запроса, а не при обработ- ке каждой строки из таблицы routes во внешнем запросе. Повторного выполнения подзапроса не требуется, т. к. его результат не зависит от значений, хранящихся в таблице routes. Такие подзапросы называются некоррелированными.
    SELECT flight_no, departure_city, arrival_city
    FROM routes
    WHERE departure_city IN (
    SELECT city
    FROM airports
    WHERE timezone

    'Krasnoyarsk'
    )
    AND arrival_city IN (
    SELECT city
    FROM airports
    WHERE timezone 'Krasnoyarsk'
    );
    flight_no | departure_city | arrival_city
    -----------+----------------+--------------
    PG0070
    | Абакан
    | Томск
    PG0071
    | Томск
    | Абакан
    PG0313
    | Абакан
    | Кызыл
    PG0314
    | Кызыл
    | Абакан
    PG0653
    | Красноярск
    | Барнаул
    PG0654
    | Барнаул
    | Красноярск
    (6 строк)
    Можно сформировать множество значений для предиката IN с помощью скалярных подзапросов. Если мы захотим найти самый западный и самый восточный аэропор- ты и представить полученные сведения в наглядной форме, то запрос может быть таким:
    SELECT airport_name, city, longitude
    FROM airports
    WHERE longitude IN (
    ( SELECT max( longitude ) FROM airports ),
    ( SELECT min( longitude ) FROM airports )
    )
    ORDER BY longitude;
    airport_name |
    city
    | longitude
    --------------+-------------+------------
    Храброво
    | Калининград | 20.592633
    Анадырь
    | Анадырь
    | 177.741483
    (2 строки)
    Конечно, в случае, когда необходимо, наоборот, исключить какие-либо значения из рассмотрения, можно использовать конструкцию NOT IN.
    Иногда возникают ситуации, когда от подзапроса требуется лишь установить сам факт наличия или отсутствия строк в конкретной таблице, удовлетворяющих опре- деленному условию, а непосредственные значения атрибутов в этих строках инте-
    137
    реса не представляют. В подобных случаях используют предикат EXISTS (или NOT
    EXISTS).
    В качестве примера выясним, в какие города нет рейсов из Москвы.
    SELECT DISTINCT a.city
    FROM airports a
    WHERE NOT EXISTS (
    SELECT * FROM routes r
    WHERE r.departure_city = 'Москва'
    AND r.arrival_city = a.city
    )
    AND a.city <> 'Москва'
    ORDER BY city;
    В этом запросе мы не можем ограничиться только лишь материализованным пред- ставлением «Маршруты» (routes), поскольку в нем представлены лишь существующие
    маршруты. Полный список городов можно найти в таблице «Аэропорты» (airports).
    Для каждой строки (каждого города) из таблицы airports выполняется поиск строки в представлении routes, в которой значение атрибута arrival_city такое же, как в те- кущей строке таблицы airports. Если такой строки не найдено, значит, в этот город маршрута из Москвы нет.
    Поскольку от подзапроса в предикате EXISTS требуется только установить факт на- личия или отсутствия строк, соответствующих критерию отбора, то в документации рекомендуется вместо списка столбцов (или символа «*») в предложении SELECT де- лать так:
    WHERE NOT EXISTS ( SELECT 1 FROM routes r ...
    Обратите внимание на ключевое слово DISTINCT в запросе. Оно необходимо, т. к.
    кроме Москвы могут быть другие города, в которых есть более одного аэропорта.
    Один такой город уже существует — Ульяновск. Если не использовать DISTINCT, то, в принципе, возможно появление строк-дубликатов в выборке.
    И еще одна важная деталь. В представленном запросе мы использовали так называ- емый коррелированный (связанный) подзапрос. В подзапросах такого типа при- сутствует ссылка (ссылки) на таблицу из внешнего запроса, как здесь:
    WHERE ...
    AND r.arrival_city = a.city
    В теории это означает, что подзапрос выполняется не один раз для всего внешнего за- проса, а для каждой строки, обрабатываемой во внешнем запросе. Однако на практи- ке важную роль играет умение планировщика (это специальная подсистема в СУБД)
    оптимизировать подобные запросы с тем, чтобы, по возможности, избегать выпол- нения подзапроса для каждой строки из внешнего запроса.
    Получаем такой результат:
    city
    ----------------------
    Благовещенск
    Иваново
    138

    Якутск
    Ярославль
    (20 строк)
    Рассмотрим использование подзапросов в предложениях SELECT, FROM и HAVING.
    Предположим, что для выработки ценовой политики авиакомпании необходимо знать, как распределяются места разных классов в самолетах всех типов. Первый ва- риант решения этой задачи основан на включении подзапросов в предложение
    SELECT
    SELECT a.model,
    ( SELECT count( * )
    FROM seats s
    WHERE s.aircraft_code = a.aircraft_code
    AND s.fare_conditions = 'Business'
    ) AS business,
    ( SELECT count( * )
    FROM seats s
    WHERE s.aircraft_code = a.aircraft_code
    AND s.fare_conditions = 'Comfort'
    ) AS comfort,
    ( SELECT count( * )
    FROM seats s
    WHERE s.aircraft_code = a.aircraft_code
    AND s.fare_conditions = 'Economy'
    ) AS economy
    FROM aircrafts a
    ORDER BY 1;
    Обратите внимание, что в этом запросе мы использовали коррелированные подза- просы. Все они ссылаются на столбец таблицы «Самолеты» (aircrafts), которая обраба- тывается во внешнем запросе. Для каждой обрабатываемой строки таблицы aircrafts подсчитывается число строк в таблице seats, в которых атрибут aircraft_code имеет такое же значение, что и в строке таблицы aircrafts. Подзапросы отличаются друг от друга только условием fare_conditions. Поскольку все эти подзапросы не зависят друг от друга, то, хотя все они обращаются к таблице «Места» (seats), не требуется исполь- зовать для нее различные псевдонимы в этих подзапросах.
    model
    | business | comfort | economy
    ---------------------+----------+---------+---------
    Airbus A319-100
    |
    20 |
    0 |
    96
    Airbus A320-200
    |
    20 |
    0 |
    120
    Airbus A321-200
    |
    28 |
    0 |
    142
    Boeing 737-300
    |
    12 |
    0 |
    118
    Boeing 767-300
    |
    30 |
    0 |
    192
    Boeing 777-300
    |
    30 |
    48 |
    324
    Bombardier CRJ-200 |
    0 |
    0 |
    50
    Cessna 208 Caravan |
    0 |
    0 |
    12
    Sukhoi SuperJet-100 |
    12 |
    0 |
    85
    (9 строк)
    А в этом варианте решения задачи используется
    1   ...   12   13   14   15   16   17   18   19   ...   28


    написать администратору сайта