Как объединить данные из разных табличных частей документа в одну строку запроса 1С

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

В практике разработки на платформе 1С:Предприятие 8.3 часто возникает специфическая задача: необходимо получить данные из двух или более табличных частей одного документа таким образом, чтобы они отображались не последовательно (одна под другой), а параллельно (в одной строке). Например, если в документе Рейс есть табличная часть Затраты со способом распределения "По весу" и табличная часть Услуги со способом распределения "По объему", стандартное использование оператора ОБЪЕДИНИТЬ выдаст две разные строки. Наша задача — "схлопнуть" их в одну.

Рассмотрим пошагово, как реализовать такую логику в языке запросов 1С, проанализируем различные подходы и выберем наиболее эффективные в зависимости от структуры данных.

Метод 1: Использование вложенного запроса и агрегатной функции МАКСИМУМ

Проанализируем ситуацию, описанную автором темы. Основная проблема оператора ОБЪЕДИНИТЬ ВСЕ заключается в том, что он просто добавляет записи из второй таблицы в конец первой. Чтобы объединить эти записи по полю Ссылка, мы должны применить группировку. Разберем этот алгоритм.

  1. Создадим вложенный запрос, где для каждой табличной части выберем ссылку на документ и нужные нам поля.
  2. В тех колонках, которых нет в текущей табличной части, проставим значение NULL (или пустую ссылку соответствующего типа).
  3. Во внешнем запросе выполним группировку по ссылке и применим функцию МАКСИМУМ к значимым полям.

Посмотрим на пример кода, который решает задачу:


ВЫБРАТЬ
    ВложенныйЗапрос.Ссылка КАК Ссылка,
    МАКСИМУМ(ВложенныйЗапрос.СпособЗатраты) КАК СпособЗатраты,
    МАКСИМУМ(ВложенныйЗапрос.СпособУслуги) КАК СпособУслуги
ИЗ
    (ВЫБРАТЬ
        ТСРейсЗатраты.Ссылка КАК Ссылка,
        ТСРейсЗатраты.СпособРаспределения КАК СпособЗатраты,
        ЗНАЧЕНИЕ(Перечисление.СпособыРаспределения.ПустаяСсылка) КАК СпособУслуги
    ИЗ
        Документ.Рейс.Затраты КАК ТСРейсЗатраты
    
    ОБЪЕДИНИТЬ ВСЕ
    
    ВЫБРАТЬ
        ТЧРейсУслуги.Ссылка,
        ЗНАЧЕНИЕ(Перечисление.СпособыРаспределения.ПустаяСсылка),
        ТЧРейсУслуги.СпособРаспределения
    ИЗ
        Документ.Рейс.Услуги КАК ТЧРейсУслуги) КАК ВложенныйЗапрос

СГРУППРОВАТЬ ПО
    ВложенныйЗапрос.Ссылка

Почему это работает? Функция МАКСИМУМ при работе со ссылочными типами (такими как ПеречислениеСсылка или СправочникСсылка) игнорирует значения NULL или пустые ссылки, если в выборке есть заполненное значение. Таким образом, из двух строк (одна с заполненными затратами, другая с услугами) запрос "соберет" одну идеальную строку.

Метод 2: Полное соединение табличных частей

Если в каждой табличной части документа гарантированно только по одной строке (или нам нужно сопоставить их строго по номеру строки), мы можем использовать ПОЛНОЕ СОЕДИНЕНИЕ. Мы не используем ЛЕВОЕ СОЕДИНЕНИЕ, так как не знаем заранее, в какой из таблиц данных больше, и какая из них может оказаться пустой.

Рассмотрим реализацию через соединение:


ВЫБРАТЬ
    ЕСТЬNULL(ТЧ_Затраты.Ссылка, ТЧ_Услуги.Ссылка) КАК Ссылка,
    ЕСТЬNULL(ТЧ_Затраты.СпособРаспределения, ЗНАЧЕНИЕ(Перечисление.СпособыРаспределения.ПустаяСсылка)) КАК СпособЗатраты,
    ЕСТЬNULL(ТЧ_Услуги.СпособРаспределения, ЗНАЧЕНИЕ(Перечисление.СпособыРаспределения.ПустаяСсылка)) КАК СпособУслуги
ИЗ
    Документ.Рейс.Затраты КАК ТЧ_Затраты
        ПОЛНОЕ СОЕДИНЕНИЕ Документ.Рейс.Услуги КАК ТЧ_Услуги
        ПО ТЧ_Затраты.Ссылка = ТЧ_Услуги.Ссылка
            И ТЧ_Затраты.НомерСтроки = ТЧ_Услуги.НомерСтроки
ГДЕ
    ЕСТЬNULL(ТЧ_Затраты.Ссылка, ТЧ_Услуги.Ссылка) = &СсылкаНаДокумент

Проанализируем этот подход. Использование НомерСтроки в условии связи позволяет вывести записи "в линейку": первая строка затрат будет напротив первой строки услуг. Если количество строк не совпадает, в соответствующих полях появится NULL, который мы корректно обрабатываем функцией ЕСТЬNULL.

Важные нюансы при работе с агрегатными функциями

При использовании метода с МАКСИМУМ (Метод 1) следует учитывать поведение системы, если в одной табличной части несколько разных значений. Выясним причину возможной потери данных: если в Затратах указано два разных способа распределения, МАКСИМУМ выберет только один из них (согласно внутреннему идентификатору UUID или порядку в конфигураторе).

Если ваша цель — найти "раскорреляцию" (расхождения), как упоминал автор, можно добавить условие ИМЕЮЩИЕ в конец запроса с группировкой:


ИМЕЮШИЕ
    МАКСИМУМ(ВложенныйЗапрос.СпособЗатраты) <> МАКСИМУМ(ВложенныйЗапрос.СпособУслуги)

Это позволит отфильтровать только те документы, где способы распределения в разных табличных частях не совпадают между собой.

Сравнение производительности и рекомендации

Выясним, какой метод лучше использовать в различных сценариях:

Рассмотрим еще один полезный прием — использование конструкции ВЫБОР для наглядности результата, если вместо пустых ссылок нужно выводить строковые пояснения (хотя в коде это не приветствуется, для отчетов это удобно):


ВЫБОР
    КОГДА ТЧ_Затраты.СпособРаспределения ЕСТЬ NULL 
    ТОГДА "Способ не задан"
    ИНАЧЕ ТЧ_Затраты.СпособРаспределения
КОНЕЦ КАК ОтображениеСпособа

Таким образом, мы разобрали основные технические средства языка запросов 1С для сведения данных из нескольких источников в одну структуру. Помните, что правильный выбор типа данных (избегайте строки "пусто", используйте NULL или типизированные пустые ссылки) является залогом корректной работы запроса и отсутствия ошибок типов.

← На главную