Дисциплина: ОП.08 Основы проектирования баз данных | Тема 5. Организация запросов SQL (§2.2 с.7–8 программы) | СИЭУиП
Язык SQL традиционно подразделяется на несколько функциональных подмножеств. Если DML (Data Manipulation Language) отвечает за операции над строками внутри уже существующих таблиц (SELECT, INSERT, UPDATE, DELETE), то DDL (Data Definition Language) управляет самим мета-слоем — структурой базы данных, ее типами, схемами, отношениями, индексами и ограничениями целостности.
В современных объектно-реляционных СУБД (PostgreSQL) таблица не является изолированным текстовым описанием. Каждое отношение представляет собой запись в системных каталогах ядра:
pg_class — регистрирует факт существования отношения (таблицы, индекса, представления, последовательности), связывает имя с уникальным идентификатором объекта (OID) и физическим файлом на диске (relfilenode).pg_attribute — описывает каждый столбец таблицы: его порядковый номер (attnum), тип данных (atttypid), модификатор длины (atttypmod) и флаг обязательности (attnotnull).pg_constraint — хранит все декларированные ограничения (первичные ключи, внешние ключи, уникальные индексы, предикаты CHECK).Когда вы выполняете команду DDL, ядро СУБД обновляет строки в системных таблицах каталога, проверяет совместимость существующих кортежей с новыми правилами и, при необходимости, перестраивает дисковые страницы данных.
Оператор CREATE TABLE аллоцирует новую таблицу. В промышленном проектировании базовый синтаксис дополняется атрибутами отказоустойчивости, значениями по умолчанию и декларативными ограничениями.
CREATE TABLE IF NOT EXISTS warehouses (
-- 1. Суррогатный первичный ключ
warehouse_id SERIAL PRIMARY KEY,
-- 2. Строковые атрибуты фиксированной и переменной длины
warehouse_code VARCHAR(20) NOT NULL,
warehouse_name VARCHAR(150) NOT NULL,
city VARCHAR(80) NOT NULL,
-- 3. Точные числовые типы с фиксированной точкой для физических величин
total_area_sqm NUMERIC(10, 2) NOT NULL,
-- 4. Логические флаги с дефолтным значением
is_active BOOLEAN NOT NULL DEFAULT TRUE,
-- 5. Метки времени для аудита жизненного цикла записи
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
-- 6. Именованные ограничения уровня таблицы
CONSTRAINT unq_warehouse_code UNIQUE (warehouse_code),
CONSTRAINT chk_total_area_positive CHECK (total_area_sqm > 0.00)
);
Временные таблицы создаются с ключевым словом TEMPORARY (или TEMP). Они кардинально отличаются от стандартных постоянных отношений:
pg_temp_XX. Если имя временной таблицы совпадает с именем постоянной, временная таблица экранирует постоянную до завершения сеанса!ON COMMIT PRESERVE ROWS (по умолчанию) — строки сохраняются до конца сеанса клиента.ON COMMIT DROP — таблица автоматически удаляется после завершения текущей транзакции (COMMIT).ON COMMIT DELETE ROWS — таблица очищается от данных в конце транзакции, но структура сохраняется.-- Создание временного буфера для разбора поступившей партии
CREATE TEMPORARY TABLE temp_incoming_shipment (
barcode VARCHAR(64) NOT NULL,
quantity INT NOT NULL CHECK (quantity > 0)
) ON COMMIT PRESERVE ROWS;
В процессе промышленной эксплуатации база данных неизбежно эволюционирует. Оператор ALTER TABLE позволяет изменять топологию отношения без удаления данных.
| Операция ALTER TABLE | Синтаксический шаблон SQL | Бизнес-сценарий применения |
|---|---|---|
| ADD COLUMN | ALTER TABLE t ADD COLUMN col_name TYPE DEFAULT val [NOT NULL]; |
Появление нового атрибута сущности (например, добавление поля temperature_zone). |
| DROP COLUMN | ALTER TABLE t DROP COLUMN col_name [CASCADE]; |
Удаление устаревшего реквизита. С опцией CASCADE удаляет зависимые представления и индексы. |
| ALTER COLUMN TYPE (c USING) | ALTER TABLE t ALTER COLUMN col TYPE new_t USING col::new_t; |
Масштабирование разрядности (из INTEGER в NUMERIC(10,2)) или конвертация строки в дату. |
| ADD CONSTRAINT | ALTER TABLE t ADD CONSTRAINT name FOREIGN KEY (col) REFERENCES p(id); |
Ужесточение правил целостности данных или связывание независимых таблиц внешним ключом. |
| DROP CONSTRAINT | ALTER TABLE t DROP CONSTRAINT constraint_name; |
Снятие устаревшего ограничения уникальности или внешнего ключа для рефакторинга схемы. |
| RENAME COLUMN / TABLE | ALTER TABLE t RENAME COLUMN old_c TO new_c;ALTER TABLE t RENAME TO new_name; |
Рефакторинг терминологии предметной области (например, унификация именования атрибутов). |
Когда вы меняете тип столбца, ядро СУБД должно перекодировать существующие бинарные данные кортежей на всех страницах диска. Если автоматическое неявное приведение (implicit cast) отсутствует, СУБД возвращает ошибку:
-- ОШИБКА: колонка "price" не может быть автоматически приведена к типу numeric
ALTER TABLE hotel_rooms ALTER COLUMN price TYPE NUMERIC(10, 2);
Конструкция USING передает парсеру явную функцию преобразования. Это может быть как простое приведение через оператор ::, так и сложная логика с функциями обработки строк:
-- Преобразование целого числа в точное десятичное:
ALTER TABLE hotel_rooms
ALTER COLUMN price TYPE NUMERIC(10, 2)
USING price::NUMERIC(10, 2);
-- Преобразование текстовой даты из формата 'DD.MM.YYYY' в системный DATE:
ALTER TABLE contracts
ALTER COLUMN signing_date TYPE DATE
USING to_date(signing_date, 'DD.MM.YYYY');
В высоконагруженных промышленных системах (банковский эквайринг, маркетплейсы) недопустимо останавливать прием клиентских запросов для обновления схемы. Применение наивного ALTER TABLE приводит к захвату блокировки AccessExclusiveLock, которая парализует все входящие запросы SELECT и INSERT.
Вместо изменения существующего столбца «на месте» с тяжелой перезаписью дисковых страниц добавляется новый столбец с константным значением по умолчанию:
ALTER TABLE hotel_rooms ADD COLUMN price_numeric NUMERIC(10, 2) DEFAULT 0.00;
В PostgreSQL 11+ это действие модифицирует только метаданные каталога за долю миллисекунды ($O(1)$).
Создается триггер, который при любых операциях INSERT или UPDATE автоматически дублирует данные из старой колонки в новую. Теперь приложение может писать в старую колонку, но новая колонка всегда актуальна.
Небольшими порциями (батчами по 1000 строк) исторические данные копируются из старого столбца в новый. Это предотвращает длительные транзакции и переполнение журнала WAL:
UPDATE hotel_rooms SET price_numeric = price::NUMERIC(10, 2)
WHERE room_id BETWEEN 1 AND 1000 AND price_numeric = 0.00;
Если на новую колонку требуется наложить ограничение CHECK, наивный ADD CONSTRAINT заблокирует таблицу для полной проверки. Инженерное решение разбивает операцию на два шага:
-- Шаг 4.1: Мгновенное наложение правила только на НОВЫЕ строки
ALTER TABLE hotel_rooms
ADD CONSTRAINT chk_price_positive CHECK (price_numeric > 0) NOT VALID;
-- Шаг 4.2: Фоновая валидация существующих строк без блокировки SELECT и INSERT!
ALTER TABLE hotel_rooms
VALIDATE CONSTRAINT chk_price_positive;
После развертывания новой версии приложения, читающей из price_numeric, триггер удаляется, а старый столбец логически отключается через DROP COLUMN.
Ограничение внешнего ключа (FOREIGN KEY) связывает зависимую (дочернюю) таблицу с родительской, предотвращая появление «висячих ссылок» (orphaned records). При удалении или обновлении строки в родительской таблице стандарт SQL предусматривает различные режимы ссылочной целостности.
FOREIGN KEY ... ON DELETE CASCADE — это правило уровня манипулирования данными (DML). При удалении строки из таблицы клиентов СУБД каскадно удаляет строки счетов из дочерней таблицы. Сами таблицы остаются целыми.DROP TABLE parent_table CASCADE — это команда уровня определения данных (DDL). Она физически уничтожает саму таблицу parent_table и автоматически удаляет зависимые ограничения внешнего ключа в дочерних таблицах, а также удаляет зависимые представления (VIEW). Сами строки в дочерней таблице НЕ удаляются, но таблица лишается внешнего ключа и теряет ссылочную целостность!Когда требуется полностью очистить таблицу (например, после завершения учебного семестра или при обновлении промежуточных логов), использование DELETE FROM table_name; является грубой инженерной ошибкой на больших объемах данных.
DELETE построчно считывает каждую страницу в оперативную память (shared buffers), записывает образ старой строки в журнал транзакций (WAL) и устанавливает заголовочный флаг t_xmax («мертвый кортеж»). Место на диске не освобождается до выполнения утилиты VACUUM FULL.TRUNCATE является операцией DDL. СУБД деаллоцирует физический файл кучи (relfilenode) и мгновенно связывает имя таблицы в каталоге с новым пустым дисковым файлом. Операция выполняется за фиксированное время $O(1)$ независимо от того, хранилось ли в таблице 10 строк или 100 000 000 строк.При использовании автоинкрементных столбцов (SERIAL, BIGSERIAL, IDENTITY) таблица ассоциирована с объектом генератора последовательностей (SEQUENCE):
-- 1. Очистка данных с продолжением нумерации (CONTINUE IDENTITY по умолчанию)
-- Если последняя запись имела ID=500, то следующая вставка получит ID=501:
TRUNCATE TABLE student_attendance_log;
-- 2. Очистка данных со сбросом автоинкремента в начальное значение:
-- Счетчик последовательности сбрасывается в 1:
TRUNCATE TABLE student_attendance_log RESTART IDENTITY;
ALTER COLUMN TYPE часто требуется выражение USING? Приведите пример конвертации данных, где USING обязателен.DROP TABLE customers RESTRICT;, если в базе существует дочерняя таблица orders с внешним ключом на customers?DROP TABLE ... CASCADE в промышленной базе данных? Удаляются ли данные в дочерних таблицах?TRUNCATE TABLE превосходит DELETE FROM table? В каких ситуациях применять TRUNCATE запрещено?RESTART IDENTITY при выполнении TRUNCATE TABLE?ALTER TABLE)? Как эта блокировка может вызвать аварийную остановку (Outage) высоконагруженного веб-приложения?CHECK на многомиллионной таблице без остановки сервиса.