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

Учебнопрактическое пособие москва 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
страница20 из 28
1   ...   16   17   18   19   20   21   22   23   ...   28

PARTITION BY left( model, 6 )
ORDER BY range
) AS rank
176

FROM aircrafts_tmp
WHERE model

'^Airbus' OR model '^Boeing'
)
DELETE FROM aircrafts_tmp a
USING min_ranges mr
WHERE a.aircraft_code = mr.aircraft_code
AND mr.rank = 1
RETURNING *;
Мы включили в команду DELETE предложение RETURNING *, чтобы показать, как вы- глядят комбинированные строки, сформированные с помощью предложения USING.
Конечно, удаляются не комбинированные строки, а только оригинальные строки из таблицы aircrafts_tmp.
aircraft_code |
model
| range | aircraft_code | rank
---------------+-----------------+-------+---------------+------
321
| Airbus A321-200 | 5600 | 321
|
1 733
| Boeing 737-300 | 4200 | 733
|
1
(2 строки)
В заключение этого раздела упомянем еще команду TRUNCATE, которая позволяет быстро удалить все строки из таблицы. Следующие две команды позволяют удалить все строки из таблицы aircrafts_tmp:
DELETE FROM aircrafts_tmp;
TRUNCATE aircrafts_tmp;
Однако команда TRUNCATE работает быстрее.
Контрольные вопросы и задания
1. Добавьте в определение таблицы aircrafts_log значение по умолчанию
CURRENT_TIMESTAMP и соответствующим образом измените команды INSERT,
приведенные в тексте главы.
2. В предложении RETURNING можно указывать не только символ «*», означаю- щий выбор всех столбцов таблицы, но также и более сложные выражения, сфор- мированные на основе этих строк. В тексте главы мы копировали содержимое таблицы «Самолеты» (aircrafts) в таблицу aircrafts_tmp. В том запросе в пред- ложении RETURNING мы использовали именно «*». Однако возможен и другой вариант запроса:
WITH add_row AS
( INSERT INTO aircrafts_tmp
SELECT * FROM aircrafts
RETURNING aircraft_code, model, range,
CURRENT_TIMESTAMP, 'INSERT'
)
INSERT INTO aircrafts_log
SELECT ? FROM add_row;
Что нужно написать в этом запросе вместо вопросительного знака?
177

3. Если бы мы для копирования данных из таблицы «Самолеты» (aircrafts) в таб- лицу aircrafts_tmp использовали команду INSERT без общего табличного выра- жения
INSERT INTO aircrafts_tmp SELECT * FROM aircrafts;
то в качестве выходного результата мы увидели бы сообщение
INSERT 0 9
Как вы думаете, что будет выведено, если дополнить команду предложением
RETURNING * ?
INSERT INTO aircrafts_tmp SELECT * FROM aircrafts RETURNING *;
Проверьте ваши предположения на практике. Подумайте, каким образом мож- но использовать выведенный результат?
4. В тексте главы в предложениях ON CONFLICT команды INSERT мы использовали только выражения, состоящие из имени одного столбца. Однако в таблице «Ме- ста» (seats) первичный ключ является составным и включает два столбца. Напи- шите команду INSERT для вставки новой строки в эту таблицу и предусмотрите возможный конфликт добавляемой строки со строкой, уже имеющейся в таб- лице. Сделайте два варианта предложения ON CONFLICT: первый — с исполь- зованием перечисления имен столбцов для проверки наличия дублирования,
второй — с использованием предложения ON CONSTRAINT.
Для того чтобы не изменить содержимое таблицы «Места», создайте ее копию и выполняйте все эти эксперименты с таблицей-копией.
5. В предложении DO UPDATE команды INSERT может использоваться условие
WHERE. Самостоятельно ознакомьтесь с этой возможностью с помощью доку- ментации и напишите такую команду INSERT.
6. Команда COPY по умолчанию ожидает получения вводимых данных в формате text, когда значения данных разделяются символами табуляции. Однако мож- но представлять входные данные в формате CSV (Comma Separated Values), т. е.
использовать в качестве разделителя запятую.
COPY aircrafts_tmp FROM STDIN WITH ( FORMAT csv );
Вводите данные для копирования, разделяя строки переводом строки.
Закончите ввод строкой '\.'.
>> IL9, Ilyushin IL96, 9800
>> I93, Ilyushin IL96-300, 9800
>> \.
SELECT * FROM aircrafts_tmp;
aircraft_code |
model
| range
---------------+---------------------+-------
CN1
| Cessna 208 Caravan | 1200
CR2
| Bombardier CRJ-200 | 2700
IL9
| Ilyushin IL96
| 9800
I93
| Ilyushin IL96-300 | 9800
(11 строк)
178

Как вы думаете, почему при выводе данных из таблицы вновь введенные зна- чения в столбце model оказались смещены вправо?
7. Команда COPY позволяет получить входные данные из файла и поместить их в таблицу. Этот файл должен быть доступен тому пользователю операцион- ной системы, от имени которого запущен серверный процесс, как правило, это пользователь postgres.
Подготовьте файл, например, /home/postgres/aircrafts_tmp.csv, имеющий такую структуру:
– каждая строка файла соответствует одной строке таблицы aircrafts_tmp;
– значения данных в строке файла разделяются запятыми.
Например:
773,Boeing 777-300,11100 763,Boeing 767-300,7900
SU9,Sukhoi SuperJet-100,3000
Введите в этот файл данные о нескольких самолетах, причем часть из них уже должна быть представлена в таблице, а часть — нет.
Поскольку при выполнении команды COPY выполняются проверки всех огра- ничений целостности, наложенных на таблицу, то дублирующие строки добав- лены, конечно же, не будут. А как вы думаете, строки, содержащиеся в этом же файле, но отсутствующие в таблице, будут добавлены или нет? Проверьте свою гипотезу, выполнив команду для вставки строк в таблицу из этого файла:
COPY aircrafts_tmp
FROM '/home/postgres/aircrafts_tmp.csv'
WITH ( FORMAT csv );
8.* В тексте главы был приведен запрос, предназначенный для учета числа биле- тов, проданных по всем направлениям на текущую дату. Однако тот запрос был рассчитан на одновременное добавление только одной записи в таблицу «Пе- релеты» (ticket_flights_tmp). Ниже мы предложим более универсальный запрос,
который предусматривает возможность единовременного ввода нескольких за- писей о перелетах, выполняемых на различных рейсах.
Для проверки работоспособности предлагаемого запроса выберем несколько рейсов по маршрутам: Красноярск — Москва, Москва — Сочи, Сочи — Москва,
Сочи — Красноярск. Для определения идентификаторов рейсов сформируем вспомогательный запрос, в котором даты начала и конца рассматриваемого пе- риода времени зададим с помощью функции bookings.now. Использование этой функции необходимо, поскольку в будущих версиях базы данных могут быть представлены другие диапазоны дат.
SELECT flight_no, flight_id, departure_city, arrival_city,
scheduled_departure
FROM flights_v
WHERE scheduled_departure
BETWEEN bookings.now() AND bookings.now() + INTERVAL '15 days'
AND ( departure_city, arrival_city ) IN
( ( 'Красноярск', 'Москва' ), ( 'Москва', 'Сочи'),
179

( 'Сочи', 'Москва' ), ( 'Сочи', 'Красноярск' )
)
ORDER BY departure_city, arrival_city, scheduled_departure;
Обратите внимание на предикат IN: в нем используются не индивидуальные значения, а пары значений.
Предположим, что в течение указанного интервала времени пассажир плани- рует совершить перелеты по маршруту: Красноярск — Москва, Москва — Сочи,
Сочи — Москва, Москва — Сочи, Сочи — Красноярск. Выполнив вспомогатель- ный запрос, выберем следующие идентификаторы рейсов (в этом же порядке):
13829, 4728, 30523, 7757, 30829.
WITH sell_tickets AS
( INSERT INTO ticket_flights_tmp
( ticket_no, flight_id, fare_conditions, amount )
VALUES ( '1234567890123', 13829, 'Economy', 10500 ),
( '1234567890123', 4728, 'Economy', 3400 ),
( '1234567890123', 30523, 'Economy', 3400 ),
( '1234567890123', 7757, 'Economy', 3400 ),
( '1234567890123', 30829, 'Economy', 12800 )
RETURNING *
)
UPDATE tickets_directions td
SET last_ticket_time = CURRENT_TIMESTAMP,
tickets_num = tickets_num +
( SELECT count( * )
FROM sell_tickets st, flights_v f
WHERE st.flight_id = f.flight_id
AND f.departure_city = td.departure_city
AND f.arrival_city
= td.arrival_city
)
WHERE ( td.departure_city, td.arrival_city ) IN (
SELECT departure_city, arrival_city
FROM flights_v
WHERE flight_id IN (
SELECT flight_id
FROM sell_tickets
)
);
UPDATE 4
В этой версии запроса предусмотрен единовременный ввод нескольких строк в таблицу ticket_flights_tmp, причем, перелеты могут выполняться на различных рейсах. Поэтому необходимо каким-то образом преобразовать список иден- тификаторов этих рейсов в множество пар «город отправления — город при- бытия», поскольку именно для таких пар и ведется подсчет числа заброниро- ванных перелетов. Эта задача решается в предложении WHERE. Здесь вложен- ный подзапрос формирует список идентификаторов рейсов, а внешний под- запрос преобразует этот список в множество пар «город отправления — город прибытия». Затем с помощью предиката IN производится отбор строк таблицы tickets_directions для обновления.
180

Теперь обратимся к предложению SET. Подзапрос с функцией count вычисляет количество перелетов по каждому направлению. Это коррелированный подза- прос: он выполняется для каждой строки, отобранной в предложении WHERE. В
нем используется соединение временной таблицы sell_tickets с представлени- ем flights_v. Это нужно для того, чтобы подсчитать все перелеты, соответству- ющие паре атрибутов «город отправления — город прибытия», взятых из теку- щей обновляемой строки таблицы tickets_directions. Этот подзапрос позволя- ет учесть такой факт: рейсы могут иметь различные идентификаторы flight_id,
но при этом соответствовать одному и тому же направлению, а в таблице tickets_directions учитываются именно направления.
В случае попытки повторного бронирования одного и того же перелета для дан- ного пассажира, т. е. ввода строки с дубликатом первичного ключа, такая строка будет отвергнута, и будет сгенерировано сообщение об ошибке. В таком случае и таблица tickets_directions не будет обновлена.
Давайте посмотрим, что изменилось в таблице tickets_directions.
SELECT departure_city AS dep_city, arrival_city AS arr_city,
last_ticket_time, tickets_num AS num
FROM tickets_directions
WHERE tickets_num > 0
ORDER BY departure_city, arrival_city;
По маршруту Москва — Сочи наш пассажир приобретал два билета, что и отра- жено в выборке.
dep_city | arr_city |
last_ticket_time
| num
------------+------------+----------------------------+-----
Красноярск | Москва
| 2017-02-04 14:02:23.769443 |
1
Москва
| Сочи
| 2017-02-04 14:02:23.769443 |
2
Сочи
| Красноярск | 2017-02-04 14:02:23.769443 |
1
Сочи
| Москва
| 2017-02-04 14:02:23.769443 |
1
(4 строки)
А это информация о каждом перелете, забронированном нашим пассажиром:
SELECT * FROM ticket_flights_tmp;
ticket_no
| flight_id | fare_conditions | amount
---------------+-----------+-----------------+----------
1234567890123 |
13829 | Economy
| 10500.00 1234567890123 |
4728 | Economy
| 3400.00 1234567890123 |
30523 | Economy
| 3400.00 1234567890123 |
7757 | Economy
| 3400.00 1234567890123 |
30829 | Economy
| 12800.00
(5 строк)
Задание: модифицируйте запрос и таблицу tickets_directions так, чтобы учет числа забронированных перелетов по различным маршрутам выполнялся для каждого класса обслуживания: Economy, Business и Comfort.
181

9.* Предположим, что руководство нашей авиакомпании решило отказаться от ис- пользования самолетов компаний Boeing и Airbus, имеющих наименьшее ко- личество пассажирских мест в салонах. Мы должны соответствующим образом откорректировать таблицу «Самолеты» (aircrafts_tmp).
Мы предлагаем такой алгоритм.
Шаг 1. Для каждой модели вычислить общее число мест в салоне.
Шаг 2. Используя оконную функцию rank, присвоить моделям ранги на основе числа мест (упорядочив их по возрастанию числа мест). Ранжирование выпол- няется в пределах каждой компании-производителя, т. е. для Boeing и для Airbus —
отдельно. Ранг, равный 1, соответствует наименьшему числу мест.
Шаг 3. Выполнить удаление тех строк из таблицы aircrafts_tmp, которые удовле- творяют следующим требованиям: модель — Boeing или Airbus, а число мест в салоне — минимальное из всех моделей данной компании-производителя, т. е.
модель имеет ранг, равный 1.
WITH aicrafts_seats AS
( SELECT aircraft_code, model, seats_num,
rank() OVER (
PARTITION BY left( model, strpos( model, ' ' ) - 1 )
ORDER BY seats_num
)
FROM (
SELECT a.aircraft_code, a.model, count( * ) AS seats_num
FROM aircrafts_tmp a, seats s
WHERE a.aircraft_code = s.aircraft_code
GROUP BY 1, 2
) AS seats_numbers
)
DELETE FROM aircrafts_tmp a
USING aicrafts_seats a_s
WHERE a.aircraft_code = a_s.aircraft_code
AND left( a.model, strpos( a.model, ' ' ) - 1 )
IN ( 'Boeing', 'Airbus' )
AND a_s.rank = 1
RETURNING *;
Шаг 1 выполняется в подзапросе в предложении WITH. Шаг 2 выполняется в главном запросе в предложении WITH. Шаг 3 реализуется командой DELETE.
Обратите внимание, что название компании-производителя мы определяем путем взятия подстроки от значения атрибута model: от начала строки до про- бельного символа (используем функции left и strpos).
Мы включили предложение RETURNING * для того, чтобы увидеть, какие имен- но модели были удалены.
Предложение WITH выдает такой результат:
aircraft_code |
model
| seats_num | rank
---------------+---------------------+-----------+------
319
| Airbus A319-100
|
116 |
1 320
| Airbus A320-200
|
140 |
2 182

321
| Airbus A321-200
|
170 |
3 733
| Boeing 737-300
|
130 |
1 763
| Boeing 767-300
|
222 |
2 773
| Boeing 777-300
|
402 |
3
CR2
| Bombardier CRJ-200 |
50 |
1
CN1
| Cessna 208 Caravan |
12 |
1
SU9
| Sukhoi SuperJet-100 |
97 |
1
(9 строк)
Очевидно, что должны быть удалены модели с кодами 319 и 733.
После выполнения запроса получим такое сообщение (это работает предложе- ние RETURNING *):
--[ RECORD 1 ]-+---------------- aircraft_code | 319
model
| Airbus A319-100
range
| 6700
aircraft_code | 319
model
| Airbus A319-100
seats_num
| 116
rank
| 1
--[ RECORD 2 ]-+---------------- aircraft_code | 733
model
| Boeing 737-300
range
| 4200
aircraft_code | 733
model
| Boeing 737-300
seats_num
| 130
rank
| 1
DELETE 2
Обратите внимание, что были выведены комбинированные строки, получен- ные при соединении таблицы aircrafts_tmp с временной таблицей aicrafts_seats,
указанной в предложении USING. Но удалены были, конечно, строки из таблицы aircrafts_tmp.
Задание: предложите другой вариант решения этой задачи. Например, можно поступить так: оставить предложение WITH без изменений, из команды DELETE
убрать предложение USING, а в предложении WHERE вместо соединения таблиц использовать подзапрос с предикатом IN для получения списка кодов удаляе- мых моделей самолетов.
Еще один вариант решения задачи связан с использованием представлений, ко- торые мы рассматривали в главе 5. В данном случае можно создать представле- ние на основе таблиц «Самолеты» (aircrafts) и «Места» (seats) и перенести кон- струкцию с функциями left и strpos в представление. В нем будут вычисляемые столбцы: company — «Компания — производитель самолетов» и seats_num —
«Число мест».
CREATE VIEW aircrafts_seats AS
( SELECT a.aircraft_code, a.model,
left( a.model, strpos( a.model, ' ' ) - 1 ) AS company,
count( * ) AS seats_num
183

FROM aircrafts a, seats s
WHERE a.aircraft_code = s.aircraft_code
GROUP BY 1, 2, 3
);
Имея это представление, можно использовать его в конструкции WITH. При этом вызов функции rank может упроститься:
rank() OVER ( PARTITION BY company ORDER BY seats_num )
Для выбора удаляемых строк в команде DELETE можно использовать, например,
подзапрос в предикате IN. При этом не забывайте, что значение столбца rank для них будет равно 1.
Еще одна идея: для выбора минимальных значений числа мест в самолетах можно попытаться в качестве замены оконной функции rank использовать предложения LIMIT 1 и ORDER BY. В таком случае не потребуется также и функ- ция min.
10.* В реальной работе иногда возникают ситуации, когда требуется быстро запол- нить таблицу тестовыми данными. В таком случае можно воспользоваться ко- мандой INSERT с подзапросом. Конечно, число атрибутов и их типы данных в подзапросе SELECT должны быть такими, какие ожидает получить команда
INSERT.
Продемонстрируем такой прием на примере таблицы «Места» (seats). Для того чтобы выполнить команду, приведенную в этом упражнении, нужно либо сна- чала удалить все строки из таблицы seats, чтобы можно было добавлять строки в эту таблицу
DELETE FROM seats;
либо создать копию этой таблицы
CREATE TABLE seats_tmp AS SELECT * FROM seats;
чтобы работать с копией.
Итак, как сформировать тестовые данные автоматическим способом? Для этого сначала нужно подготовить исходные данные, на основе которых и будут фор- мироваться результирующие значения для вставки в таблицу «Места». В рам- ках реляционной модели наиболее естественным будет представление исход- ных данных в виде таблиц. Для формирования каждой строки таблицы «Места»
нужно задать код модели самолета, класс обслуживания и номер места, кото- рый состоит из двух компонентов: номера ряда и буквенного идентификатора позиции в ряду. Поскольку размеры и компоновки салонов различаются, необ- ходимо для каждой модели указать предельное число рядов кресел в салонах бизнес-класса и экономического класса, а также число кресел в каждом ряду.
Это число можно задать с помощью указания буквенного идентификатора для самого последнего кресла в ряду. Например, если в ряду всего шесть кресел, то- гда их буквенные обозначения будут такими: A, B, C, D, E, F. Таким образом,
последней будет буква F. В салоне бизнес-класса число мест в ряду меньше, чем в салоне экономического класса, но для упрощения задачи примем эти числа одинаковыми. В результате получим первую исходную таблицу с атрибутами:
184

– код модели самолета;
– номер последнего ряда кресел в салоне бизнес-класса;
– номер последнего ряда кресел в салоне экономического класса;
– буква, обозначающая позицию последнего кресла в ряду.
Классы обслуживания также поместим в отдельную таблицу. В ней будет всего один атрибут — класс обслуживания.
Список номеров рядов также поместим в отдельную таблицу. В ней будет также всего один атрибут — номер ряда.
Также поступим и с буквенными обозначениями кресел в ряду. В этой таблице будет один атрибут — латинская буква, обозначающая позицию кресла.
В принципе можно было бы создать все четыре таблицы с помощью команды
CREATE TABLE и ввести в них исходные данные, а затем использовать эти табли- цы в команде SELECT. Но команда SELECT позволяет использовать в предложе- нии FROM виртуальные таблицы, которые можно создавать с помощью предло- жения VALUES. Для этого непосредственно в текст команды записываются груп- пы значений, представляющие собой строки такой виртуальной таблицы. Каж- дая такая строка заключается в круглые скобки. Вся эта конструкция получает имя таблицы и к ней прилагается список атрибутов. Это выглядит, например,
так:
1   ...   16   17   18   19   20   21   22   23   ...   28


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