Настройка среды и определение структуры базы данных
Перед началом работы необходимо установить PostgreSQL Server. Для подключения к серверу используется интерактивная консоль psql.
Визуальные редакторы для работы с PostgreSQL
Помимо psql существуют графические инструменты, которые упрощают навигацию по схеме, написание запросов и просмотр результатов. Ниже описаны три наиболее распространённых.
pgAdmin — официальный графический клиент PostgreSQL. Работает как веб-приложение в браузере. Включает визуальный редактор запросов с подсветкой синтаксиса, дерево объектов базы данных, конструктор таблиц и средства мониторинга сервера. Поставляется вместе с установщиком PostgreSQL на Windows, поэтому чаще всего уже доступен сразу после установки сервера. Подходит для учебных задач и администрирования, но интерфейс может казаться перегруженным из-за большого количества настроек.
DBeaver — универсальный клиент для работы с различными СУБД (PostgreSQL, MySQL, SQLite, Oracle и др.). Построен на базе Eclipse, доступен в бесплатной Community-версии. Предоставляет удобный редактор SQL с автодополнением, визуальное представление структуры таблиц, ER-диаграммы, экспорт и импорт данных. Хорошо подходит, если в учебном процессе или на практике приходится работать с несколькими СУБД одновременно.
TablePlus — легковесный клиент с минималистичным интерфейсом. Доступен для macOS, Windows и Linux. Отличается быстрым запуском и простой навигацией: подключение к серверу, просмотр и редактирование данных прямо в табличном виде, выполнение SQL-запросов. Бесплатная версия имеет ограничение на количество одновременно открытых вкладок, но для учебных задач этого достаточно.
Для выполнения лабораторных работ можно использовать любой из перечисленных инструментов или работать напрямую в psql. Все SQL-команды, приведённые в тексте, одинаково работают в любом клиенте.
psql -U postgresЕсли вы работаете в Windows и видите некорректную кириллицу, это не проблема SQL-команд и не ошибка PostgreSQL. Обычно достаточно настроить шрифт терминала и кодировку консоли. В классическом варианте перед запуском psql переключают кодовую страницу.
chcp 1251Проверьте справку утилиты psql, чтобы сразу видеть ключи запуска и режимы работы.
psql --helpЗадание
- Подключитесь к серверу командой
psql -U postgres. - Если работаете в Windows, при необходимости выставьте кодировку и перепроверьте отображение кириллицы.
- Откройте
psql --helpи зафиксируйте в отчете, какие ключи вам понадобятся дальше (-d,-f,-U).
Работа в psql
В интерактивной сессии psql сразу важно разделить два типа команд. Первый тип - это SQL, то есть CREATE TABLE, SELECT, ALTER TABLE и другие операторы, которые отправляются серверу. Второй тип - сервисные команды psql, начинающиеся с \; они не изменяют язык SQL, но дают быстрый доступ к метаданным, справке и настройке сессии.
Сначала проверьте встроенную справочную систему psql, чтобы уточнять синтаксис прямо в терминале.
\?Команда \? выводит список сервисных команд самой утилиты psql. Для получения справки непосредственно по операторам языка SQL предназначена отдельная команда — \h, которая показывает перечень всех доступных SQL-команд и их краткое описание.
\hДля вывода полного синтаксиса конкретного оператора следует передать его имя аргументом. Ниже пример для CREATE TABLE.
\h CREATE TABLEПроверьте, какие таблицы видит текущая база.
\dtКогда нужно посмотреть структуру конкретной таблицы, работайте через \d. Вы получите список столбцов, типы данных, ограничения и индексы, которые PostgreSQL создал автоматически.
\d aircraftsВ ходе работы нередко требуется переключиться на другую базу данных, не завершая текущую сессию. Для этого предназначена команда \c, после которой указывается имя базы.
\c demoПри необходимости сохраните историю команд в файл.
\s history_lab3.txtЗадание
- В активной сессии выполните
\?,\h,\h CREATE TABLE. - Запустите
\dtи убедитесь, что команда отрабатывает без ошибок. - Проверьте
\dдля любой существующей таблицы. - Зафиксируйте в отчете, какие из сервисных команд оказались наиболее полезными.
Развертывание учебной базы
В лабораторной используется учебная база demo в нескольких вариантах по объему данных. В рамках выполнения лабораторных работ будет достаточно demo_small; устанавливать более крупный вариант имеет смысл для сравнения эффективности выполнения SQL-запросов.
Перед запуском SQL-скрипта распакуйте архив с учебными данными.
unzip demo_small.zipДалее запускается сам SQL-скрипт. Он создает структуру базы и заполняет ее данными, поэтому команда может выполняться заметно дольше обычных интерактивных запросов.
psql -f demo_small.sql -U postgresПосле развёртывания подключитесь к базе demo и убедитесь, что сессия открыта в нужном контексте.
psql -d demo -U postgresДля детальной диагностики разделяйте обычный вывод и поток ошибок.
psql -f demo_small.sql -U postgres > demo.log 2>demo.errЕсли достаточно одного общего файла, объедините стандартный поток и поток ошибок.
psql -f demo_small.sql -U postgres > demo.log 2>&1Для длинных скриптов запускайте загрузку в фоне и отслеживайте лог в реальном времени.
psql -f demo_small.sql -U postgres > demo.log 2>&1 &
tail -f demo.logПосле завершения загрузки проверьте, что таблицы созданы.
\dtОткройте описание одной из таблиц схемы bookings, чтобы убедиться, что структура создана корректно. Таблица flights подходит для первой проверки: в ней есть и первичный ключ, и внешние ключи, и ограничения CHECK.
\d flightsЗадание
- Разверните
demo_small.sqlодним из удобных вам способов. - Подключитесь к базе
demo. - Проверьте список таблиц и структуру минимум одной таблицы.
- Добавьте в отчет короткий комментарий: какой способ запуска скрипта вы выбрали и почему.
Базовая практика SQL
Практика строится вокруг полного цикла работы с таблицей: создание структуры, добавление данных, чтение, обновление и удаление. Для этого сначала используется небольшая таблица aircrafts, чтобы каждый шаг можно было проверить вручную.
Создание таблицы aircrafts
Сначала фиксируем структуру таблицы: код самолета, модель и дальность полета. Здесь задаются типы данных и ограничения целостности.
Прим.: синтаксис
CREATE TABLECREATE TABLEсоздает новую таблицу и описывает ее столбцы, типы, ограничения и ключи. Для читаемости обычно ключевые слова пишут в верхнем регистре, а типы данных - в нижнем.
CREATE TABLE aircrafts
(
aircraft_code char(3) NOT NULL,
model text NOT NULL,
range integer NOT NULL,
CHECK (range > 0),
PRIMARY KEY (aircraft_code)
);Сразу после создания таблицы откройте ее описание в psql и проверьте ограничения.
\d aircraftsДобавление данных через INSERT
После создания структуры заполните таблицу строками. В INSERT сразу указывайте список столбцов.
Прим.: синтаксис
INSERT INTOINSERT INTOдобавляет строки в таблицу. Один оператор может вставлять как одну строку, так и набор строк через списокVALUES.
INSERT INTO aircrafts (aircraft_code, model, range)
VALUES ('SU9', 'Sukhoi SuperJet-100', 3000);Для однотипных записей используйте пакетную вставку несколькими кортежами.
INSERT INTO aircrafts (aircraft_code, model, range)
VALUES
('773', 'Boeing 777-300', 11100),
('763', 'Boeing 767-300', 7900),
('733', 'Boeing 737-300', 4200),
('320', 'Airbus A320-200', 5700),
('321', 'Airbus A321-200', 5600),
('319', 'Airbus A319-100', 6700),
('CN1', 'Cessna 208 Caravan', 1200),
('CR2', 'Bombardier CRJ-200', 2700);Чтение данных через SELECT
Чтобы убедиться в том, что строки добавились корректно, выполним SELECT. Команда SELECT — основное средство чтения данных в реляционных базах; с ее помощью можно извлечь как все столбцы таблицы, так и только нужные.
Прим.: синтаксис
SELECT ... FROMSELECTвыполняет чтение данных.*возвращает все столбцы, но в рабочих запросах принято указывать явный список полей: если схема таблицы изменится (добавится столбец), запрос с*вернёт лишние данные, что может нарушить логику приложения.
SELECT *
FROM aircrafts;По умолчанию порядок строк в результирующем наборе не определён и может меняться от запроса к запросу. Чтобы получить предсказуемую выдачу, к запросу добавляют ORDER BY.
Прим.: синтаксис
ORDER BYORDER BYзадает порядок строк в результирующем наборе. По умолчанию сортировка идет по возрастанию; для убывания используютDESC.
SELECT model, aircraft_code, range AS max_range_km
FROM aircrafts
ORDER BY model;На практике редко нужны все строки таблицы сразу — гораздо чаще требуется отобрать только те из них, которые соответствуют некоторому условию. Для этого служит предложение WHERE, которое записывается после имени таблицы и фильтрует строки до формирования результата.
Прим.: синтаксис
WHEREиBETWEENWHEREограничивает выборку по условию.BETWEEN a AND bвключает обе границы диапазона — это эквивалентно>= a AND <= b. Данный факт важен при работе с временными диапазонами:BETWEEN '2024-01-01' AND '2024-01-31'включит в результат и 31 января.
SELECT model, aircraft_code, range
FROM aircrafts
WHERE range BETWEEN 4000 AND 6000;Обновление данных через UPDATE
Команда UPDATE меняет уже существующие строки. Главный риск здесь связан не с синтаксисом, а с условием отбора: если пропустить WHERE, будут изменены все строки таблицы. Поэтому перед обновлением желательно выполнить такой же SELECT и убедиться, что условие затрагивает ровно нужные записи.
Прим.: синтаксис
UPDATE ... SET ... WHEREUPDATEобновляет значения столбцов. СообщениеUPDATE nпоказывает, сколько строк реально изменено.
UPDATE aircrafts
SET range = 3500
WHERE aircraft_code = 'SU9';После UPDATE сразу проверьте измененную строку точечным запросом.
SELECT *
FROM aircrafts
WHERE aircraft_code = 'SU9';Если нужно изменить значение по формуле, UPDATE поддерживает вычисления прямо в выражении SET. Это часто используется для индексаций, пересчетов и пакетных корректировок.
UPDATE aircrafts
SET range = range * 2
WHERE aircraft_code = 'SU9';Удаление данных через DELETE
DELETE использует ту же логику условия, что и SELECT. Перед удалением сначала проверьте набор строк тем же условием.
Прим.: синтаксис
DELETE FROM ... WHEREDELETEудаляет строки, удовлетворяющие условию. БезWHEREкоманда удаляет все строки таблицы.
DELETE FROM aircrafts
WHERE aircraft_code = 'CN1';Перед массовым удалением используйте двухшаговую схему: сначала SELECT, затем DELETE с тем же условием.
SELECT *
FROM aircrafts
WHERE range > 10000 OR range < 3000;
DELETE FROM aircrafts
WHERE range > 10000 OR range < 3000;Полная очистка таблицы допустима только для временных данных.
DELETE FROM aircrafts;Прим. Для полной очистки таблицы в PostgreSQL существует более быстрая альтернатива — команда
TRUNCATE. В отличие отDELETE, она не сканирует строки поочерёдно, а освобождает страницы данных напрямую. ОднакоTRUNCATEнельзя ограничить условиемWHERE, и она ведёт себя иначе при наличии внешних ключей.
Прим. Сообщения
UPDATE 0иDELETE 0не считаются ошибкой SQL. Они показывают, что команда синтаксически корректна, но условие не нашло подходящих строк.
Задание
- Создайте таблицу
aircraftsс ограничениями из примера. - Добавьте строки через одиночный и пакетный
INSERT. - Выполните
SELECTс сортировкой и фильтрацией. - Проверьте
UPDATEиDELETEс безопасной предварительной выборкой. - Зафиксируйте в отчете результат каждого шага и число затронутых строк.
Связи и группировка
После CRUD на одной таблице переходите к связям. Главное правило: строка в дочерней таблице может ссылаться только на существующую строку в родительской таблице. Для проверки используется пара aircrafts и seats.
Создание дочерней таблицы seats
Таблица мест включает код самолета, номер места и класс обслуживания. Для нее используется составной первичный ключ aircraft_code + seat_no, внешний ключ на aircrafts и политика ON DELETE CASCADE.
Прим.: синтаксис
FOREIGN KEY ... REFERENCESВнешний ключ обеспечивает ссылочную целостность между таблицами.ON DELETE CASCADEудаляет дочерние строки автоматически при удалении родительской.
CREATE TABLE seats
(
aircraft_code char(3) NOT NULL,
seat_no varchar(4) NOT NULL,
fare_conditions varchar(10) NOT NULL,
CHECK (fare_conditions IN ('Economy', 'Comfort', 'Business')),
PRIMARY KEY (aircraft_code, seat_no),
FOREIGN KEY (aircraft_code)
REFERENCES aircrafts (aircraft_code)
ON DELETE CASCADE
);После создания таблицы сразу проверьте ее метаданные.
\d seatsПроверка ссылочной целостности
Выполните ошибочную вставку с несуществующим кодом самолета, чтобы проверить внешний ключ.
INSERT INTO seats VALUES ('123', '1A', 'Business');Добавьте корректные строки для существующего самолета и проверьте, что вставка проходит.
INSERT INTO seats VALUES
('SU9', '1A', 'Business'),
('SU9', '1B', 'Business'),
('SU9', '10A', 'Economy'),
('SU9', '10B', 'Economy');Агрегирование через GROUP BY
При работе с данными часто нужны не отдельные строки, а сводные показатели по группам: количество мест для каждого типа самолёта, распределение мест по классам обслуживания и т. п. Для этого в SQL предусмотрен оператор GROUP BY, который объединяет строки по заданному ключу, а агрегатные функции — такие как count(*), sum(), avg() — вычисляют итог по каждой группе.
Прим.: синтаксис
GROUP BYGROUP BYсобирает строки по ключу группировки. Для каждой группы агрегатные функции считают общий результат.
SELECT aircraft_code, count(*) AS seat_count
FROM seats
GROUP BY aircraft_code;Оператор GROUP BY сам по себе не устанавливает никакого порядка вывода групп. Чтобы результат был легко читаемым и сопоставимым, к агрегирующему запросу добавляют ORDER BY.
SELECT aircraft_code, count(*) AS seat_count
FROM seats
GROUP BY aircraft_code
ORDER BY seat_count;Группировка может быть и многоуровневой. Например, когда нужно не только общее число мест по самолету, но и разрез по классам обслуживания.
SELECT aircraft_code, fare_conditions, count(*) AS seat_count
FROM seats
GROUP BY aircraft_code, fare_conditions
ORDER BY aircraft_code, fare_conditions;Прим. Сравните обычную выборку и агрегированную выборку по
GROUP BY.
Задание
- Создайте
seatsс составным ключом и внешним ключом наaircrafts. - Вызовите ошибку внешнего ключа и зафиксируйте текст ошибки.
- Добавьте корректные строки и выполните три варианта агрегирующих запросов.
- Коротко опишите в отчете, зачем нужен
GROUP BYв практической работе.
Типы данных PostgreSQL
Тип данных — это часть предметной модели, а не техническая формальность. От правильного выбора типа зависят точность вычислений, корректность сравнений, объем хранения и поведение ограничений. Подробная практика по каждой группе типов — в лабораторной работе 3b; здесь важно знать, что real даёт погрешность при накоплении операций (для финансовых расчётов лучше подходит numeric), а timestamptz корректно хранит время при смене часового пояса.
Ограничения и значения по умолчанию
Ограничения — основа надежной схемы. Проверки в приложении нужны, но корректность данных в любом источнике записи обеспечивают именно ограничения БД: DEFAULT, CHECK, NOT NULL, UNIQUE, PRIMARY KEY, FOREIGN KEY.
Таблицы students и progress
Модель из двух таблиц позволяет быстро проверить базовые ограничения. students хранит карточку студента, progress — его успеваемость по дисциплинам и периодам обучения.
Сначала создайте таблицу students с первичным ключом по номеру зачетной книжки.
CREATE TABLE students
(
record_book numeric(5) NOT NULL,
name text NOT NULL,
doc_ser numeric(4),
doc_num numeric(6),
PRIMARY KEY (record_book)
);Затем создайте progress, добавив ограничения диапазонов, значение по умолчанию и внешний ключ.
CREATE TABLE progress
(
record_book numeric(5) NOT NULL,
subject text NOT NULL,
acad_year text NOT NULL,
term numeric(1) NOT NULL CHECK (term = 1 OR term = 2),
mark numeric(1) NOT NULL CHECK (mark >= 3 AND mark <= 5) DEFAULT 5,
FOREIGN KEY (record_book)
REFERENCES students (record_book)
ON DELETE CASCADE
ON UPDATE CASCADE
);Сразу после создания таблиц проверьте их определения.
\d students
\d progressПроверка ограничений
Добавьте базовую строку в students, чтобы сформировать корректную родительскую запись.
INSERT INTO students (record_book, name, doc_ser, doc_num)
VALUES (10001, 'Иванов Иван Иванович', 1234, 567890);Вставьте строку в progress без явного значения оценки и проверьте срабатывание DEFAULT 5.
INSERT INTO progress (record_book, subject, acad_year, term)
VALUES (10001, 'Базы данных', '2025/2026', 1);Выполним выборку и убедимся, что поле mark получило значение 5, хотя в операторе INSERT оно явно не указывалось — это результат действия ключевого слова DEFAULT.
SELECT *
FROM progress
WHERE record_book = 10001;Проверим срабатывание CHECK при нарушении диапазона оценки.
INSERT INTO progress (record_book, subject, acad_year, term, mark)
VALUES (10001, 'Алгоритмы', '2025/2026', 3, 2);Проверим нарушение внешнего ключа: вставка строки, ссылающейся на отсутствующий record_book.
INSERT INTO progress (record_book, subject, acad_year, term, mark)
VALUES (99999, 'Физика', '2025/2026', 1, 5);Проверим нарушение первичного ключа при вставке строки с уже существующим record_book.
INSERT INTO students (record_book, name)
VALUES (10001, 'Петров Петр Петрович');Дополнительные ограничения
Ограничениям задавайте осмысленные имена. Это делает сообщения об ошибках понятнее, особенно в больших схемах. Ниже пример именованного ограничения для оценки.
ALTER TABLE progress
ADD CONSTRAINT valid_mark CHECK (mark >= 3 AND mark <= 5);Когда бизнес-правило требует уникальности набора столбцов, добавляют составной UNIQUE. В нашем примере естественно запретить дубли пары doc_ser + doc_num.
ALTER TABLE students
ADD CONSTRAINT unique_passport UNIQUE (doc_ser, doc_num);NOT NULL запрещает только NULL, но не пустую строку. Чтобы блокировать пустые и «пробельные» имена, добавьте отдельную проверку.
ALTER TABLE students
ADD CONSTRAINT students_name_not_blank CHECK (trim(name) <> '');Прим. Первичный ключ в таблице может быть только один, а ограничений
UNIQUEможет быть несколько. Это важно при проектировании:PRIMARY KEYзадает основную идентификацию строки,UNIQUEдополняет ее бизнес-правилами.
Задание
- Создайте
studentsиprogressкак в примере. - Проверьте корректную вставку с
DEFAULTи ошибки поCHECK,PRIMARY KEY,FOREIGN KEY. - Добавьте хотя бы одно именованное ограничение через
ALTER TABLE. - В отчете кратко поясните, какие ошибки поймала БД и почему они возникли.
Политика внешних ключей
Когда таблицы связаны внешним ключом, заранее определяйте, что делать с дочерними строками при изменении или удалении родительской. Неправильно выбранная политика либо блокирует нужные операции, либо удаляет слишком много данных.
Режимы ON DELETE и ON UPDATE
Каскадный вариант удобен, если дочерние строки не имеют смысла без родительской. В учебной модели это допустимо для сценария «удалили студента - удалили его записи в progress».
CREATE TABLE progress_cascade
(
record_book numeric(5),
subject text,
FOREIGN KEY (record_book)
REFERENCES students (record_book)
ON DELETE CASCADE
);ON DELETE RESTRICT и NO ACTION
ON DELETE RESTRICT используют, когда удаление родительской строки нужно запретить при наличии зависимых данных. NO ACTION — синоним и значение по умолчанию: если политика не указана явно, PostgreSQL применяет именно его.
CREATE TABLE progress_restrict
(
record_book numeric(5),
subject text,
FOREIGN KEY (record_book)
REFERENCES students (record_book)
ON DELETE RESTRICT
);Прим. Существуют ещё два варианта:
SET NULL(обнуляет ссылку в дочерней строке) иSET DEFAULT(записывает значение по умолчанию). На практике они используются редко и требуют дополнительных условий (столбец FK должен допускатьNULLили иметь подходящее значение по умолчанию).
ON UPDATE CASCADE
При обновлении ключевого значения в родительской таблице каскадный режим переносит новое значение во все дочерние строки.
CREATE TABLE progress_update_cascade
(
record_book numeric(5),
subject text,
FOREIGN KEY (record_book)
REFERENCES students (record_book)
ON UPDATE CASCADE
);Задание
- Создайте тестовые таблицы для
CASCADEиRESTRICT. - На одинаковом наборе данных проверьте, как меняется поведение
DELETE. - Зафиксируйте в отчете, какую политику вы бы выбрали для учета успеваемости и почему.
Работа с учебной схемой
После базовых упражнений переходим к анализу готовой «производственной» схемы bookings. Здесь цель не переписывать весь DDL вручную, а научиться читать структуру: где естественные ключи, где суррогатные, как построены связи и почему выбраны определенные типы данных.
Подключение к схеме и первичный обзор
Убедитесь, что сессия открыта в базе demo и нужной схеме — одинаковые имена объектов могут существовать в разных схемах.
\c demo
SET search_path TO bookings;Просмотрите структуры таблиц в указанном порядке, отслеживая иерархию ссылочных связей.
\d airports
\d flights
\d bookings
\d tickets
\d ticket_flights
\d boarding_passesАнализ ключевых таблиц
Таблица airports удобна как отправная точка: у нее простой первичный ключ и понятные атрибуты предметной области. Координаты в схеме заданы через float (тип double precision без параметра точности).
Для flights нужно разобрать три критичных блока: суррогатный ключ flight_id (serial), естественный уникальный ключ (flight_no, scheduled_departure) и систему CHECK-ограничений по времени и статусам. Именно здесь хорошо видно, как SQL-контроль отражает бизнес-логику предметной области.
tickets, ticket_flights, boarding_passes
Таблица tickets демонстрирует практичный выбор типов: номер билета хранится как char(13) из-за возможных лидирующих нулей, а контактные данные вынесены в jsonb как слабоструктурированный атрибут. Это хороший пример того, как структура таблицы подстраивается под формат реальных данных.
ticket_flights и boarding_passes показывают работу составных ключей и составных внешних ключей. Здесь важно увидеть, как через пару полей (ticket_no, flight_id) выстраивается строгая связь между перелётом и посадочным талоном и исключаются дубликаты мест/номеров в рамках одного рейса.
В описании tickets обратите внимание на блоки «Ограничения внешнего ключа» и «Ссылки извне», отражающие двусторонние связи.
\d ticketsЗадание
- Просмотрите описания всех таблиц схемы
bookings. - Выпишите в отчет примеры: суррогатного ключа, естественного ключа, составного ключа.
- Отдельно отметьте денежные поля
numeric(10,2)и проверкиCHECKвflights. - Поясните, почему в
tickets.ticket_noвыбран символьный тип, а не числовой.
Модификация структуры
Даже хорошо спроектированная схема со временем меняется. Если таблицы уже заполнены, пересоздание обычно нежелательно, и основным инструментом становится ALTER TABLE. Ниже разобраны типовые сценарии: добавление столбца, наложение ограничений, смена типа, переименование и удаление.
Добавление нового столбца
Частая ошибка - пытаться сразу добавить столбец с NOT NULL в таблицу, где уже есть строки. PostgreSQL отклоняет такой шаг, поскольку существующие строки получат NULL, что нарушит новое ограничение.
Безопасный порядок: добавить столбец без ограничений, заполнить данные, убедиться в отсутствии NULL, затем зафиксировать ограничения.
ALTER TABLE aircrafts
ADD COLUMN speed integer;После добавления столбца все существующие строки содержат в нём NULL. Заполним значения поэтапно — отдельным UPDATE для каждого кода самолёта.
UPDATE aircrafts SET speed = 807 WHERE aircraft_code = '733';
UPDATE aircrafts SET speed = 851 WHERE aircraft_code = '763';
UPDATE aircrafts SET speed = 905 WHERE aircraft_code = '773';
UPDATE aircrafts SET speed = 840 WHERE aircraft_code IN ('319', '320', '321');
UPDATE aircrafts SET speed = 786 WHERE aircraft_code = 'CR2';
UPDATE aircrafts SET speed = 341 WHERE aircraft_code = 'CN1';
UPDATE aircrafts SET speed = 830 WHERE aircraft_code = 'SU9';Перед наложением NOT NULL проверьте отсутствие пустых значений.
SELECT count(*)
FROM aircrafts
WHERE speed IS NULL;Если запрос вернул 0, все строки заполнены и можно безопасно установить ограничение обязательности.
ALTER TABLE aircrafts
ALTER COLUMN speed SET NOT NULL;Наряду с обязательностью добавим проверку на минимально допустимое значение: крейсерская скорость не может быть ниже 300 км/ч ни для одного из представленных типов воздушных судов.
ALTER TABLE aircrafts
ADD CHECK (speed >= 300);Откат изменений и удаление ограничений
Любое изменение структуры должно быть обратимым. Ниже показано, как снять ограничения и убрать добавленный столбец, если решение оказалось неудачным.
ALTER TABLE aircrafts
ALTER COLUMN speed DROP NOT NULL;Для удаления CHECK нужно знать имя ограничения. Его можно посмотреть через \d aircrafts; в нашем случае имя, как правило, генерируется автоматически.
ALTER TABLE aircrafts
DROP CONSTRAINT aircrafts_speed_check;После снятия ограничений столбец можно удалить. Так завершается полный цикл проверки изменения структуры с возможностью отката.
ALTER TABLE aircrafts
DROP COLUMN speed;Изменение типа данных
Иногда меняется требуемая точность или формат хранения. В таком случае используют ALTER COLUMN ... SET DATA TYPE. В примере ниже переводим координаты аэропортов в фиксированную точность numeric(5,2).
Посмотрите исходные значения для сравнения с результатом после преобразования.
SELECT airport_code, longitude, latitude
FROM airports
ORDER BY airport_code
LIMIT 10;В PostgreSQL один оператор ALTER TABLE может содержать несколько предложений ALTER COLUMN, что позволяет изменить несколько столбцов атомарно — в рамках одной транзакции.
ALTER TABLE airports
ALTER COLUMN longitude SET DATA TYPE numeric(5,2),
ALTER COLUMN latitude SET DATA TYPE numeric(5,2);Сравним значения до и после преобразования.
SELECT airport_code, longitude, latitude
FROM airports
ORDER BY airport_code
LIMIT 10;Изменение типа с USING и внешним ключом
Когда преобразование идет между разными логическими типами, автоматического каста может быть недостаточно. Тогда используют USING, где явно описывают правило преобразования старых значений в новые.
Сначала создадим справочник классов обслуживания. Он понадобится как целевая таблица для будущего внешнего ключа.
CREATE TABLE fare_conditions
(
fare_conditions_code integer,
fare_conditions_name varchar(10) NOT NULL,
PRIMARY KEY (fare_conditions_code)
);Заполним справочник: каждому текстовому наименованию класса обслуживания сопоставим числовой код, который в дальнейшем заменит строковые значения в столбце fare_conditions таблицы seats.
INSERT INTO fare_conditions
VALUES (1, 'Economy'), (2, 'Business'), (3, 'Comfort');Перед сменой типа в seats нужно удалить старый CHECK по текстовым значениям. Если этого не сделать, ограничение начнет конфликтовать с новыми числовыми кодами.
ALTER TABLE seats
DROP CONSTRAINT seats_fare_conditions_check,
ALTER COLUMN fare_conditions SET DATA TYPE integer
USING (
CASE
WHEN fare_conditions = 'Economy' THEN 1
WHEN fare_conditions = 'Business' THEN 2
ELSE 3
END
);Проверим, что преобразование прошло корректно.
SELECT *
FROM seats
ORDER BY aircraft_code, seat_no;Добавим внешний ключ от seats к справочнику fare_conditions.
ALTER TABLE seats
ADD FOREIGN KEY (fare_conditions)
REFERENCES fare_conditions (fare_conditions_code);Чтобы сделать структуру самодокументируемой, переименуем столбец так, чтобы имя явно отражало хранение кода, а не текстового наименования.
ALTER TABLE seats
RENAME COLUMN fare_conditions TO fare_conditions_code;После этого приведите имя ограничения внешнего ключа к согласованному стилю.
ALTER TABLE seats
RENAME CONSTRAINT seats_fare_conditions_fkey
TO seats_fare_conditions_code_fkey;В конце добавим уникальность на название класса в справочнике. Это защитит таблицу от случайного дублирования текстовых наименований.
ALTER TABLE fare_conditions
ADD UNIQUE (fare_conditions_name);Задание
- Пройдите безопасный сценарий добавления нового столбца в
aircrafts. - Выполните откат (снятие ограничений и удаление столбца).
- Измените типы координат в
airportsи сравните значения до/после. - Протестируйте сценарий
USINGдляseatsи привяжите внешний ключ кfare_conditions.
Удаление таблиц и зависимости
Удаление структуры в связанной схеме требует аккуратности. Если таблица участвует во внешних ключах, попытка простого DROP TABLE приведет к ошибке. Это нормальная защитная реакция PostgreSQL, которая не дает случайно разрушить часть схемы.
Сначала попробуем удалить таблицу без каскада. Команда полезна как диагностическая: из текста ошибки вы узнаете, какие именно объекты зависят от удаляемой таблицы.
DROP TABLE aircrafts;Если цель - действительно удалить таблицу вместе с зависимыми ограничениями, используют CASCADE. Перед запуском важно четко понимать, какие связи будут затронуты.
DROP TABLE aircrafts CASCADE;В скриптах часто применяют защитный вариант с IF EXISTS, чтобы команда не падала при повторном запуске. Это удобный формат для идемпотентных миграций и учебных перезапусков.
DROP TABLE IF EXISTS aircrafts CASCADE;После каскадного удаления всегда проверяйте структуру связанных таблиц. Так вы увидите, какие внешние ключи были удалены автоматически вместе с объектом-источником.
\d flights
\d seatsПрим.
CASCADEудобен, но потенциально опасен. В рабочей БД его применяют только после явной проверки карты зависимостей.
Задание
- Проверьте поведение
DROP TABLEбез каскада и с каскадом. - Зафиксируйте в отчете, какие зависимости удалились вместе с таблицей.
- Приведите краткий вывод, когда
IF EXISTSдействительно полезен.
Схемы базы данных и search_path
Схема - это пространство имен объектов. В одной базе может быть несколько схем, и в каждой могут существовать одноименные таблицы. Из-за этого в SQL всегда важен контекст: либо вы явно пишете schema.table, либо настраиваете search_path.
Для начала выведем список всех схем в текущей базе данных. В учебной базе demo помимо стандартной схемы public присутствует схема bookings, в которой находятся все таблицы предметной области.
\dnДальше посмотрите текущее значение search_path. По умолчанию PostgreSQL обычно начинает поиск с "$user", затем public, но в учебной базе основная рабочая схема - bookings.
SHOW search_path;Теперь явно установите bookings как текущую схему поиска. После этого обращения к таблицам без префикса схемы будут интерпретироваться именно в ее контексте.
SET search_path = bookings;Проверьте результат через функцию current_schema (в PostgreSQL ее можно вызывать без круглых скобок).
SELECT current_schema;Два приведённых ниже запроса семантически эквивалентны: в первом схема указана явно, во втором PostgreSQL находит таблицу через search_path. Сравните их результаты, чтобы убедиться в идентичности вывода.
SELECT *
FROM bookings.aircrafts
LIMIT 5;
SELECT *
FROM aircrafts
LIMIT 5;Если запрос завершается сообщением «relation does not exist», первым делом следует проверить, в какой базе и схеме открыта сессия. Следующие две команды дают исчерпывающую картину текущего контекста.
SELECT current_database(), current_schema;
SHOW search_path;Для поиска объектов в нескольких схемах задают явный порядок просмотра; первая схема в списке имеет приоритет.
SET search_path = bookings, public;Прим. Команда
SET search_pathдействует только в рамках текущей сессии и сбрасывается при новом подключении. Чтобы сделать настройку постоянной для конкретной роли или базы данных, используютALTER ROLE имя_роли SET search_path TO схема;илиALTER DATABASE demo SET search_path TO bookings, public;.
При создании нового объекта можно явно указать схему, даже если она не текущая.
CREATE TABLE public.lab3_note
(
id integer,
note text
);Задание
- Проверьте список схем и текущее значение
search_path. - Установите
bookingsкак текущую схему и сравните выборку с префиксом схемы и без него. - Зафиксируйте в отчете блок «контекст выполнения»:
current_database,current_schema,search_path.
Итоговое практическое задание
Задача. Спроектируйте мини-схему из двух связанных таблиц для предметной области по вашему выбору (например: библиотека и читатели, товары и заказы, курсы и записи на курсы, музыканты и альбомы). Не копируйте students/progress — придумайте свой домен.
Требования к схеме:
- Родительская таблица: первичный ключ, минимум 3 столбца разных типов (
integer,text,numericилиdate), хотя бы одно ограничениеCHECK. - Дочерняя таблица: внешний ключ на родительскую с осознанно выбранной политикой
ON DELETE, хотя бы одноDEFAULT-значение. - Вставьте не менее 5 строк в родительскую и 8 строк в дочернюю таблицу.
Что нужно продемонстрировать:
CREATE TABLEдля обеих таблиц с обоснованием выбора типов и ограничений.- Корректный
INSERTи хотя бы одну намеренную ошибку (нарушениеCHECK,FKилиPK) с фиксацией текста ошибки. SELECTсWHERE,ORDER BYиGROUP BY(по дочерней таблице).UPDATEс предварительной проверкой черезSELECT.DELETEродительской строки — показать, как сработала выбранная политика FK.ALTER TABLE: добавить один столбец, заполнить его, наложитьNOT NULL.
Дополнительно (по желанию)
- Добавьте в
studentsстолбецwho_adds_rowсDEFAULT current_user, затем проверьте вставку без явного значения этого поля. - Добавьте в
studentsстолбец времени вставки сDEFAULT current_timestampи сравните, чем этот подход удобен при аудите. - Создайте отдельную схему для экспериментов, перенесите в нее одну тестовую таблицу и проверьте доступ через
search_path.
Требования к отчету
Отчет оформляется одним файлом (.md, .pdf или .docx). Полный перенос всех команд из текста лабораторной не нужен — достаточно ключевых проверок.
-
CRUD и ограничения. Покажите по одному примеру каждой операции (
INSERT,SELECT,UPDATE,DELETE) и минимум два срабатывания ограничений (CHECK,FKилиPK) — команда, текст ошибки, пояснение в 1-2 предложениях. -
Работа со схемой
bookings. Анализ структуры двух таблиц: тип ключа, одно проектное решение по типам данных, одно ограничение. РезультатALTER TABLE(добавление столбца или изменение типа). -
Итоговое задание. DDL ваших таблиц, примеры данных, ключевые запросы. Обоснуйте выбор типов, ограничений и политики FK в 3-5 предложениях.
-
Выводы. 3-5 предложений: что оказалось новым или неочевидным. Если при выполнении возникали ошибки, кратко опишите каждую: в чём заключалась ошибка, почему она произошла и как вы её исправили.