В современной разработке на платформе 1С 8.x мы привыкли к гибкости языка Query (SQL-подобного), где конструкция ЛЕВОЕ СОЕДИНЕНИЕ (LEFT JOIN) является базовой. Однако в среде 1С:Предприятие 7.7 механизмы работы с запросами существенно отличаются. Часто перед разработчиком встает задача: вывести полный список номенклатуры из справочника и пристыковать к нему данные по остаткам из регистра. Если остатка нет, система должна вывести ноль, но сам товар не должен исчезнуть из отчета.
В этой статье мы подробно разберем, как заставить встроенный язык запросов 1С 7.7 работать по принципу левого соединения, рассмотрим нюансы группировок и научимся правильно фильтровать данные по иерархии справочников.
В 1С 7.7 аналог LEFT JOIN реализуется не в секции связей (которой нет в явном виде), а через определение состава группировки. По умолчанию запрос 1С 7.7 работает как INNER JOIN: в результат попадают только те записи, для которых есть соответствующие данные и в справочнике, и в регистре (или другом источнике данных). Чтобы изменить это поведение, необходимо использовать ключевое слово Все (или ALL в англоязычном варианте).
Рассмотрим базовый пример. Допустим, нам нужно получить все товары и их конечные остатки. Проанализируем структуру запроса:
// Создание объекта Запрос
ТекстЗапроса = "
|Период с НачДата по КонДата;
|Товар = Справочник.Номенклатура.ТекущийЭлемент, Регистр.ОстаткиТоваров.Товар;
|Кол = Регистр.ОстаткиТоваров.Количество;
|Функция КонОст = КонОст(Кол);
|Группировка Товар Все;
|";
Обратите внимание на строку Группировка Товар Все;. Именно слово Все указывает системе, что в итоговую выборку должны попасть абсолютно все элементы справочника Номенклатура, даже если по ним не было движений или остатков в регистре ОстаткиТоваров.
По умолчанию группировка по справочнику в запросе может выводить иерархические уровни (родительские папки). Если ваша задача — получить плоский список товаров (только элементы), необходимо дополнить конструкцию группировки. Выясним, как это сделать правильно.
Добавим условие исключения групп непосредственно в описание группировки:
|Группировка Товар Без Групп Все;
Комбинация Без Групп Все позволяет решить сразу две задачи:
Часто возникает необходимость отсечь определенную ветку справочника (например, категорию «Услуги» или «Брак»). Казалось бы, достаточно добавить простое условие НЕ (Товар в ВыбГруппа). Однако здесь кроется главная ловушка встроенных запросов 1С 7.7.
Разберем ситуацию: если вы используете Группировка Товар Все и одновременно ставите условие НЕ (Товар в ВыбГруппа), вы можете столкнуться с тем, что товары без остатков начнут исчезать или, наоборот, фильтр перестанет работать корректно. Это связано с тем, как движок 1С 7.7 интерпретирует логические условия при построении временных таблиц в памяти.
Рассмотрим наиболее надежный способ фильтрации, который предложили участники обсуждения:
// Пример правильного исключения группы с сохранением "левого соединения"
Период с НачДата по КонДата;
Товар = Справочник.Номенклатура.ТекущийЭлемент, Регистр.ОстаткиТоваров.Товар;
Количество = Регистр.ОстаткиТоваров.Количество;
Функция Остаток = КонОст(Количество);
// Используем метод ПринадлежитГруппе для надежности
Условие (Товар.ПринадлежитГруппе(ВыбГруппа) = 0);
Группировка Товар Без Групп Все;
Важный нюанс: использование функции ПринадлежитГруппе() внутри условия запроса может замедлить выполнение на больших справочниках, особенно в файловой версии (DBF). Если производительность критична, а структура справочника позволяет, можно использовать проверку родителя: Условие (Товар.Родитель <> ВыбГруппа), но помните, что это сработает только для элементов, находящихся непосредственно внутри ВыбГруппа, без учета вложенности.
Проанализируем техническую сторону работы запроса в среде SQL. В 1С 7.7 при использовании стандартного языка запросов есть известная особенность с оператором НЕ. Иногда он некорректно обрабатывается транслятором при формировании SQL-кода. Именно поэтому опытные разработчики рекомендуют использовать сравнение с нулем (как в примере выше: = 0) вместо явного отрицания НЕ, если это возможно.
Если же база данных имеет внушительный объем и штатные запросы работают слишком медленно, рекомендуется обратить внимание на прямые запросы (Direct SQL). Это технология, реализуемая с помощью внешних компонент (например, 1С++). В прямом запросе вы сможете написать классический SQL:
SELECT
Nom.ID as [Товар $Справочник.Номенклатура],
ISNULL(Reg.Quantity, 0) as Остаток
FROM
$Справочник.Номенклатура as Nom
LEFT JOIN
(SELECT ItemID, SUM(Balance) as Quantity FROM RegTable WHERE ...) as Reg ON Nom.ID = Reg.ItemID
WHERE
Nom.ParentID <> :ExcludeGroup
Однако для быстрой разработки простых отчетов штатный механизм с ключевым словом Все остается незаменимым инструментом.
Подведем итог и составим пошаговый план реализации задачи:
Функция (например, КонОст или Сумма), чтобы система знала, что именно нужно агрегировать.Все — это активирует режим LEFT JOIN.ПринадлежитГруппе() = 0 в секции Условие.Без Групп.Следуя этим правилам, вы получите корректный отчет, в котором будут отображены все товары, независимо от наличия остатков на складах, что обеспечит полноту и достоверность аналитических данных.