На основе примера: Получаем актуальные данные о сотрудниках и должностях

В этой статье мы разберем практический пример SQL-запроса, который извлекает данные о сотрудниках и их должностях с использованием оператора LEFT JOIN. Вы научитесь правильно соединять таблицы, фильтровать данные по датам и учитывать статусы записей для получения только актуальной информации.

Структура таблиц и их взаимосвязи

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

  1. Таблица employees — содержит основные данные о сотрудниках:

    • Идентификатор сотрудника

    • Фамилия и имя

    • Электронная почта

    • Статус активности (is_active)

  2. Таблица positions — справочник должностей:

    • Идентификатор должности

    • Название должности

    • Статус активности позиции

  3. Таблица positions_by_date — связующая таблица, которая определяет:

    • Какой сотрудник (employee_id)

    • В какой должности (position_id)

    • Находился/находится в определенный период (date_start, date_finish)

Пошаговый разбор SQL-запроса

Шаг 1: Определение структуры выборки (SELECT)

Начинаем с определения, какие данные мы хотим получить:

sql
SELECT 
    p.position_name AS "Должность",
    CONCAT(e.last_name, ' ', e.first_name) AS "ФИО сотрудника",
    e.email AS "Email"

Что здесь происходит:

  • Выбираем название должности из таблицы positions

  • Объединяем фамилию и имя сотрудника в одно поле через пробел

  • Добавляем email сотрудника

  • Используем алиасы (p для positions, e для employees) для удобства

Шаг 2: Первый LEFT JOIN — присоединение таблицы назначений

sql
FROM positions p
LEFT JOIN positions_by_date pbd ON p.id = pbd.position_id

Ключевой момент:
Мы начинаем с таблицы должностей (positions) как основной (левой таблицы). LEFT JOIN гарантирует, что в результат попадут ВСЕ должности, даже если на них в данный момент никто не назначен.

Шаг 3: Второй LEFT JOIN — присоединение данных о сотрудниках

sql
LEFT JOIN employees e ON pbd.employee_id = e.id

Важно: Этот JOIN также использует LEFT JOIN, что позволяет сохранить в результате все должности из предыдущего шага, даже если для них нет сотрудника в таблице назначений.

Шаг 4: Фильтрация по актуальным периодам (WHERE)

sql
WHERE pbd.date_start <= CURRENT_DATE 
  AND (pbd.date_finish IS NULL OR pbd.date_finish > CURRENT_DATE)

Эта часть запроса отвечает за актуальность данных:

  • date_start <= CURRENT_DATE — должность уже началась

  • date_finish > CURRENT_DATE — должность еще не закончилась

  • OR date_finish IS NULL — учитывает бессрочные назначения

Шаг 5: Учет активности сотрудников и должностей

sql
  AND e.is_active = true
  AND p.is_active = true

Дополнительные условия, которые исключают из результатов:

  • Уволенных сотрудников (is_active = false)

  • Удаленные или неактивные должности

Полный запрос

sql
SELECT 
    p.position_name AS "Должность",
    CONCAT(e.last_name, ' ', e.first_name) AS "ФИО сотрудника",
    e.email AS "Email"
FROM positions p
LEFT JOIN positions_by_date pbd ON p.id = pbd.position_id
LEFT JOIN employees e ON pbd.employee_id = e.id
WHERE pbd.date_start <= CURRENT_DATE 
  AND (pbd.date_finish IS NULL OR pbd.date_finish > CURRENT_DATE)
  AND e.is_active = true
  AND p.is_active = true;

Что мы получим в результате?

Типичные сценарии вывода:

  1. Должность с назначенным активным сотрудником:

    text
    Должность: Senior Developer
    ФИО сотрудника: Иванов Иван
    Email: Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.
  2. Вакантная должность (есть должность, но нет сотрудника):

    text
    Должность: Project Manager
    ФИО сотрудника: NULL
    Email: NULL
  3. Записи, которые НЕ попадут в результат:

    • Должность с уволенным сотрудником

    • Неактивная (удаленная) должность

    • Назначения, период которых еще не начался или уже завершился

Когда использовать LEFT JOIN?

LEFT JOIN применяется в ситуациях, когда необходимо:

  • Получить ВСЕ записи из основной таблицы, даже если в связанных таблицах нет соответствующих данных

  • Проанализировать заполненность данных (например, найти вакантные должности)

  • Сохранить целостность данных при наличии необязательных связей

Практическая ценность запроса

Данный запрос решает несколько важных бизнес-задач:

  1. Актуальный штатный состав — всегда видно, какие должности заняты, а какие вакантны

  2. Контроль периодов работы — автоматическое исключение исторических данных

  3. Учет статусов — фильтрация только по действующим сотрудникам и должностям

  4. Подготовка данных для отчетов — структурированная информация для кадрового учета

Рекомендации по использованию

  1. Для повышения производительности создайте индексы на полях, используемых в JOIN и WHERE:

    • positions_by_date.position_id

    • positions_by_date.employee_id

    • positions_by_date.date_start, positions_by_date.date_finish

  2. Для расширения функциональности можно добавить:

    • Сортировку по названию должности или ФИО

    • Группировку для статистики (сколько должностей вакантно)

    • Дополнительные поля (дата найма, отдел)

  3. Для отладки временно убирайте условия WHERE, чтобы увидеть полную картину данных.

Этот запрос — отличная основа для построения различных отчетов в системе управления персоналом. Он демонстрирует, как правильно комбинировать JOIN-операции с фильтрацией по датам и статусам для получения бизнес-значимых результатов.

Чтобы увидеть запрос в действии, с пошаговым выполнением и визуализацией результатов каждого этапа, посмотрите наше видео-руководство. В нем мы наглядно показываем, как изменяется результат при добавлении каждого условия, и разбираем типичные ошибки, которые допускают новички при работе с LEFT JOIN.


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