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