Работа с базами данных всегда начинается с добавления данных. В PostgreSQL оператор INSERT является основным инструментом для вставки новых записей в таблицы. В этой статье мы подробно разберем все способы использования INSERT — от базовых примеров до продвинутых техник вставки данных.

Подготовка: структура таблицы employees

Прежде чем начать добавлять данные, давайте вспомним структуру таблицы, с которой будем работать:

sql
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: Явное указание всех значений
sql
INSERT INTO employees ( first_name, last_name, email, hire_date, salary, is_active ) VALUES ( 'Иван', 'Петров', Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.', '2024-01-15', 75000.00, TRUE );
Разбор команды:
  1. INSERT INTO employees — указываем, в какую таблицу вставляем данные

  2. (first_name, last_name, ...) — перечисляем названия столбцов

  3. VALUES (...) — указываем соответствующие значения

  4. Значения должны соответствовать типам данных столбцов

  5. employee_id не указываем — он генерируется автоматически

Проверка результата:
sql
SELECT * FROM employees WHERE email = Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.';
Способ 2: Использование значений по умолчанию
Пример 2: Вставка без указания некоторых полей
sql
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:
sql
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: Добавление нескольких сотрудников за один запрос
sql
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 вернет сообщение:

text
INSERT 0 4  -- Успешно вставлено 4 записи
Способ 4: Вставка данных из результата SELECT
Пример 4: Копирование данных из другой таблицы

Предположим, у нас есть временная таблица new_employees с новыми сотрудниками:

sql
-- Создаем временную таблицу
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);
Вставка данных из временной таблицы:
sql
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;
Более сложный пример с преобразованием данных:
sql
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
  1. Запустите pgAdmin

  2. В левой панели найдите вашу базу данных

  3. Разверните Schemas → public → Tables

  4. Найдите таблицу employees

  5. Правой кнопкой мыши → Query Tool

Шаг 2: Генерация INSERT-запроса
  1. Правой кнопкой мыши по таблице employees

  2. Выберите Scripts → INSERT Script

  3. В окне Query Tool появится шаблон:

sql
INSERT INTO public.employees(
    first_name, last_name, email, hire_date, salary, is_active)
VALUES (?, ?, ?, ?, ?, ?);
Шаг 3: Заполнение значений

Замените ? на реальные значения:

sql
INSERT INTO public.employees(
    first_name, last_name, email, hire_date, salary, is_active)
VALUES ('Иван', 'Петров', Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.', '2024-01-01', 50000, true);
Обработка конфликтов при вставке
Пример 5: Вставка с обработкой дубликатов
sql
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: Вставка с обновлением при конфликте
sql
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: Вставка с возвратом данных
sql
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;

Результат:

text
 employee_id | first_name | last_name | email
-------------+------------+-----------+--------------------------------
         101 | Павел      | Федоров   | Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.
Техника 2: Вставка с использованием CTE (Common Table Expressions)
sql
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
sql
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
sql
-- Создаем временную таблицу
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: Импорт данных с преобразованием
sql
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: Вставка с проверкой ограничений
sql
-- Создаем функцию для безопасной вставки
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: Отключаем индексы временно
sql
-- Отключаем индексы перед массовой вставкой
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 для больших объемов
sql
-- Копирование из CSV
COPY employees(first_name, last_name, email, hire_date, salary)
FROM '/path/to/large_file.csv'
DELIMITER ',' CSV HEADER;
Рекомендация 3: Пакетная вставка с транзакциями
sql
BEGIN; -- Начало транзакции -- Первый пакет INSERT INTO employees (...) VALUES (...); -- Второй пакет INSERT INTO employees (...) VALUES (...); COMMIT; -- Фиксация транзакции
Лучшие практики работы с INSERT
1. Всегда указывайте имена столбцов

Неправильно:

sql
INSERT INTO employees VALUES (...);

Правильно:

sql
INSERT INTO employees (first_name, last_name, email, ...) VALUES (...);
2. Используйте транзакции для связанных операций
sql
BEGIN;
INSERT INTO employees (...) VALUES (...);
INSERT INTO position_history (...) VALUES (...);
COMMIT;
3. Проверяйте уникальность данных
sql
INSERT INTO employees (...)
SELECT ... FROM source_data
WHERE NOT EXISTS (
    SELECT 1 FROM employees 
    WHERE email = source_data.email
);
4. Логируйте операции вставки
sql
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
Просмотр последних вставленных записей
sql
SELECT * FROM employees 
ORDER BY created_at DESC 
LIMIT 10;
Анализ производительности
sql
-- Статистика по времени выполнения INSERT
SELECT 
    query,
    calls,
    total_time,
    mean_time,
    rows
FROM pg_stat_statements
WHERE query LIKE 'INSERT%employees%'
ORDER BY total_time DESC;
Отслеживание ошибок
sql
-- Создание таблицы для ошибок
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 — это гибкий оператор для добавления данных в таблицы. Освоив различные способы его использования, вы сможете:

  1. Эффективно добавлять одиночные и множественные записи

  2. Обрабатывать конфликты и дубликаты данных

  3. Выполнять массовые вставки с оптимальной производительностью

  4. Интегрировать данные из различных источников

  5. Обеспечивать целостность данных через валидацию и транзакции

Начните с простых примеров, постепенно осваивая более сложные техники. Помните, что правильное использование INSERT — это основа эффективной работы с любой базой данных.

В следующих материалах мы рассмотрим оператор UPDATE для изменения существующих записей и научимся создавать триггеры для автоматизации бизнес-логики.

Picture background

Статья подготовлена по материалам обучающего видео для вашего удобства.