При разработке сложных отчетов в 1С, особенно если они строятся не на системе компоновки данных (СКД), а на основе ручного обхода выборки запроса, программисты часто сталкиваются с двумя критическими проблемами. Первая — это задвоение (дублирование) строк при попытке присоединить данные из регистров сведений, таких как НоменклатураПоставщиков. Вторая — появление значений NULL в полях на уровне группировок, даже если в детальных записях данные присутствуют. Разберем детально, как решить эти задачи, оптимизировать запрос и корректно вывести результат в табличный документ.
Рассмотрим ситуацию: у нас есть основной запрос, который собирает потребности и остатки номенклатуры. Нам необходимо добавить колонку с артикулом или наименованием номенклатуры поставщика. Регистр сведений НоменклатураПоставщиков часто имеет структуру «многие к одному» или «многие ко многим». Одной позиции нашей номенклатуры может соответствовать несколько записей поставщика (разные поставщики, разные упаковки или просто исторические данные).
Когда мы выполняем обычное ЛЕВОЕ СОЕДИНЕНИЕ, СУБД создает новую строку для каждого совпадения. Если для одной позиции номенклатуры в регистре нашлось три записи поставщика, то все количественные показатели (остатки, потребности) в итоговой таблице увеличатся в три раза, так как строки продублируются. Это классическая ошибка, приводящая к неверным итогам в отчете.
Проанализируем самый простой способ избежать дублей — использование функции МАКСИМУМ. Если нам не принципиально, какой именно поставщик будет выведен (или мы точно знаем, что поставщик один, но хотим подстраховаться от дублей), необходимо сгруппировать данные регистра перед соединением или использовать агрегаты в финальной выборке.
Посмотрим на пример модификации секции ВЫБРАТЬ и СГРУППИРОВАТЬ ПО:
ВЫБРАТЬ
РассчитанныеДанные.Номенклатура КАК Номенклатура,
РассчитанныеДанные.ВариантИсполнения КАК ВариантИсполнения,
МАКСИМУМ(НоменклатураПоставщиков.НоменклатураПоставщика) КАК НоменклатураПоставщика,
МАКСИМУМ(НоменклатураПоставщиков.ВариантИсполненияПоставщика) КАК ВариантИсполненияПоставщика,
СУММА(РассчитанныеДанные.КолОстаток) КАК КолОстаток
ПОМЕСТИТЬ ИтоговыеДанные
ИЗ
РассчитанныеДанные КАК РассчитанныеДанные
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НоменклатураПоставщиков КАК НоменклатураПоставщиков
ПО РассчитанныеДанные.Номенклатура = НоменклатураПоставщиков.Номенклатура
И РассчитанныеДанные.ВариантИсполнения = НоменклатураПоставщиков.ВариантИсполнения
СГРУППИРОВАТЬ ПО
РассчитанныеДанные.Номенклатура,
РассчитанныеДанные.ВариантИсполнения
Использование МАКСИМУМ для строковых полей или ссылок позволяет выбрать одно значение из множества и сохранить структуру строк основной таблицы неизменной. Это гарантирует, что СУММА(КолОстаток) останется корректной.
Для оптимизации сложных запросов, как в представленном примере, лучше всего вынести работу с регистром поставщиков в отдельную временную таблицу. Это позволит сначала подготовить уникальный список соответствий, а затем «приклеить» его к основным данным без риска искажения расчетов.
Рассмотрим алгоритм действий:
ВТ_ОсновныеДанные со всеми расчетами остатков и потребностей.ВТ_ДанныеПоставщиков, где выбираем только нужные поля из регистра, используя СГРУППИРОВАТЬ ПО для исключения дублей.
// Шаг 2: Подготовка данных поставщиков
ВЫБРАТЬ
НоменклатураПоставщиков.Номенклатура КАК Номенклатура,
НоменклатураПоставщиков.ВариантИсполнения КАК ВариантИсполнения,
МАКСИМУМ(НоменклатураПоставщиков.НоменклатураПоставщика) КАК НоменклатураПоставщика
ПОМЕСТИТЬ ВТ_СвязиПоставщиков
ИЗ
РегистрСведений.НоменклатураПоставщиков КАК НоменклатураПоставщиков
СГРУППИРОВАТЬ ПО
НоменклатураПоставщиков.Номенклатура,
НоменклатураПоставщиков.ВариантИсполнения;
////////////////////////////////////////////////////////////////////////////////
// Шаг 3: Соединение
ВЫБРАТЬ
ВТ_Основные.Номенклатура,
ВТ_Основные.КолОстаток,
ВТ_Связи.НоменклатураПоставщика
ИЗ
ВТ_Основные КАК ВТ_Основные
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_СвязиПоставщиков КАК ВТ_Связи
ПО ВТ_Основные.Номенклатура = ВТ_Связи.Номенклатура
И ВТ_Основные.ВариантИсполнения = ВТ_Связи.ВариантИсполнения
Выясним причину, по которой данные поставщика могут быть видны в детальных записях, но возвращают NULL при попытке обращения к ним из группировки (например, из уровня «Номенклатура»).
В языке запросов 1С, если поле не указано в списке группировок секции ИТОГИ ПО..., оно будет доступно только на самом нижнем уровне выборки. Чтобы поле «пробрасывалось» наверх, к нему нужно применить агрегатную функцию в блоке ИТОГИ.
Проанализируем правильное написание секции итогов для нашего случая:
ИТОГИ
СУММА(КолПотребность),
МАКСИМУМ(НоменклатураПоставщика),
МАКСИМУМ(ВариантИсполненияПоставщика)
ПО
ОБЩИЕ,
Номенклатура,
ВариантИсполнения
Теперь, когда мы будем обходить результат запроса по группировкам (используя метод Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам)), значение НоменклатураПоставщика не будет пустым на уровне группировки «Номенклатура». Оно примет значение «максимального» поставщика для всех дочерних записей этой группы.
Если отчет строится не через СКД, а программно, важно правильно обращаться к полям выборки. Рассмотрим фрагмент кода, отвечающий за вывод параметров в область макета. В процедуре вывода группировок необходимо проверять уровень иерархии и наличие данных.
Пока ВхЗапрВыб.Следующий() Цикл
// Получаем область строки из макета
Область = ВхМакет.ПолучитьОбласть("Строка|Данные");
// Заполняем параметры из выборки
Область.Параметры.Заполнить(ВхЗапрВыб);
// Если нужно специфическое условие для отображения данных поставщика
Если ЗначениеЗаполнено(ВхЗапрВыб.НоменклатураПоставщика) Тогда
Область.Параметры.ЗначениеГрСтрПостав = ВхЗапрВыб.НоменклатураПоставщика;
Иначе
Область.Параметры.ЗначениеГрСтрПостав = "---";
КонецЕсли;
ВхТаблДок.Вывести(Область, ВхЗапрВыб.Уровень());
КонецЦикла;
Важный момент: если вы используете построитель отчета или ручной обход, убедитесь, что в измерениях строк (ИзмеренияСтроки) поля поставщика не являются обязательными для группировки, если вы хотите видеть их просто как справочную информацию в колонке.
Если у одной номенклатуры действительно может быть несколько названий у разных поставщиков и их нужно вывести в одну ячейку через запятую, сделать это чистым запросом в 1С невозможно (отсутствует аналог функции STRING_AGG). В этом случае мы рекомендуем следующий алгоритм:
ТаблицуЗначений.Таким образом, мы рассмотрели комплексный подход к решению проблемы: от предотвращения дублирования строк на уровне SQL-запроса до правильной настройки агрегации в итогах и программного вывода в табличный документ. Соблюдение этих правил позволит создавать стабильные и точные отчеты даже в самых сложных архитектурных условиях.