Язык манипулирования данными (Data Manipulation Language) управляет фактическим состоянием кортежей таблиц в рамках транзакционной модели ACID.
xmax.Явное перечисление колонок защищает код от изменений схемы. Пакетная вставка кратно снижает накладные расходы.
-- Пакетная вставка (Batch Multi-row INSERT) 5 позиций оборудования
INSERT INTO warehouse_inventory (sku, product_name, quantity, unit_price)
VALUES
('SKU-101', 'Процессор Intel Core i5-13400', 15, 18200.00),
('SKU-102', 'Материнская плата B760 DDR5', 20, 12500.00),
('SKU-103', 'Модуль памяти DDR5 32GB Kit', 35, 9800.00),
('SKU-104', 'Блок питания 750W Gold', 18, 8100.00),
('SKU-105', 'Корпус ATX Mesh Black', 12, 5400.00)
RETURNING id, sku, created_at;
Эффект батчинга: 1 сетевой пакет вместо N; 1 операция сброса WAL (fsync) вместо N; 1 проход оптимизатора запросов.
Позволяет производить миграцию и трансформацию массивов данных исключительно силами ядра СУБД.
-- Перенос закрытых кредитных договоров в исторический архив
INSERT INTO contracts_archive (
contract_id, client_id, close_date, final_balance
)
SELECT
c.id, c.client_id, c.closed_at, c.balance
FROM active_contracts c
WHERE c.status = 'CLOSED'
AND c.closed_at < CURRENT_DATE - INTERVAL '1 year';
Преимущество архитектуры: Отсутствие передачи гигабайт сырых строк на сторону бэкенда через TCP-сокет. Операция выполняется in-memory на уровне движка СУБД с максимальной скоростью дискового ввода-вывода.
Конструкция RETURNING возвращает клиенту значения вставленных, обновленных или удаленных кортежей без повторного SELECT.
INSERT INTO orders (client_id, amount)
VALUES (42, 15990.00)
RETURNING
order_id,
created_at,
status;
Исключает состояние гонки при попытке прочитать MAX(id) или currval() в многопоточной среде.
Классический антипаттерн «проверить существование в SELECT, а затем выполнить INSERT» приводит к коллизиям в продакшне.
-- Поток А: Проверяет наличие товара
SELECT id FROM warehouse WHERE sku = 'SKU-101'; -- (Пусто!)
-- Поток Б: Параллельно проверяет тот же товар
SELECT id FROM warehouse WHERE sku = 'SKU-101'; -- (Тоже пусто!)
-- Поток А выполняет: INSERT INTO warehouse ... -- УСПЕШНО!
-- Поток Б выполняет: INSERT INTO warehouse ... -- ОШИБКА: unique_violation!
Вывод: Проверка на уровне приложения не атомарна. Требуется нативная поддержка со стороны ядра СУБД — механизм UPSERT.
Конструкция ON CONFLICT (target_column) перехватывает ошибку уникальности прямо в момент вставки.
Игнорирует конфликтующие кортежи. Ошибка подавляется, транзакция не прерывается.
INSERT INTO tags (tag_name)
VALUES ('postgresql'), ('sql')
ON CONFLICT (tag_name)
DO NOTHING;
Идеально для импорта справочников и очередей задач.
При конфликте преобразует операцию в атомарный UPDATE существующей строки.
INSERT INTO metrics (host, cpu)
VALUES ('srv-1', 78.4)
ON CONFLICT (host)
DO UPDATE SET
cpu = EXCLUDED.cpu;
Использует псевдотаблицу EXCLUDED.
Виртуальная таблица EXCLUDED содержит кортеж, который предлагался к вставке.
-- Идемпотентный UPSERT: суммирование остатков на складе при повторной накладной
INSERT INTO warehouse_inventory (sku, product_name, quantity, unit_price)
VALUES ('SKU-101', 'Процессор Intel Core i5-13400', 10, 17900.00)
ON CONFLICT (sku)
DO UPDATE SET
-- склад.остаток (текущий) + EXCLUDED.остаток (новый приход)
quantity = warehouse_inventory.quantity + EXCLUDED.quantity,
unit_price = EXCLUDED.unit_price,
updated_at = CURRENT_TIMESTAMP
RETURNING warehouse_inventory.id, warehouse_inventory.sku, warehouse_inventory.quantity;
Формула слияния: Имя таблицы обращается к старому сохраненному значению, префикс EXCLUDED. — к новому поступившему значению.
Оператор UPDATE обновляет существующие кортежи. Забытая секция WHERE приводит к катастрофическим последствиям.
-- Селективное обновление
UPDATE credit_contracts
SET
status = 'DELINQUENT',
penalty_rate = 0.05
WHERE overdue_days > 30
AND status = 'ACTIVE'
RETURNING id, client_id;
SELECT COUNT(*) FROM ... WHERE ...BEGIN; ... ROLLBACK;RETURNING для контроля затронутых строк.Специфика PostgreSQL: объединение обновляемой таблицы с другими таблицами через секцию FROM.
-- Обновление статуса приема врача по внешнему соединению с таблицей Оплаты
UPDATE doctor_appointments AS a
SET
status = 'CONFIRMED',
updated_at = CURRENT_TIMESTAMP
FROM payment_invoices AS p
WHERE a.id = p.appointment_id
AND p.payment_status = 'PAID'
AND a.status = 'PENDING_PAYMENT'
RETURNING a.id, a.patient_id, a.status;
Критическое правило: Обновляемая таблица (doctor_appointments) не указывается в списке FROM повторно, иначе будет сформировано декартово произведение (Cartesian Product).
Удаление кортежей с возвратом удаленных строк для дальнейшей обработки.
-- Селективное удаление отмененных приемов старше 1 года
DELETE FROM doctor_appointments
WHERE status = 'CANCELLED'
AND appointment_date < CURRENT_DATE - INTERVAL '1 year'
RETURNING id, patient_fio, appointment_date, cancellation_reason;
Физическая сущность DELETE в PostgreSQL: Строки не затираются нулями на диске. В заголовке кортежа выставляется поле xmax = tx_id. Кортеж перестает быть виден последующим транзакциям, превращаясь в Dead Tuple. Очистка выполняется VACUUM.
| Параметр | DELETE FROM t | TRUNCATE TABLE t |
|---|---|---|
| Тип операции | DML (уровень строк) | DDL (уровень структуры) |
| Уровень блокировки | ROW EXCLUSIVE |
ACCESS EXCLUSIVE (полный лок) |
| Генерация WAL | Запись каждой строки | Только факт выделения relfilenode |
| Возврат памяти ОС | Нет (нужен VACUUM FULL) | Мгновенно освобождает диск |
| Скорость на 1 млн строк | Секунды (сканирование + WAL) | Миллисекунды (пересоздание файла) |
Журнал предзаписи WAL гарантирует сохранность данных (Crash Recovery), но создает нагрузку на дисковый ввод-вывод.
COMMIT происходит системный вызов fsync() журнала WAL.UNLOGGED TABLE для промежуточных расчетов (не пишут в WAL).Каждый дополнительный индекс на таблице ускоряет чтение, но замедляет DML-операции.
Создается новая строка. Если колонка индексирована — во ВСЕ B-Tree индексы добавляются новые ссылки. Индексы разбухают (bloat).
Если индексированные поля не менялись и на текущей странице есть место, СУБД строит цепочку указателей внутри страницы. B-Tree индексы не трогаются! Колоссальная экономия IOPS.
Robert'); DROP TABLE Students;--. Склейка неэкранированных строковых переменных в операторах INSERT, UPDATE и DELETE открывает критическую уязвимость внедрения SQL-инъекций (SQL Injection). В промышленной разработке манипулирование данными должно выполняться строго через параметризованные запросы (Prepared Statements) и механизмы безопасного экранирования.