Как в запросе 1С найти ответственного сотрудника на дату релиза программного продукта

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В практике разработки на платформе 1С часто возникают задачи, когда необходимо сопоставить данные из двух независимых источников, основываясь на временных метках. Типичный пример — определение ответственного лица, работающего в компании на момент выхода определенной версии ПО, когда у нас есть только дата приема сотрудника и дата релиза. Разберем, как правильно реализовать такую логику одним запросом, и проанализируем технические нюансы работы с датами.

Анализ проблемы и постановка задачи

Рассмотрим ситуацию: у нас есть два непериодических регистра сведений. В первом (Платформы) хранятся названия программных продуктов и даты их выпуска. Во втором (ОтветственныеЛица) зафиксированы даты приема сотрудников. Сложность заключается в том, что в таблице ответственных нет даты увольнения, а значит, сотрудник считается ответственным с момента приема и до тех пор, пока не будет принят следующий специалист.

Для решения этой задачи нам необходимо имитировать механизм "Среза последних" для непериодических данных. Нам нужно для каждой платформы найти такую запись в таблице ответственных, у которой ДатаПриема максимально близка к ДатаРелиза, но не превышает ее.

Способ 1: Метод группировки и поиска максимальной даты

Этот метод считается классическим для языка запросов 1С. Он наиболее читаем и эффективно использует индексы таблиц. Мы разделим логику на три этапа: получение всех возможных кандидатов, поиск самой актуальной даты приема и финальная выборка ФИО.

Проанализируем шаги реализации:

  1. Создадим временную таблицу со всеми возможными парами "Платформа — Дата приема", где дата приема меньше или равна дате релиза.
  2. Сгруппируем этот набор данных, чтобы оставить для каждой платформы только одну, самую позднюю дату приема.
  3. Соединим результат с исходной таблицей ответственных, чтобы получить строковое представление ФИО.

Рассмотрим пример кода, реализующий этот алгоритм:


// Шаг 1 и 2: Получаем таблицу Платформа - АктуальнаяДатаПриема
ВЫБРАТЬ
    ДатыПлатформ.Название КАК Платформа,
    МАКСИМУМ(Ответственные.ДатаПриема) КАК ДатаПриема
ПОМЕСТИТЬ ВТ_АктуальныеДаты
ИЗ
    РегистрСведений.Платформы КАК ДатыПлатформ
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ОтветственныеЛица КАК Ответственные
        ПО ДатыПлатформ.ДатаРелиза >= Ответственные.ДатаПриема
СГРУППИРОВАТЬ ПО
    ДатыПлатформ.Название;

////////////////////////////////////////////////////////////////////////////////
// Шаг 3: Получаем итоговые данные с ФИО
ВЫБРАТЬ
    ВТ_АктуальныеДаты.Платформа КАК Платформа,
    Ответственные.ФИО КАК Ответственный
ИЗ
    ВТ_АктуальныеДаты КАК ВТ_АктуальныеДаты
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ОтветственныеЛица КАК Ответственные
        ПО ВТ_АктуальныеДаты.ДатаПриема = Ответственные.ДатаПриема

В данном решении мы используем ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Обратите внимание: если релиз платформы состоялся раньше, чем был принят первый сотрудник, такая платформа не попадет в выборку. Если вам нужно вывести все платформы (даже без ответственных), замените соединение на ЛЕВОЕ СОЕДИНЕНИЕ и используйте функцию ЕСТЬNULL(Ответственные.ФИО, "Не назначен").

Способ 2: Интервальный подход

Иногда удобнее сначала определить "период действия" каждой записи сотрудника. Мы можем вычислить для каждого ответственного не только дату начала работы, но и дату ее окончания (момент приема следующего сотрудника). Рассмотрим, как это сделать программно в запросе.

Выясним причину, почему этот способ может быть полезен: он позволяет избежать многократных соединений в финальной части запроса, если нужно проверять вхождение даты в интервал (условие МЕЖДУ).

Пример формирования интервалов:


ВЫБРАТЬ
    Слева.ФИО КАК ФИО,
    Слева.ДатаПриема КАК НачалоИнтервала,
    МИНИМУМ(ЕСТЬNULL(Справа.ДатаПриема, ДАТАВРЕМЯ(3999, 12, 31))) КАК КонецИнтервала
ПОМЕСТИТЬ ВТ_ИнтервалыРаботы
ИЗ
    РегистрСведений.ОтветственныеЛица КАК Слева
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ОтветственныеЛица КАК Справа
        ПО Слева.ДатаПриема < Справа.ДатаПриема
СГРУППИРОВАТЬ ПО
    Слева.ФИО,
    Слева.ДатаПриема;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Платформы.Название КАК Платформа,
    ВТ_ИнтервалыРаботы.ФИО КАК Ответственный
ИЗ
    РегистрСведений.Платформы КАК Платформы
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ИнтервалыРаботы КАК ВТ_ИнтервалыРаботы
        ПО (Платформы.ДатаРелиза >= ВТ_ИнтервалыРаботы.НачалоИнтервала)
            И (Платформы.ДатаРелиза < ВТ_ИнтервалыРаботы.КонецИнтервала)

В этом примере мы используем коррелированное соединение таблицы самой с собой. Для каждой записи сотрудника мы ищем минимальную дату среди всех дат приема, которые больше текущей. Если такой даты нет (это последний принятый сотрудник), мы устанавливаем "бесконечную" дату — 31 декабря 3999 года.

Важные технические нюансы

При работе с подобными запросами в 1С необходимо учитывать несколько критических моментов, которые могут повлиять на корректность данных:

  1. Точность времени в датах. В 1С тип Дата всегда включает время. Даже если в конфигураторе установлено "Состав даты: Дата", на уровне СУБД время хранится как 00:00:00. Если в один из регистров попадет дата со временем 12:00:00, а в другой — 00:00:00 того же дня, простое сравнение может дать неверный результат. Рекомендуется использовать функцию НАЧАЛОПЕРИОДА(Дата, ДЕНЬ) для приведения всех значений к единому моменту времени.
  2. Проблема коллизий. Что произойдет, если два сотрудника приняты в один день? В методе с МАКСИМУМ(ДатаПриема) запрос вернет обе фамилии для одной платформы, что приведет к дублированию строк в итоговой таблице. Чтобы этого избежать, структура данных должна содержать дополнительный уникальный ключ (например, время приема или номер приказа), который также нужно будет использовать в группировке.
  3. Производительность и индексы. Поля ДатаРелиза и ДатаПриема должны быть индексированы. В регистрах сведений измерения индексируются автоматически, а ресурсы — нет. Если дата в вашем регистре является ресурсом, скорость выполнения запроса на больших таблицах существенно снизится.
  4. Обработка пустых значений. Всегда используйте ЕСТЬNULL при левых соединениях. Это гарантирует, что ваша таблица значений на форме будет заполнена корректными строками, а не значениями NULL, которые могут вызвать ошибки в коде при дальнейшей обработке.

Подведем итог: для решения задачи сопоставления дат "в лоб" лучше всего подходит Способ №1 с использованием временной таблицы и агрегатной функции МАКСИМУМ. Он является стандартом разработки в среде 1С и гарантирует предсказуемый результат при соблюдении уникальности дат.

← На главную