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