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