Как в запросе 1С скрыть строки, в которых суммы по разным регистрам равны?

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

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

Анализ исходной проблемы

Рассмотрим ситуацию, когда мы объединяем данные из трех источников: ТоварыОрганизаций.Остатки, ТоварыНаСкладах.Остатки и ТоварыВЯчейках.Остатки. Автор вопроса пытался использовать следующую конструкцию в секции ГДЕ:


ГДЕ СУММА(ЗапросПоДанным.ТоварыОрганизаций) 
<>
СУММА(ЗапросПоДанным.ТоварыНаСкладах) 
<> 
СУММА(ЗапросПоДанным.ТоварыВЯчейках)

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

  1. Использование агрегатных функций в секции ГДЕ. В языке запросов 1С (как и в стандартном SQL) секция ГДЕ фильтрует исходные записи до того, как они будут сгруппированы. Поскольку функция СУММА вычисляется только в момент группировки, использовать ее в ГДЕ технически невозможно.
  2. Множественное сравнение «цепочкой». Конструкция А <> B <> C не поддерживается. Логические условия должны быть четко разделены операторами И или ИЛИ.

Решение №1: Использование секции ИМЕЮЩИЕ (HAVING)

Для фильтрации данных, которые уже прошли процесс группировки и суммирования, в 1С предназначена секция ИМЕЮЩИЕ. Она располагается строго после секции СГРУППИРОВАТЬ ПО. Разберем по шагам, как должен выглядеть исправленный запрос.

Выясним причину, почему это работает: секция ИМЕЮЩИЕ анализирует уже вычисленные агрегатные значения (суммы, максимумы, количество) и отсекает ненужные строки из финальной выборки.


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

СГРУППИРОВАТЬ ПО 
    Номенклатура

ИМЕЮЩИЕ 
    СУММА(ЗапросПоДанным.ТоварыОрганизаций) <> СУММА(ЗапросПоДанным.ТоварыНаСкладах)
    ИЛИ 
    СУММА(ЗапросПоДанным.ТоварыОрганизаций) <> СУММА(ЗапросПоДанным.ТоварыВЯчейках)

Важный нюанс: Условие А <> B ИЛИ А <> C математически достаточно для выявления любого расхождения в тройке чисел. Если А=B и А=C, то автоматически B=C, и строка скроется. Если хотя бы одно значение отличается, условие сработает, и мы увидим расхождение.

Решение №2: Обработка значений NULL

Проанализируем ситуацию глубже. Если по конкретной номенклатуре есть остатки на складе, но нет данных в регистре «Товары в ячейках», запрос вернет NULL для этой колонки. В 1С любое сравнение с NULL дает результат «Ложь». Это значит, что строки с отсутствующими остатками могут пропасть из отчета, хотя они и представляют собой расхождение.

Чтобы избежать этой ошибки, применим функцию ЕСТЬNULL. Посмотрим на пример доработанной секции ИМЕЮЩИЕ:


ИМЕЮЩИЕ 
    ЕСТЬNULL(СУММА(ЗапросПоДанным.ТоварыОрганизаций), 0) <> ЕСТЬNULL(СУММА(ЗапросПоДанным.ТоварыНаСкладах), 0)
    ИЛИ 
    ЕСТЬNULL(СУММА(ЗапросПоДанным.ТоварыОрганизаций), 0) <> ЕСТЬNULL(СУММА(ЗапросПоДанным.ТоварыВЯчейках), 0)

Таким образом, мы гарантируем, что даже если в одном из регистров данных нет, система воспримет это как число 0 и корректно сравнит с остатками из других таблиц.

Решение №3: Оптимизация через временные таблицы

Использование вложенных запросов (как в исходном примере) при больших объемах данных может замедлять работу системы. Проанализируем, как сделать запрос более профессиональным и быстрым. Рекомендуется использовать временные таблицы вместо вложенных запросов.

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

  1. Поместим результаты объединения остатков во временную таблицу ВТ_ВсеДанные.
  2. Проиндексируем ее по полю Номенклатура.
  3. В финальном запросе выполним группировку и наложим условие.

// Помещаем данные во временную таблицу
ВЫБРАТЬ
    Номенклатура,
    ТоварыОрганизаций,
    ТоварыНаСкладах,
    ТоварыВЯчейках
ПОМЕСТИТЬ ВТ_ДанныеОстатков
ИЗ (
    // Здесь ваш текст с ОБЪЕДИНИТЬ ВСЕ
) КАК ЗапросПоДанным;

////////////////////////////////////////////////////////////////////////////////
// Основной запрос
ВЫБРАТЬ
    ВТ.Номенклатура,
    СУММА(ВТ.ТоварыОрганизаций) КАК Организация,
    СУММА(ВТ.ТоварыНаСкладах) КАК Склад,
    СУММА(ВТ.ТоварыВЯчейках) КАК Ячейки
ИЗ
    ВТ_ДанныеОстатков КАК ВТ
СГРУППИРОВАТЬ ПО
    ВТ.Номенклатура
ИМЕЮЩИЕ
    СУММА(ВТ.ТоварыОрганизаций) <> СУММА(ВТ.ТоварыНаСкладах)
    ИЛИ СУММА(ВТ.ТоварыОрганизаций) <> СУММА(ВТ.ТоварыВЯчейках)

Такой подход позволяет SQL-серверу более эффективно строить план запроса, особенно если данных в регистрах десятки тысяч строк.

Дополнительные советы по производительности

Рассмотрим еще один важный момент, упомянутый опытными разработчиками. В исходном запросе автора при обращении к регистру ТоварыВЯчейках.Остатки используется условие:

Ячейка.Владелец = &Склад

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

Резюмируем:

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

← На главную