INSERT INTO: добавление строк
INSERT INTO: добавление строк
До сих пор все запросы читали данные — SELECT. Но реальные приложения не только читают: они добавляют, изменяют и удаляют данные. Операторы изменения данных называются DML (Data Manipulation Language): INSERT, UPDATE, DELETE. В этом уроке — INSERT INTO: как добавлять новые строки в таблицу.
Базовый синтаксис INSERT
INSERT INTO таблица (col1, col2, col3)
VALUES (val1, val2, val3);
Пример — добавить нового клиента:
INSERT INTO customers (name, email, city)
VALUES ('Анна Новая', 'anna@example.com', 'Москва');
СУБД создаст новую строку в таблице customers с указанными значениями. Колонки, не перечисленные в списке, получают значения по умолчанию (или NULL, если умолчания нет).
Зачем перечислять колонки
Теоретически можно писать без списка колонок:
-- Без списка колонок (не рекомендуется)
INSERT INTO customers VALUES (1, 'Анна', 'anna@example.com', 'Москва');
Проблема: если структура таблицы изменится — добавится или удалится колонка — запрос перестанет работать или вставит данные не туда. Всегда перечисляйте колонки явно — это защита от ошибок при изменении схемы.
Кроме того, явный список колонок:
- Документирует, какие поля заполняются
- Позволяет пропустить колонки с умолчаниями (например,
id SERIALилиcreated_at DEFAULT NOW())
Вставка нескольких строк
Один INSERT может добавить несколько строк сразу:
INSERT INTO customers (name, email, city)
VALUES
('Борис Иванов', 'boris@example.com', 'СПб'),
('Вера Петрова', 'vera@example.com', 'Казань'),
('Гриша Сидоров', 'grisha@example.com', 'Москва');
Это эффективнее, чем три отдельных INSERT — один раз обращаемся к таблице, одна транзакция.
Автоинкрементные колонки: SERIAL и GENERATED
Первичный ключ id обычно заполняется автоматически — не нужно указывать его в INSERT:
-- Таблица: customers(id SERIAL, name TEXT, email TEXT)
INSERT INTO customers (name, email)
VALUES ('Новый клиент', 'new@example.com');
-- id получит следующее значение последовательности: 1, 2, 3...
SERIAL в PostgreSQL — это сокращение для INTEGER с автоматической последовательностью (sequence). PostgreSQL 10+ предлагает стандартный вариант: id GENERATED ALWAYS AS IDENTITY. Оба работают аналогично — при вставке без явного id СУБД генерирует следующее значение.
RETURNING: получить вставленные значения
PostgreSQL-расширение RETURNING возвращает значения вставленных строк — полезно, чтобы узнать сгенерированный id:
INSERT INTO customers (name, email, city)
VALUES ('Дина Орлова', 'dina@example.com', 'Москва')
RETURNING id, name;
Результат:
id | name
---+-----------
5 | Дина Орлова
RETURNING id — самый частый случай: приложение получает id только что вставленной записи и может сразу использовать его для создания связанных записей. Без RETURNING пришлось бы делать отдельный SELECT для получения id.
Проверь себя: что вернёт INSERT ... RETURNING *?
Значения по умолчанию
Если колонка имеет значение по умолчанию (DEFAULT), можно явно его использовать:
INSERT INTO orders (customer_id, amount, status)
VALUES (5, 1500.00, DEFAULT);
-- status получит своё DEFAULT-значение, например 'pending'
Или просто пропустить колонку в списке — результат тот же.
NULL в INSERT
Если нужно явно вставить NULL:
INSERT INTO customers (name, email, phone)
VALUES ('Тест', 'test@example.com', NULL);
Разница между NULL и пропуском колонки: оба результата одинаковые (значение NULL), но явный NULL нагляднее показывает намерение.
Если колонка объявлена NOT NULL и нет DEFAULT — попытка вставить NULL или пропустить колонку вызовет ошибку:
-- Ошибка: email NOT NULL, нельзя пропустить или вставить NULL
INSERT INTO customers (name) VALUES ('Тест');
-- ERROR: null value in column "email" of relation "customers"
Ошибки при вставке: нарушения ограничений
INSERT может завершиться ошибкой при нарушении ограничений:
Уникальность (UNIQUE / PRIMARY KEY):
INSERT INTO customers (id, name) VALUES (1, 'Дубль');
-- ERROR: duplicate key value violates unique constraint "customers_pkey"
Внешний ключ (FOREIGN KEY):
INSERT INTO orders (customer_id, amount) VALUES (9999, 100);
-- ERROR: insert or update on table "orders" violates foreign key constraint
-- Key (customer_id)=(9999) is not present in table "customers"
NOT NULL:
INSERT INTO customers (name) VALUES ('Тест'); -- пропустили email NOT NULL
-- ERROR: null value in column "email" violates not-null constraint
Все эти ошибки — защитные механизмы СУБД, которые не дают сохранить некорректные данные. Подробнее об ограничениях — в модуле 9.
Производительность при массовой вставке
При необходимости вставить тысячи строк есть несколько подходов:
Пакетная вставка (batch insert) — несколько VALUES в одном INSERT:
INSERT INTO products (title, price, category) VALUES
('Товар 1', 100, 'A'), ('Товар 2', 200, 'B'), ..., ('Товар 1000', 150, 'C');
Значительно быстрее, чем 1000 отдельных INSERT.
COPY — специальная команда PostgreSQL для загрузки данных из файла. Это самый быстрый способ вставки больших объёмов:
COPY customers (name, email, city) FROM '/path/to/data.csv' CSV HEADER;
COPY — тема за рамками этого курса, но важно знать: для ETL-задач и загрузки больших датасетов COPY на порядок быстрее последовательных INSERT.
INSERT и транзакции
По умолчанию каждый INSERT в PostgreSQL — автоматическая транзакция: либо строка вставляется полностью, либо не вставляется вовсе. Если INSERT нескольких строк прерывается на середине (например, вторая строка нарушает ограничение), ни одна строка не будет вставлена.
Подробнее о транзакциях — в модуле 10. Пока достаточно знать: INSERT атомарен.
Вставка из SELECT: краткий обзор
Можно вставить в таблицу данные из другой таблицы:
-- Скопировать московских клиентов в архивную таблицу
INSERT INTO customers_archive (name, email, city)
SELECT name, email, city
FROM customers
WHERE city = 'Москва';
INSERT INTO ... SELECT — мощный инструмент для массового копирования данных. Подробнее — в следующем уроке модуля.
Практический пример: регистрация пользователя
Типичный сценарий в веб-приложении — создание нового пользователя с получением его id:
INSERT INTO users (username, email, password_hash, created_at)
VALUES ('anna_smith', 'anna@company.com', 'hashed_value', NOW())
RETURNING id;
Приложение использует возвращённый id для создания профиля или начальных настроек. NOW() — функция, возвращающая текущее время — подставляет временную метку регистрации.
Как проверить результат INSERT
INSERT без RETURNING не возвращает данные — только сообщение INSERT 0 1 (0 OID, 1 строка вставлена). Чтобы убедиться, что вставка прошла корректно, выполните SELECT после:
INSERT INTO customers (name, email, city)
VALUES ('Тест', 'test@example.com', 'Москва');
-- Проверка:
SELECT * FROM customers WHERE email = 'test@example.com';
Или используйте RETURNING сразу:
INSERT INTO customers (name, email, city)
VALUES ('Тест', 'test@example.com', 'Москва')
RETURNING *;
RETURNING * возвращает всю вставленную строку, включая сгенерированный id и все поля с DEFAULT.
Ментальная модель: INSERT как добавление в список
Таблица — это упорядоченный список строк. INSERT добавляет в конец этого списка новую запись. Порядок строк без ORDER BY не гарантирован — СУБД возвращает строки в произвольном порядке. Не полагайтесь на то, что «новая строка всегда последняя в SELECT * FROM».
Краткий итог
INSERT INTO таблица (колонки) VALUES (значения)— добавляет строку- Всегда перечисляйте колонки явно — защита от изменений схемы
- Несколько строк в одном
INSERT VALUES (...)— эффективнее серии одиночных вставок - Колонки с
DEFAULTилиSERIALможно не указывать — СУБД заполнит автоматически RETURNING col(PostgreSQL) — возвращает значения вставленной строки, например сгенерированныйid- Нарушения ограничений (PK, FK, NOT NULL, UNIQUE) вызывают ошибку и отменяют вставку
INSERT в реальных приложениях
В production-коде INSERT почти никогда не выполняется напрямую из SQL-клиента — он идёт через ORM (Object-Relational Mapper) или prepared statements в коде приложения. Это важно с точки зрения безопасности: параметры передаются отдельно, что предотвращает SQL-инъекции.
Пример на Node.js с pg-клиентом:
// Параметры передаются отдельно — безопасно
const result = await client.query(
'INSERT INTO customers (name, email) VALUES ($1, $2) RETURNING id',
['Анна', 'anna@example.com']
);
const newId = result.rows[0].id;
Никогда не составляйте SQL-строку конкатенацией пользовательского ввода — это классическая SQL-инъекция. В SQL-клиентах для анализа данных это не проблема, но в приложениях — критическое правило безопасности.
Что дальше
Вы умеете добавлять строки. Следующий урок — UPDATE: как изменять существующие данные. Особое внимание к WHERE — без него UPDATE изменит все строки в таблице.