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