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

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

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

Почему Левое Соединение работает как Внутреннее?

Разберем типичную ситуацию. Программист пишет ЛЕВОЕ СОЕДИНЕНИЕ, ожидая получить все строки из первой таблицы (Расход) и только подходящие из второй (Приход). Однако, как только в секцию ГДЕ добавляется условие по полям из второй (правой) таблицы, результат меняется. Если во второй таблице нет соответствующей записи, поля будут содержать NULL. Условие NULL = Значение всегда ложно, поэтому строка отсекается целиком. Таким образом, ваше левое соединение превращается во внутреннее.

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

Способ 1. Перенос условий в секцию ПО (Связи таблиц)

Самый простой способ заставить ЛЕВОЕ СОЕДИНЕНИЕ работать правильно — это убрать все фильтры по зависимой таблице из секции ГДЕ и переместить их непосредственно в условие связи. Рассмотрим этот подход на примере кода:


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

Важный нюанс: Обратите внимание на использование функции ЕСТЬNULL. При левом соединении, если товара нет в приходе, система вернет пустое значение (NULL). Любая математическая операция с NULL (например, 10 - NULL) даст в результате NULL. Чтобы корректно рассчитать разницу, мы принудительно заменяем пустоту на ноль.

Способ 2. Использование объединения таблиц (UNION ALL)

Метод с соединением таблиц (JOIN) имеет скрытую опасность: если в одной накладной одна и та же номенклатура указана двумя разными строками, может произойти "размножение" записей (декартово произведение). Чтобы этого избежать и получить максимально точный результат, профессионалы используют схему с временными таблицами и объединением.

Разберем этот алгоритм по шагам:

  1. Выбираем данные из Расхода и помещаем во временную таблицу.
  2. Выбираем данные из Прихода и помещаем в ту же или другую временную таблицу.
  3. Объединяем их через ОБЪЕДИНИТЬ ВСЕ.
  4. Схлопываем (группируем) результат по номенклатуре.

Рассмотрим пример реализации через МенеджерВременныхТаблиц:


// Шаг 1: Собираем все данные в одну кучу
Запрос.Текст = "ВЫБРАТЬ
               |    РасходнаяСостав.Номенклатура КАК Номенклатура,
               |    РасходнаяСостав.Количество КАК КоличествоРасход,
               |    0 КАК КоличествоПриход
               |ПОМЕСТИТЬ ВТ_Данные
               |ИЗ
               |    Документ.РасходнаяНакладная.Состав КАК РасходнаяСостав
               |ГДЕ
               |    РасходнаяСостав.Ссылка.Дата МЕЖДУ &ДатаНач И &ДатаКон
               |
               |ОБЪЕДИНИТЬ ВСЕ
               |
               |ВЫБРАТЬ
               |    ПриходнаяСостав.Номенклатура,
               |    0,
               |    ПриходнаяСостав.Количество
               |ИЗ
               |    Документ.ПриходнаяНакладная.Состав КАК ПриходнаяСостав
               |ГДЕ
               |    ПриходнаяСостав.Ссылка.ДокОснование.Дата МЕЖДУ &ДатаНач И &ДатаКон;
               |
               |////////////////////////////////////////////////////////////////////////////////
               |// Шаг 2: Группируем и вычисляем разницу
               |ВЫБРАТЬ
               |    ВТ_Данные.Номенклатура КАК Номенклатура,
               |    СУММА(ВТ_Данные.КоличествоРасход) КАК Расход,
               |    СУММА(ВТ_Данные.КоличествоПриход) КАК Приход,
               |    СУММА(ВТ_Данные.КоличествоРасход) - СУММА(ВТ_Данные.КоличествоПриход) КАК Разница
               |ИЗ
               |    ВТ_Данные КАК ВТ_Данные
               |СГРУППИРОВАТЬ ПО
               |    ВТ_Данные.Номенклатура";

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

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

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

1. Статус документа (Проведен). Документы в 1С могут быть просто записаны ("черновики"). Если вы не добавите условие ГДЕ Ссылка.Проведен = ИСТИНА, ваш отчет будет показывать цифры, которые еще не повлияли на остатки на складах.

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

3. Учет характеристик. Если в вашей программе включен учет по характеристикам (цвет, размер), то связывать таблицы только по полю Номенклатура нельзя. Вы получите неверные данные, так как разные модификации товара "схлопнутся" в одну строку. Обязательно добавляйте Характеристика в условия связи и группировки.

4. Индексация. Если вы используете временные таблицы, не забывайте индексировать поле Номенклатура. Это значительно ускорит выполнение финального запроса при больших объемах данных.

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

← На главную