При работе с запросами в 1С часто возникает задача: вывести список основных объектов (например, физических лиц) и дополнить их данными из табличных частей (например, сведениями об образовании или составе семьи). Однако сложность заключается в том, что в табличной части может быть много строк, а в отчет нужно вывести только одну — самую первую или актуальную. Попытка использовать конструкцию ПЕРВЫЕ 1 внутри пакета запросов часто приводит к ошибкам, когда вместо одной строки на каждого человека система выдает одну строку на весь запрос. В этой статье мы разберем, как правильно решить эту задачу, используя примеры из реальной практики разработчиков.
Рассмотрим типичную ошибку начинающего разработчика. Проанализируем ситуацию: когда мы пишем ВЫБРАТЬ ПЕРВЫЕ 1 во временной таблице, которая соединяется с основной таблицей, язык запросов 1С ограничивает весь результат этой временной таблицы одной записью. Если у нас в списке 100 сотрудников, то после такого запроса во временной таблице останется информация только по одному (случайному) сотруднику. Соединение в финальном запросе просто не найдет данных для остальных 99 человек.
Для решения этой проблемы нам нужно использовать методы, которые позволяют ограничить выборку индивидуально для каждого владельца табличной части.
Самый простой и быстрый способ получить первую запись — это использование поля НомерСтроки. В 1С каждая строка табличной части имеет уникальный порядковый номер в пределах своего владельца. Разберем, как это реализовать на практике.
Предположим, нам нужно получить список физических лиц и их первое указанное образование. Посмотрим на код:
ВЫБРАТЬ
ФизическиеЛица.Ссылка КАК ФизЛицо,
ФизическиеЛица.ФИО
ПОМЕСТИТЬ ВТ_Сотрудники
ИЗ
Справочник.ФизическиеЛица КАК ФизическиеЛица
ГДЕ
ФизическиеЛица.ЭтоГруппа = ЛОЖЬ;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Образование.Ссылка КАК Ссылка,
Образование.ВидОбразования,
Образование.УчебноеЗаведение
ПОМЕСТИТЬ ВТ_ПервоеОбразование
ИЗ
Справочник.ФизическиеЛица.Образование КАК Образование
ГДЕ
Образование.НомерСтроки = 1;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_Сотрудники.ФИО,
ВТ_ПервоеОбразование.ВидОбразования,
ВТ_ПервоеОбразование.УчебноеЗаведение
ИЗ
ВТ_Сотрудники КАК ВТ_Сотрудники
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ПервоеОбразование КАК ВТ_ПервоеОбразование
ПО ВТ_Сотрудники.ФизЛицо = ВТ_ПервоеОбразование.Ссылка
Важный нюанс: этот метод эффективен, если вы уверены, что первая строка (где НомерСтроки = 1) всегда существует и содержит нужные данные. Однако, если первая строка была удалена программно или нумерация сбита, этот фильтр может не сработать. Для более надежных систем рекомендуется второй метод.
Если нам нужно выбрать не просто первую по порядку строку, а, например, самое последнее образование или родственника с максимальной датой рождения, мы используем группировку. Рассмотрим этот алгоритм по шагам:
Ссылка) и минимальный или максимальный показатель (например, МИНИМУМ(НомерСтроки) или МАКСИМУМ(ДатаРождения)).Проанализируем пример получения данных о семье с использованием этого подхода:
// Шаг 1: Получаем минимальный номер строки для каждого физлица
ВЫБРАТЬ
СоставСемьи.Ссылка КАК ФизЛицо,
МИНИМУМ(СоставСемьи.НомерСтроки) КАК НомерПервойСтроки
ПОМЕСТИТЬ ВТ_ИндексыСемьи
ИЗ
Справочник.ФизическиеЛица.СоставСемьи КАК СоставСемьи
СГРУППИРОВАТЬ ПО
СоставСемьи.Ссылка;
////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Получаем данные конкретно по этим строкам
ВЫБРАТЬ
СоставСемьи.Ссылка КАК Ссылка,
СоставСемьи.СтепеньРодства,
СоставСемьи.Имя
ПОМЕСТИТЬ ВТ_ДанныеСемьи
ИЗ
ВТ_ИндексыСемьи КАК Индексы
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ФизическиеЛица.СоставСемьи КАК СоставСемьи
ПО Индексы.ФизЛицо = СоставСемьи.Ссылка
И Индексы.НомерПервойСтроки = СоставСемьи.НомерСтроки;
Этот метод считается "золотым стандартом" в разработке на 1С, так как он гарантирует получение ровно одной записи для каждого объекта, даже если в ТЧ сотни строк.
Выясним причину, почему иногда запрос начинает выдавать огромное количество повторяющихся строк ("кашу"). Это происходит из-за декартова произведения. Если вы во временной таблице ВТ_Образование не ограничили количество строк до одной на сотрудника, а потом просто соединили таблицы, система выведет все возможные комбинации.
Никогда не делайте так:
ИЗ
ВТ_Сотрудники КАК ВТ_Сотрудники,
ВТ_Семья КАК ВТ_Семья,
ВТ_Образ КАК ВТ_Образ
Такой синтаксис (через запятую) в 1С означает соединение "каждый с каждым". Всегда используйте ЛЕВОЕ СОЕДИНЕНИЕ с четким условием связи по Ссылке.
Посмотрим на вопрос производительности. Если в справочнике "Физические лица" десятки тысяч записей, соединения временных таблиц будут работать медленно. Чтобы ускорить процесс, необходимо индексировать поля связи.
Добавим инструкцию индексации в наши временные таблицы:
ПОМЕСТИТЬ ВТ_ОбщееФизЛица
ИЗ
Справочник.ФизическиеЛица КАК ФизическиеЛица
ГДЕ
...
ИНДЕКСИРОВАТЬ ПО
ФИО // ФИО здесь - это поле Ссылка
;
Индексирование по полю связи позволяет СУБД мгновенно находить соответствия, что сокращает время выполнения запроса с нескольких минут до секунд.
Подведем итоги. Чтобы вывести по одной записи из табличной части в запросе 1С, мы рекомендуем использовать следующий алгоритм:
ЛЕВОЕ СОЕДИНЕНИЕ, чтобы не потерять основных владельцев (сотрудников), у которых табличные части могут быть не заполнены.ГДЕ НомерСтроки = 1, либо предварительную группировку по МИНИМУМ(НомерСтроки).ПЕРВЫЕ 1, если в выборке должно присутствовать более одного владельца.Следуя этим правилам, вы сможете строить сложные и эффективные отчеты в таких конфигурациях, как ЗУП 2.5, УПП или ERP, где данные распределены по множеству вложенных таблиц.