Работа с базами данных всегда начинается с добавления данных. В PostgreSQL оператор INSERT является основным инструментом для вставки новых записей в таблицы. В этой статье мы подробно разберем все способы использования INSERT — от базовых примеров до продвинутых техник вставки данных.
Подготовка: структура таблицы employees
Прежде чем начать добавлять данные, давайте вспомним структуру таблицы, с которой будем работать:
CREATE TABLE employees ( employee_id SERIAL PRIMARY KEY, -- Уникальный идентификатор (автоинкремент) first_name VARCHAR(50) NOT NULL, -- Имя сотрудника last_name VARCHAR(50) NOT NULL, -- Фамилия сотрудника email VARCHAR(100) UNIQUE NOT NULL, -- Email (уникальный) hire_date DATE NOT NULL, -- Дата приема на работу salary DECIMAL(10, 2), -- Заработная плата is_active BOOLEAN DEFAULT TRUE, -- Статус активности (по умолчанию TRUE) created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- Дата создания записи );
Ключевые особенности таблицы:
-
employee_id — заполняется автоматически (SERIAL)
-
email — должен быть уникальным для каждого сотрудника
-
is_active — имеет значение по умолчанию TRUE
-
created_at — автоматически устанавливается текущая дата и время
Способ 1: Базовая вставка с указанием всех полей
Пример 1: Явное указание всех значений
INSERT INTO employees ( first_name, last_name, email, hire_date, salary, is_active ) VALUES ( 'Иван', 'Петров', Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ', '2024-01-15', 75000.00, TRUE );
Разбор команды:
-
INSERT INTO employees— указываем, в какую таблицу вставляем данные -
(first_name, last_name, ...)— перечисляем названия столбцов -
VALUES (...)— указываем соответствующие значения -
Значения должны соответствовать типам данных столбцов
-
employee_id не указываем — он генерируется автоматически
Проверка результата:
SELECT * FROM employees WHERE email =Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ';
Способ 2: Использование значений по умолчанию
Пример 2: Вставка без указания некоторых полей
INSERT INTO employees ( first_name, last_name, email, hire_date, salary ) VALUES ( 'Мария', 'Сидорова',Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ', '2024-02-20', 68000.00 );
Что происходит:
-
Поля is_active и created_at получают значения по умолчанию
-
is_active = TRUE (значение по умолчанию)
-
created_at = текущая дата и время
Эквивалентная запись с использованием DEFAULT:
INSERT INTO employees ( first_name, last_name, email, hire_date, salary, is_active ) VALUES ( 'Мария', 'Сидорова',Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ', '2024-02-20', 68000.00, DEFAULT -- Используем значение по умолчанию для поля is_active );
Способ 3: Вставка нескольких записей одновременно
Пример 3: Добавление нескольких сотрудников за один запрос
INSERT INTO employees ( first_name, last_name, email, hire_date, salary ) VALUES ('Алексей', 'Кузнецов',Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ', '2024-03-10', 82000.00), ('Ольга', 'Васильева',Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ', '2024-03-12', 71000.00), ('Дмитрий', 'Смирнов',Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ', '2024-03-15', 89000.00), ('Екатерина', 'Попова',Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ', '2024-03-18', 76000.00);
Преимущества:
-
Быстрее, чем несколько отдельных INSERT-запросов
-
Атомарность — либо все записи добавятся, либо ни одна
-
Эффективно для загрузки больших объемов данных
Статистика выполнения:
После выполнения такого запроса PostgreSQL вернет сообщение:
INSERT 0 4 -- Успешно вставлено 4 записи
Способ 4: Вставка данных из результата SELECT
Пример 4: Копирование данных из другой таблицы
Предположим, у нас есть временная таблица new_employees с новыми сотрудниками:
-- Создаем временную таблицу CREATE TEMP TABLE new_employees ( emp_name VARCHAR(50), emp_surname VARCHAR(50), emp_email VARCHAR(100), start_date DATE, emp_salary DECIMAL(10, 2) ); -- Добавляем данные во временную таблицу INSERT INTO new_employees VALUES ('Сергей', 'Морозов',Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ', '2024-04-01', 73000.00), ('Анна', 'Николаева',Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ', '2024-04-05', 69000.00);
Вставка данных из временной таблицы:
INSERT INTO employees ( first_name, last_name, email, hire_date, salary ) SELECT emp_name, emp_surname, emp_email, start_date, emp_salary FROM new_employees;
Более сложный пример с преобразованием данных:
INSERT INTO employees ( first_name, last_name, email, hire_date, salary, is_active ) SELECT INITCAP(lower_name), -- Преобразуем имя с заглавной буквы INITCAP(lower_surname), -- Преобразуем фамилию lower_email ||Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ', -- Добавляем домен CURRENT_DATE, -- Используем текущую дату base_salary * 1.1, -- Увеличиваем зарплату на 10% TRUE FROM external_data_source WHERE department = 'IT';
Использование pgAdmin для генерации INSERT-запросов
Шаг 1: Открытие Query Tool
-
Запустите pgAdmin
-
В левой панели найдите вашу базу данных
-
Разверните Schemas → public → Tables
-
Найдите таблицу employees
-
Правой кнопкой мыши → Query Tool
Шаг 2: Генерация INSERT-запроса
-
Правой кнопкой мыши по таблице employees
-
Выберите Scripts → INSERT Script
-
В окне Query Tool появится шаблон:
INSERT INTO public.employees( first_name, last_name, email, hire_date, salary, is_active) VALUES (?, ?, ?, ?, ?, ?);
Шаг 3: Заполнение значений
Замените ? на реальные значения:
INSERT INTO public.employees( first_name, last_name, email, hire_date, salary, is_active) VALUES ('Иван', 'Петров',Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ', '2024-01-01', 50000, true);
Обработка конфликтов при вставке
Пример 5: Вставка с обработкой дубликатов
INSERT INTO employees ( first_name, last_name, email, hire_date, salary ) VALUES ( 'Иван', 'Петров',Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ', -- Предположим, этот email уже существует '2024-05-01', 80000.00 ) ON CONFLICT (email) -- Что делать при конфликте по email DO NOTHING; -- Ничего не делать (пропустить запись)
Пример 6: Вставка с обновлением при конфликте
INSERT INTO employees ( employee_id, first_name, last_name, email, hire_date, salary ) VALUES ( 100, -- Предполагаемый ID 'Иван', 'Петров',Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ', '2024-05-01', 80000.00 ) ON CONFLICT (employee_id) -- Конфликт по ID DO UPDATE SET salary = EXCLUDED.salary, -- Обновляем зарплату hire_date = EXCLUDED.hire_date;
Продвинутые техники вставки данных
Техника 1: Вставка с возвратом данных
INSERT INTO employees ( first_name, last_name, email, hire_date, salary ) VALUES ( 'Павел', 'Федоров',Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ', '2024-06-01', 85000.00 ) RETURNING employee_id, first_name, last_name, email;
Результат:
employee_id | first_name | last_name | email
-------------+------------+-----------+--------------------------------
101 | Павел | Федоров | Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.
Техника 2: Вставка с использованием CTE (Common Table Expressions)
WITH new_employee AS ( INSERT INTO employees ( first_name, last_name, email, hire_date, salary ) VALUES ( 'Татьяна', 'Орлова',Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ', '2024-06-15', 78000.00 ) RETURNING employee_id ) INSERT INTO position_history ( employee_id, position_id, date_start ) SELECT employee_id, 3, -- ID должности CURRENT_DATE FROM new_employee;
Техника 3: Вставка данных из JSON
INSERT INTO employees ( first_name, last_name, email, hire_date, salary ) SELECT data->>'first_name', data->>'last_name', data->>'email', (data->>'hire_date')::DATE, (data->>'salary')::DECIMAL FROM jsonb_array_elements('[{ "first_name": "Андрей", "last_name": "Белов", "email": "Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ", "hire_date": "2024-07-01", "salary": 72000 }, { "first_name": "Наталья", "last_name": "Краснова", "email": "Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ", "hire_date": "2024-07-05", "salary": 81000 }]') AS data;
Практические примеры из реальной жизни
Пример 7: Массовая загрузка сотрудников из CSV
-- Создаем временную таблицу CREATE TEMP TABLE temp_employees ( first_name VARCHAR(50), last_name VARCHAR(50), email VARCHAR(100), hire_date DATE, salary DECIMAL(10, 2), department VARCHAR(50) ); -- Копируем данные из CSV COPY temp_employees FROM '/path/to/employees.csv' DELIMITER ',' CSV HEADER; -- Вставляем в основную таблицу INSERT INTO employees ( first_name, last_name, email, hire_date, salary ) SELECT first_name, last_name, email, hire_date, salary FROM temp_employees WHERE department = 'Development'; -- Очищаем временную таблицу DROP TABLE temp_employees;
Пример 8: Импорт данных с преобразованием
INSERT INTO employees ( first_name, last_name, email, hire_date, salary, is_active ) SELECT INITCAP(lower(first_name_raw)), INITCAP(lower(last_name_raw)), LOWER(email_raw), TO_DATE(hire_date_raw, 'DD.MM.YYYY'), CAST(REPLACE(salary_raw, ',', '.') AS DECIMAL), CASE WHEN status_raw = 'Активен' THEN TRUE ELSE FALSE END FROM external_system_data WHERE import_date = CURRENT_DATE;
Обработка ошибок и валидация данных
Пример 9: Вставка с проверкой ограничений
-- Создаем функцию для безопасной вставки CREATE OR REPLACE FUNCTION safe_insert_employee( p_first_name VARCHAR, p_last_name VARCHAR, p_email VARCHAR, p_hire_date DATE, p_salary DECIMAL ) RETURNS INTEGER AS $$ DECLARE v_employee_id INTEGER; BEGIN -- Проверяем, существует ли уже email IF EXISTS (SELECT 1 FROM employees WHERE email = p_email) THEN RAISE EXCEPTION 'Email % уже существует', p_email; END IF; -- Проверяем корректность даты IF p_hire_date > CURRENT_DATE THEN RAISE EXCEPTION 'Дата приема не может быть в будущем'; END IF; -- Проверяем зарплату IF p_salary <= 0 THEN RAISE EXCEPTION 'Зарплата должна быть положительной'; END IF; -- Выполняем вставку INSERT INTO employees ( first_name, last_name, email, hire_date, salary ) VALUES ( p_first_name, p_last_name, p_email, p_hire_date, p_salary ) RETURNING employee_id INTO v_employee_id; RETURN v_employee_id; END; $$ LANGUAGE plpgsql; -- Используем функцию SELECT safe_insert_employee( 'Александр', 'Григорьев',Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ', '2024-08-01', 95000.00 );
Оптимизация производительности при вставке больших объемов данных
Рекомендация 1: Отключаем индексы временно
-- Отключаем индексы перед массовой вставкой DROP INDEX IF EXISTS idx_employees_email; DROP INDEX IF EXISTS idx_employees_name; -- Выполняем массовую вставку INSERT INTO employees (...) VALUES (...), (...), ...; -- Восстанавливаем индексы CREATE INDEX idx_employees_email ON employees(email); CREATE INDEX idx_employees_name ON employees(first_name, last_name);
Рекомендация 2: Используем COPY для больших объемов
-- Копирование из CSV COPY employees(first_name, last_name, email, hire_date, salary) FROM '/path/to/large_file.csv' DELIMITER ',' CSV HEADER;
Рекомендация 3: Пакетная вставка с транзакциями
BEGIN; -- Начало транзакции -- Первый пакет INSERT INTO employees (...) VALUES (...); -- Второй пакет INSERT INTO employees (...) VALUES (...); COMMIT; -- Фиксация транзакции
Лучшие практики работы с INSERT
1. Всегда указывайте имена столбцов
Неправильно:
INSERT INTO employees VALUES (...);
Правильно:
INSERT INTO employees (first_name, last_name, email, ...) VALUES (...);
2. Используйте транзакции для связанных операций
BEGIN; INSERT INTO employees (...) VALUES (...); INSERT INTO position_history (...) VALUES (...); COMMIT;
3. Проверяйте уникальность данных
INSERT INTO employees (...) SELECT ... FROM source_data WHERE NOT EXISTS ( SELECT 1 FROM employees WHERE email = source_data.email );
4. Логируйте операции вставки
CREATE TABLE insert_audit ( audit_id SERIAL PRIMARY KEY, table_name VARCHAR(100), operation VARCHAR(10), new_data JSONB, executed_by VARCHAR(100), executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Триггер для логирования CREATE OR REPLACE FUNCTION log_insert() RETURNS TRIGGER AS $$ BEGIN INSERT INTO insert_audit (table_name, operation, new_data, executed_by) VALUES (TG_TABLE_NAME, TG_OP, row_to_json(NEW), current_user); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trg_log_employees_insert AFTER INSERT ON employees FOR EACH ROW EXECUTE FUNCTION log_insert();
Мониторинг и отладка операций INSERT
Просмотр последних вставленных записей
SELECT * FROM employees ORDER BY created_at DESC LIMIT 10;
Анализ производительности
-- Статистика по времени выполнения INSERT SELECT query, calls, total_time, mean_time, rows FROM pg_stat_statements WHERE query LIKE 'INSERT%employees%' ORDER BY total_time DESC;
Отслеживание ошибок
-- Создание таблицы для ошибок CREATE TABLE insert_errors ( error_id SERIAL PRIMARY KEY, error_message TEXT, sql_command TEXT, error_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Функция для безопасной вставки с логированием ошибок CREATE OR REPLACE FUNCTION safe_insert_with_logging(...) RETURNS INTEGER AS $$ BEGIN INSERT INTO employees (...) VALUES (...); RETURN 1; EXCEPTION WHEN OTHERS THEN INSERT INTO insert_errors (error_message, sql_command) VALUES (SQLERRM, current_query()); RETURN 0; END; $$ LANGUAGE plpgsql;
Заключение
Оператор INSERT в PostgreSQL — это гибкий оператор для добавления данных в таблицы. Освоив различные способы его использования, вы сможете:
-
Эффективно добавлять одиночные и множественные записи
-
Обрабатывать конфликты и дубликаты данных
-
Выполнять массовые вставки с оптимальной производительностью
-
Интегрировать данные из различных источников
-
Обеспечивать целостность данных через валидацию и транзакции
Начните с простых примеров, постепенно осваивая более сложные техники. Помните, что правильное использование INSERT — это основа эффективной работы с любой базой данных.
В следующих материалах мы рассмотрим оператор UPDATE для изменения существующих записей и научимся создавать триггеры для автоматизации бизнес-логики.
Статья подготовлена по материалам обучающего видео для вашего удобства.