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