В практике разработки на платформе 1С:Предприятие 8.3 часто возникает специфическая задача: необходимо получить данные из двух или более табличных частей одного документа таким образом, чтобы они отображались не последовательно (одна под другой), а параллельно (в одной строке). Например, если в документе Рейс есть табличная часть Затраты со способом распределения "По весу" и табличная часть Услуги со способом распределения "По объему", стандартное использование оператора ОБЪЕДИНИТЬ выдаст две разные строки. Наша задача — "схлопнуть" их в одну.
Рассмотрим пошагово, как реализовать такую логику в языке запросов 1С, проанализируем различные подходы и выберем наиболее эффективные в зависимости от структуры данных.
Проанализируем ситуацию, описанную автором темы. Основная проблема оператора ОБЪЕДИНИТЬ ВСЕ заключается в том, что он просто добавляет записи из второй таблицы в конец первой. Чтобы объединить эти записи по полю Ссылка, мы должны применить группировку. Разберем этот алгоритм.
NULL (или пустую ссылку соответствующего типа).МАКСИМУМ к значимым полям.Посмотрим на пример кода, который решает задачу:
ВЫБРАТЬ
ВложенныйЗапрос.Ссылка КАК Ссылка,
МАКСИМУМ(ВложенныйЗапрос.СпособЗатраты) КАК СпособЗатраты,
МАКСИМУМ(ВложенныйЗапрос.СпособУслуги) КАК СпособУслуги
ИЗ
(ВЫБРАТЬ
ТСРейсЗатраты.Ссылка КАК Ссылка,
ТСРейсЗатраты.СпособРаспределения КАК СпособЗатраты,
ЗНАЧЕНИЕ(Перечисление.СпособыРаспределения.ПустаяСсылка) КАК СпособУслуги
ИЗ
Документ.Рейс.Затраты КАК ТСРейсЗатраты
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ТЧРейсУслуги.Ссылка,
ЗНАЧЕНИЕ(Перечисление.СпособыРаспределения.ПустаяСсылка),
ТЧРейсУслуги.СпособРаспределения
ИЗ
Документ.Рейс.Услуги КАК ТЧРейсУслуги) КАК ВложенныйЗапрос
СГРУППРОВАТЬ ПО
ВложенныйЗапрос.Ссылка
Почему это работает? Функция МАКСИМУМ при работе со ссылочными типами (такими как ПеречислениеСсылка или СправочникСсылка) игнорирует значения NULL или пустые ссылки, если в выборке есть заполненное значение. Таким образом, из двух строк (одна с заполненными затратами, другая с услугами) запрос "соберет" одну идеальную строку.
Если в каждой табличной части документа гарантированно только по одной строке (или нам нужно сопоставить их строго по номеру строки), мы можем использовать ПОЛНОЕ СОЕДИНЕНИЕ. Мы не используем ЛЕВОЕ СОЕДИНЕНИЕ, так как не знаем заранее, в какой из таблиц данных больше, и какая из них может оказаться пустой.
Рассмотрим реализацию через соединение:
ВЫБРАТЬ
ЕСТЬNULL(ТЧ_Затраты.Ссылка, ТЧ_Услуги.Ссылка) КАК Ссылка,
ЕСТЬNULL(ТЧ_Затраты.СпособРаспределения, ЗНАЧЕНИЕ(Перечисление.СпособыРаспределения.ПустаяСсылка)) КАК СпособЗатраты,
ЕСТЬNULL(ТЧ_Услуги.СпособРаспределения, ЗНАЧЕНИЕ(Перечисление.СпособыРаспределения.ПустаяСсылка)) КАК СпособУслуги
ИЗ
Документ.Рейс.Затраты КАК ТЧ_Затраты
ПОЛНОЕ СОЕДИНЕНИЕ Документ.Рейс.Услуги КАК ТЧ_Услуги
ПО ТЧ_Затраты.Ссылка = ТЧ_Услуги.Ссылка
И ТЧ_Затраты.НомерСтроки = ТЧ_Услуги.НомерСтроки
ГДЕ
ЕСТЬNULL(ТЧ_Затраты.Ссылка, ТЧ_Услуги.Ссылка) = &СсылкаНаДокумент
Проанализируем этот подход. Использование НомерСтроки в условии связи позволяет вывести записи "в линейку": первая строка затрат будет напротив первой строки услуг. Если количество строк не совпадает, в соответствующих полях появится NULL, который мы корректно обрабатываем функцией ЕСТЬNULL.
При использовании метода с МАКСИМУМ (Метод 1) следует учитывать поведение системы, если в одной табличной части несколько разных значений. Выясним причину возможной потери данных: если в Затратах указано два разных способа распределения, МАКСИМУМ выберет только один из них (согласно внутреннему идентификатору UUID или порядку в конфигураторе).
Если ваша цель — найти "раскорреляцию" (расхождения), как упоминал автор, можно добавить условие ИМЕЮЩИЕ в конец запроса с группировкой:
ИМЕЮШИЕ
МАКСИМУМ(ВложенныйЗапрос.СпособЗатраты) <> МАКСИМУМ(ВложенныйЗапрос.СпособУслуги)
Это позволит отфильтровать только те документы, где способы распределения в разных табличных частях не совпадают между собой.
Выясним, какой метод лучше использовать в различных сценариях:
МенеджерВременныхТаблиц), проиндексировать их по полю Ссылка, и только потом выполнять соединение или объединение. Это значительно сократит время выполнения запроса за счет оптимизации планов SQL.Рассмотрим еще один полезный прием — использование конструкции ВЫБОР для наглядности результата, если вместо пустых ссылок нужно выводить строковые пояснения (хотя в коде это не приветствуется, для отчетов это удобно):
ВЫБОР
КОГДА ТЧ_Затраты.СпособРаспределения ЕСТЬ NULL
ТОГДА "Способ не задан"
ИНАЧЕ ТЧ_Затраты.СпособРаспределения
КОНЕЦ КАК ОтображениеСпособа
Таким образом, мы разобрали основные технические средства языка запросов 1С для сведения данных из нескольких источников в одну структуру. Помните, что правильный выбор типа данных (избегайте строки "пусто", используйте NULL или типизированные пустые ссылки) является залогом корректной работы запроса и отсутствия ошибок типов.