Главная страница

Учебнопрактическое пособие москва 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
страница18 из 28
1   ...   14   15   16   17   18   19   20   21   ...   28
Контрольные вопросы и задания
1. В документации сказано, что служебный символ «%» в шаблоне оператора LIKE
соответствует любой последовательности символов, в том числе и пустой после- довательности, однако ничего не сказано насчет правил обработки пробелов.
В таблице «Билеты» (tickets) столбец passenger_name содержит имя и фамилию пассажира, записанные заглавными латинскими буквами и разделенные одним пробелом.
Выясните правила обработки пробелов самостоятельно, выполнив следующие команды и сравнив полученные результаты:
SELECT count( * ) FROM tickets;
SELECT count( * ) FROM tickets WHERE passenger_name LIKE '% %';
SELECT count( * ) FROM tickets WHERE passenger_name LIKE '% % %';
SELECT count( * ) FROM tickets WHERE passenger_name LIKE '% %%';
2. Этот запрос выбирает из таблицы «Билеты» (tickets) всех пассажиров с именами,
состоящими из трех букв (в шаблоне присутствует три символа «_»):
SELECT passenger_name
FROM tickets
WHERE passenger_name LIKE '___ %';
Предложите шаблон поиска в операторе LIKE для выбора из этой таблицы всех пассажиров с фамилиями, состоящими из пяти букв.
3. В разделе 9.7.2 «Регулярные выражения SIMILAR TO» рассматривается оператор
SIMILAR TO. Он работает аналогично оператору LIKE, но использует шаблоны,
соответствующие определению регулярных выражений, приведенному в стан- дарте SQL. Регулярные выражения SQL представляют собой комбинацию син- таксиса LIKE с синтаксисом обычных регулярных выражений. Самостоятельно ознакомьтесь с оператором SIMILAR TO.
4. В разделе 9.2 «Функция и операторы сравнения» представлены различные пре- дикаты сравнения, кроме предиката BETWEEN, рассмотренного в этой главе.
Самостоятельно ознакомьтесь с ними.
5. В разделе 9.17 «Условные выражения» представлены условные выражения, ко- торые поддерживаются в PostgreSQL. В тексте главы была рассмотрена кон- струкция CASE. Самостоятельно ознакомьтесь с функциями COALESCE, NULLIF,
GREATEST и LEAST.
149

6. Выясните, на каких маршрутах используются самолеты компании Boeing? В
выборке вместо кода модели должно выводиться ее наименование, например,
вместо кода 733 должно быть Boeing 737-300.
Указание: можно воспользоваться соединением представления «Маршруты»
(routes) и таблицы «Самолеты» (aircrafts).
7. Самые крупные самолеты в нашей авиакомпании — это Boeing 777-300. Выяс- нить, между какими парами городов они летают, поможет запрос:
SELECT DISTINCT departure_city, arrival_city
FROM routes r
JOIN aircrafts a ON r.aircraft_code = a.aircraft_code
WHERE a.model = 'Boeing 777-300'
ORDER BY 1;
departure_city | arrival_city
----------------+--------------
Екатеринбург
| Москва
Москва
| Екатеринбург
Москва
| Новосибирск
Москва
| Пермь
Москва
| Сочи
Новосибирск
| Москва
Пермь
| Москва
Сочи
| Москва
(8 строк)
К сожалению, в этой выборке информация дублируется. Пары городов приведе- ны по два раза: для рейса «туда» и для рейса «обратно». Модифицируйте запрос таким образом, чтобы каждая пара городов была выведена только один раз:
departure_city | arrival_city
----------------+--------------
Москва
| Екатеринбург
Новосибирск
| Москва
Пермь
| Москва
Сочи
| Москва
(4 строки)
8. В тексте главы мы рассматривали различные примеры использования левого и правого внешних соединений: LEFT OUTER JOIN и RIGHT OUTER JOIN. Напи- шите запрос, в котором использовалось бы полное внешнее соединение — FULL
OUTER JOIN.
9. Для получения ответа на вопрос, сколько рейсов выполняется из Москвы в
Санкт-Петербург, можно написать совсем простой запрос:
SELECT count( * )
FROM routes
WHERE departure_city = 'Москва'
AND arrival_city
= 'Санкт-Петербург';
150
count
-------
12
(1 строка)
А с помощью какого запроса можно получить результат в таком виде?
departure_city | arrival_city
| count
----------------+-----------------+-------
Москва
| Санкт-Петербург |
12
(1 строка)
10. Выяснить, сколько различных рейсов выполняется из каждого города, без уче- та частоты рейсов в неделю, можно с помощью обращения к представлению
«Маршруты» (routes):
SELECT departure_city, count( * )
FROM routes
GROUP BY departure_city
ORDER BY count DESC;
departure_city
| count
--------------------------+-------
Москва
|
154
Санкт-Петербург
|
35
Новосибирск
|
19
Екатеринбург
|
15
Ростов-на-Дону
|
14
Сочи
|
14
Красноярск
|
13
Ульяновск
|
11
Благовещенск
|
1
Братск
|
1
(101 строка)
Модифицируйте этот запрос так, чтобы он выводил число направлений, по ко- торым летают самолеты из каждого города. Например, из Москвы в Санкт-
Петербург летает несколько различных рейсов, но все эти рейсы относятся к одному направлению.
Указание: нужно передать параметр в функцию count.
11. В материализованном представлении «Маршруты» (routes) есть столбец days_of_week, который содержит списки (массивы) номеров дней недели, когда выполняется каждый рейс.
Для оптимизации расписания вылетов из Москвы нужно выявить пять горо- дов, в которые из столицы отправляется наибольшее число ежедневных рей- сов (маршрутов). Строки в выборке следует расположить в убывающем порядке числа выполняемых рейсов.
Указание. Воспользуйтесь функцией array_length.
151

12.* Предположим, что служба материального снабжения нашей авиакомпании за- просила информацию о числе рейсов, выполняющихся из Москвы в каждый день недели.
Результат можно получить путем выполнения семи аналогичных запросов: по одному для каждого дня недели. Начнем с понедельника:
SELECT 'Понедельник' AS day_of_week, count( * ) AS num_flights
FROM routes
WHERE departure_city = 'Москва'
AND days_of_week @> '{ 1 }'::integer[];
В этом запросе используется оператор «@>», который проверяет, содержатся ли все элементы массива, стоящего справа от него, в том массиве, который нахо- дится слева. В правом массиве всего один элемент — номер интересующего нас дня недели.
day_of_week | num_flights
-------------+-------------
Понедельник |
131
(1 строка)
Запрос для вторника отличается лишь названием дня недели и его номером в массиве.
SELECT 'Вторник' AS day_of_week, count( * ) AS num_flights
FROM routes
WHERE departure_city = 'Москва'
AND days_of_week @> '{ 2 }'::integer[];
day_of_week | num_flights
-------------+-------------
Вторник
|
134
(1 строка)
Нужно выполнить еще пять аналогичных команд, чтобы получить результаты для всех дней недели. Очевидно, что это нерациональный способ.
Получить требуемый результат можно с помощью одного запроса:
SELECT unnest( days_of_week ) AS day_of_week,
count( * ) AS num_flights
FROM routes
WHERE departure_city = 'Москва'
GROUP BY day_of_week
ORDER BY day_of_week;
day_of_week | num_flights
-------------+-------------
1 |
131 2 |
134 3 |
126 4 |
136 5 |
124 6 |
133 7 |
124
(7 строк)
152

Задание 1.
Самостоятельно разберитесь, как работает приведенный запрос.
Выясните, что делает функция unnest. Для того чтобы найти ее описание, можно воспользоваться теми разделами документации, которые были указаны в тек- сте главы 4. Однако можно воспользоваться и предметным указателем (Index),
ссылка на который находится в самом низу оглавления документации.
В качестве вспомогательного запроса, проясняющего работу функции unnest,
можно выполнить следующий:
SELECT flight_no, unnest( days_of_week ) AS day_of_week
FROM routes
WHERE departure_city = 'Москва'
ORDER BY flight_no;
Задание 2.
Использование номеров дней недели в предыдущей выборке не должно вызывать затруднений. Но все-таки предположим, что нас попросили модифицировать запрос, чтобы результат выводился в таком виде:
name_of_day | num_flights
-------------+-------------
Пн.
|
131
Вт.
|
134
Ср.
|
126
Чт.
|
136
Пт.
|
124
Сб.
|
133
Вс.
|
124
(7 строк)
Покажем одно из возможных решений задачи. Оно основано на использова- нии специальной табличной функции unnest в предложении FROM. Подробно об этом написано в документации в разделе 7.2.1.4 «Табличные функции». Функ- ция unnest может принимать любое число параметров-массивов, а возвращает набор строк, которые могут использоваться в запросах, как обычные таблицы.
В этих наборах строк столбцы формируются из значений, содержащихся в мас- сивах.
SELECT dw.name_of_day, count( * ) AS num_flights
FROM (
SELECT unnest( days_of_week ) AS num_of_day
FROM routes
WHERE departure_city = 'Москва'
) AS r,
unnest( '{ 1, 2, 3, 4, 5, 6, 7 }'::integer[],
'{ "Пн.", "Вт.", "Ср.", "Чт.", "Пт.", "Сб.", "Вс."}'::text[]
) AS dw( num_of_day, name_of_day )
WHERE
r.num_of_day = dw.num_of_day
GROUP BY r.num_of_day, dw.name_of_day
ORDER BY r.num_of_day;
Этот запрос можно упростить, воспользовавшись предложением WITH
ORDINALITY. Оно позволяет в нашем примере избавиться от массива це- лых чисел, обозначающих дни недели, поскольку автоматически формируется
153
столбец целых чисел, нумерующих строки результирующего набора. По умол- чанию этот столбец называется ordinality. Это имя можно использовать в запросе.
Самостоятельно модифицируйте запрос с применением предложения WITH
ORDINALITY.
13. Ответить на вопрос о том, каковы максимальные и минимальные цены билетов на все направления, может такой запрос:
SELECT f.departure_city, f.arrival_city,
max( tf.amount ), min( tf.amount )
FROM flights_v f
JOIN ticket_flights tf ON f.flight_id = tf.flight_id
GROUP BY 1, 2
ORDER BY 1, 2;
departure_city
|
arrival_city
|
max
|
min
---------------------+---------------------+-----------+----------
Абакан
| Москва
| 101000.00 | 33700.00
Абакан
| Новосибирск
|
5800.00 | 5800.00
Абакан
| Томск
|
4900.00 | 4900.00
Анадырь
| Москва
| 185300.00 | 61800.00
Анадырь
| Хабаровск
| 92200.00 | 30700.00
Якутск
| Мирный
|
8900.00 | 8100.00
Якутск
| Санкт-Петербург
| 145300.00 | 48400.00
(367 строк)
А как выявить те направления, на которые не было продано ни одного билета?
Один из вариантов решения такой: если на рейсы, отправляющиеся по какому- то направлению, не было продано ни одного билета, то максимальная и мини- мальная цены будут равны NULL. Нужно получить выборку в таком виде:
departure_city
|
arrival_city
|
max
|
min
---------------------+---------------------+-----------+----------
Абакан
| Архангельск
|
|
Абакан
| Грозный
|
|
Абакан
| Кызыл
|
|
Абакан
| Москва
| 101000.00 | 33700.00
Абакан
| Новосибирск
|
5800.00 | 5800.00
Модифицируйте запрос, приведенный выше.
14. Предположим, что маркетологи нашей авиакомпании хотят знать, как часто встречаются различные имена среди пассажиров? Получить распределение ча- стот имен пассажиров в таблице «Билеты» (tickets) поможет такой запрос:
SELECT left( passenger_name, strpos( passenger_name, ' ' ) - 1 )
AS firstname, count( * )
FROM tickets
GROUP BY 1
ORDER BY 2 DESC;
154
firstname | count
------------+-------
ALEKSANDR | 20328
SERGEY
| 15133
VLADIMIR | 12806
TATYANA
| 12058
ELENA
| 11291
OLGA
| 9998
MAGOMED
|
14
ASKAR
|
13
RASUL
|
11
(363 строки)
Напишите запрос для ответа на аналогичный вопрос насчет распределения ча- стот фамилий пассажиров.
Подробные сведения о других функциях для работы со строковыми данными приведены в документации в разделе 9.4 «Строковые функции и операторы».
15.* В тексте главы были кратко рассмотрены оконные функции. Самостоятельно ознакомьтесь с разделами документации, которые рекомендуется изучить для более детального ознакомления с этим классом функций.
Подумайте, в какой ситуации, связанной с базой данных «Авиаперевозки», было бы полезно применить оконные функции, и напишите запрос.
16.* Вместе с агрегатными функциями может использоваться предложение FILTER.
Самостоятельно ознакомьтесь с этой темой, обратившись к разделу документа- ции 4.2.7 «Агрегатные выражения». Напишите запрос с использованием пред- ложения FILTER с агрегатной функцией.
17. В тексте главы в разделе 6.4 мы рассмотрели два способа получения ответа на вопрос: как распределяются места с разными классами обслуживания в самоле- тах всех типов?
А с помощью какого запроса можно получить результат в таком виде:
aircraft_code |
model
| fare_conditions | count
---------------+---------------------+-----------------+-------
319
| Airbus A319-100
| Business
|
20 319
| Airbus A319-100
| Economy
|
96
CR2
| Bombardier CRJ-200 | Economy
|
50
SU9
| Sukhoi SuperJet-100 | Business
|
12
SU9
| Sukhoi SuperJet-100 | Economy
|
85
(17 строк)
18. В разделе 6.2 «Соединения» мы находили ответ на вопрос: сколько маршрутов обслуживают самолеты каждого типа? Но для повышения наглядности получа- емых результатов необходимо еще рассчитывать относительные величины, т. е.
доли от общего числа маршрутов.
Вот что требуется получить:
155
a_code |
model
| r_code | num_routes | fraction
--------+---------------------+--------+------------+----------
CR2
| Bombardier CRJ-200 | CR2
|
232 |
0.327
CN1
| Cessna 208 Caravan | CN1
|
170 |
0.239 773
| Boeing 777-300
| 773
|
10 |
0.014 320
| Airbus A320-200
|
|
0 |
0.000
(9 строк)
19.* В разделе 6.4 «Подзапросы» мы использовали рекурсивный алгоритм в общем табличном выражении. Изучите этот пример, чтобы лучше понять работу ре- курсивного алгоритма:
WITH RECURSIVE ranges ( min_sum, max_sum ) AS
( VALUES( 0,
100000 ),
( 100000, 200000 ),
( 200000, 300000 )
UNION ALL
SELECT min_sum + 100000, max_sum + 100000
FROM ranges
WHERE max_sum <
( SELECT max( total_amount ) FROM bookings )
)
SELECT * FROM ranges;
min_sum | max_sum
---------+---------
0 | 100000
исходные строки
100000 | 200000 200000 | 300000 100000 | 200000
результат первой итерации
200000 | 300000 300000 | 400000 200000 | 300000
результат второй итерации
300000 | 400000 400000 | 500000 300000 | 400000 400000 | 500000 500000 | 600000 1000000 | 1100000
результат (n-3)-й итерации
1100000 | 1200000 1200000 | 1300000 1100000 | 1200000
результат (n-2)-й итерации
1200000 | 1300000 1200000 | 1300000
результат (n-1)-й итерации (предпоследней)
(36 строк)
Здесь мы с помощью предложения VALUES специально создали виртуальную таблицу из трех строк, хотя для получения требуемого результата достаточно
156
только одной строки (0, 100000). Еще важно то, что предложение UNION ALL не удаляет строки-дубликаты, поэтому мы можем видеть весь рекурсивный про- цесс порождения новых строк.
При рекурсивном выполнении запроса
SELECT min_sum + 100000, max_sum + 100000 ...
каждый раз выполняется проверка в условии WHERE. И на (n−2)-й итерации это условие отсеивает одну строку, т. к. после (n − 3)-й итерации значение атрибута max_sum в третьей строке было равно 1 300 000. Ведь запрос
SELECT max( total_amount ) FROM bookings;
выдаст значение max
------------
1204500.00
(1 строка)
Таким образом, после (n − 2)-й итерации во временной области остается всего две строки, после (n − 1)-й итерации во временной области остается только од- на строка. Заключительная итерация уже не добавляет строк в результирующую таблицу, поскольку единственная строка, поданная на вход команде SELECT, бу- дет отклонена условием WHERE. Работа алгоритма завершается.
Задание 1.
Модифицируйте запрос, добавив в него столбец level (можно назвать его и iteration). Этот столбец должен содержать номер текущей итерации, поэто- му нужно увеличивать его значение на единицу на каждом шаге. Не забудьте задать начальное значение для добавленного столбца в предложении VALUES.
Задание 2.
Для завершения экспериментов замените UNION ALL на UNION и выполните запрос. Сравните этот результат с предыдущим, когда мы использо- вали UNION ALL.
20.* В тексте главы есть такой запрос, вычисляющий распределение сумм брониро- ваний по диапазонам в сто тысяч рублей:
WITH RECURSIVE ranges ( min_sum, max_sum ) AS
( VALUES( 0, 100000 )
UNION ALL
SELECT min_sum + 100000, max_sum + 100000
FROM ranges
WHERE max_sum <
( SELECT max( total_amount ) FROM bookings )
)
SELECT r.min_sum, r.max_sum, count( b.* )
FROM bookings b
RIGHT OUTER JOIN ranges r
ON b.total_amount >= r.min_sum
AND b.total_amount < r.max_sum
GROUP BY r.min_sum, r.max_sum
ORDER BY r.min_sum;
157

Как вы думаете, почему функция count получает в качестве параметра выраже- ние «b.*», а не просто «*»? Что изменится, если оставить только «*», и почему?
21. В тексте главы был приведен запрос, выводящий список городов, в которые нет рейсов из Москвы.
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;
Можно предложить другой вариант, в котором используется одна из операций над множествами строк: объединение, пересечение или разность. Вместо зна- ка «?» поставьте в запросе нужное ключевое слово — UNION, INTERSECT или
EXCEPT — и обоснуйте ваше решение.
1   ...   14   15   16   17   18   19   20   21   ...   28


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