Как в запросе 1С вывести всех контрагентов, включая тех, у кого нет продаж

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

При построении отчетов в 1С:Предприятие часто возникает задача вывести полный список объектов (например, контрагентов, номенклатуры или сотрудников), независимо от того, были ли по ним какие-то движения в регистрах за выбранный период. Обычное соединение таблиц часто «отрезает» тех, у кого оборотов нет, что делает отчет неполным. В этой статье мы подробно разберем, как правильно составить запрос, чтобы получить данные по всем контрагентам, даже если продаж по ним не зафиксировано.

Проблема: Почему «пропадают» контрагенты без продаж?

Рассмотрим стандартную ситуацию. У нас есть справочник Контрагенты и регистр накопления Продажи. Если мы попытаемся соединить их так, как это часто делают новички, мы получим только тех контрагентов, которые совершили покупку. Давайте проанализируем ошибочный запрос:


ВЫБРАТЬ
    СУММА(Продажи.Стоимость) КАК Стоимость,
    Контрагенты.Ссылка
ИЗ
    РегистрНакопления.Продажи КАК Продажи
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
        ПО Продажи.Контрагент = Контрагенты.Ссылка
ГДЕ
    Продажи.Период >= &Период
СГРУППИРОВАТЬ ПО
    Контрагенты.Ссылка

Здесь допущены две концептуальные ошибки. Во-первых, основной таблицей (левой) выбраны Продажи. Это означает, что запрос берет все записи из продаж и пытается найти к ним контрагентов. Если продажи нет — строки в результате не будет. Во-вторых, условие в секции ГДЕ окончательно исключает всех «пустых» контрагентов, так как для них значение Период будет равно NULL, а сравнение NULL >= &Период всегда ложно.

Шаг 1: Правильный выбор ведущей таблицы

Для решения задачи нам нужно поменять таблицы местами. Сделаем справочник Контрагенты основной таблицей, а таблицу Продажи — присоединяемой. Это гарантирует нам, что каждая запись из справочника попадет в результат, а данные из регистра подтянутся только там, где они есть. Проанализируем правильную структуру:


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

Шаг 2: Перенос условий из ГДЕ в секцию ПО

Теперь разберем критически важный момент с фильтрацией по периоду. Если мы напишем условие ГДЕ Продажи.Период >= &Период, мы снова потеряем всех контрагентов без продаж. В SQL-логике 1С секция ГДЕ применяется после выполнения соединения. Все записи контрагентов, у которых в колонках продаж стоит NULL, будут отфильтрованы этим условием.

Решение: Условие на период в правой таблице (регистре) нужно переносить непосредственно в условие связи ПО. В этом случае система сначала отберет продажи за нужный период, а уже потом «приклеит» их к полному списку контрагентов.


ВЫБРАТЬ
    Контрагенты.Ссылка КАК Контрагент,
    Продажи.Стоимость КАК Стоимость
ИЗ
    Справочник.Контрагенты КАК Контрагенты
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Продажи КАК Продажи
        ПО Контрагенты.Ссылка = Продажи.Контрагент 
        И Продажи.Период >= &НачалоПериода 
        И Продажи.Период <= &КонецПериода

Шаг 3: Обработка NULL-значений с помощью ЕСТЬNULL

Когда мы выводим отчет, пользователю не очень удобно видеть пустые ячейки там, где не было продаж. Профессиональнее будет вывести «0». Для этого в языке запросов 1С используется функция ЕСТЬNULL. Она проверяет первый аргумент на значение NULL и, если оно обнаружено, заменяет его на второй аргумент.

Посмотрим на пример:

ЕСТЬNULL(Продажи.Стоимость, 0) КАК СтоимостьПродаж

Теперь, если продаж не было, в колонке будет стоять число 0, что позволит корректно суммировать данные или использовать их в дальнейших расчетах.

Шаг 4: Использование виртуальных таблиц (Оптимизация)

В реальных задачах обращение к физической таблице регистра накопления (как мы делали выше через РегистрНакопления.Продажи) — это плохая практика. Виртуальные таблицы, такие как Обороты, работают значительно быстрее, так как используют предварительно рассчитанные итоги в базе данных SQL.

Давайте перепишем наш запрос с использованием виртуальной таблицы Продажи.Обороты. Важно помнить, что период в виртуальную таблицу передается как параметр самой таблицы, а не через условия ГДЕ или ПО.


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

Дополнительные рекомендации

При работе с большими справочниками мы рекомендуем обратить внимание на следующие нюансы:

  1. Фильтрация справочника: Если в базе десятки тысяч контрагентов, левое соединение ко всему справочнику может замедлить систему. Обязательно добавляйте условия ГДЕ НЕ Контрагенты.ЭтоГруппа (чтобы не выводить папки) и ГДЕ НЕ Контрагенты.ПометкаУдаления (чтобы исключить неактуальные записи).
  2. Временные таблицы: Если логика отчета сложная, лучше сначала выбрать нужный список контрагентов во временную таблицу (ВТ), проиндексировать её по ссылке, а уже затем соединять с оборотами.
  3. Группировка: При использовании физических таблиц регистра не забывайте про СГРУППИРОВАТЬ ПО. Если у одного контрагента было 10 продаж за день, без группировки он появится в отчете 10 раз. Виртуальная таблица Обороты решает эту проблему автоматически, так как возвращает уже свернутые данные.

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

← На главную