При разработке отчетов в системе компоновки данных (СКД) начинающие разработчики часто сталкиваются с ситуацией, когда данные в отчете «дублируются» или выводятся за весь период работы системы, несмотря на установленные отборы. Типичный пример: отчет по выработке сотрудников, где нужно сравнить фактические данные из регистра накопления и нормативные показатели из периодического регистра сведений. Разберем по шагам, почему возникают такие ошибки и как правильно построить запрос для получения корректного результата.
Рассмотрим типичную ситуацию: у нас есть регистр сведений НормаМесяц (где хранится норма часов для каждого сотрудника на каждый месяц) и оборотный регистр накопления Выработка (где фиксируется реальный результат). Если просто перечислить эти таблицы в запросе через запятую, 1С попытается выполнить перекрестное соединение (Cartesian product), либо, если соединение указано неверно, привяжет каждую запись о норме к каждой записи о факте.
Основные причины «неработающего» отбора:
Обороты без указания параметров выбирает данные за все время существования базы.Первое, что мы должны сделать — ограничить выборку данных из регистра накопления еще на этапе обращения к базе данных. Для этого используются параметры виртуальной таблицы Обороты. Проанализируем, как должен выглядеть фрагмент кода в конструкторе запроса:
ВЫБРАТЬ
ВыработкаОбороты.Сотрудник,
ВыработкаОбороты.КоличествоОборот
ИЗ
РегистрНакопления.Выработка.Обороты(&НачалоПериода, &КонецПериода, , ) КАК ВыработкаОбороты
Здесь &НачалоПериода и &КонецПериода — это стандартные параметры СКД. Если их не указать внутри скобок, СКД попытается наложить отбор позже, через секцию ГДЕ, что крайне неэффективно и часто приводит к ошибкам в расчете остатков и оборотов.
Теперь рассмотрим ситуацию, когда нам нужно добавить норму выработки. Нам необходимо соединить данные из регистра накопления с регистром сведений. Важно использовать ЛЕВОЕ СОЕДИНЕНИЕ, чтобы в отчет попали даже те сотрудники, по которым есть факт выработки, но по какой-то причине не внесена норма.
Проанализируем правильную структуру соединения:
ВЫБРАТЬ
ВыработкаОбороты.Сотрудник КАК Сотрудник,
ВыработкаОбороты.КоличествоОборот КАК Факт,
ЕСТЬNULL(Нормы.КоличествоЧасов, 0) КАК Норма
ИЗ
РегистрНакопления.Выработка.Обороты(&НачалоПериода, &КонецПериода, , ) КАК ВыработкаОбороты
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НормаМесяц КАК Нормы
ПО ВыработкаОбороты.Сотрудник = Нормы.Сотрудник
И (НАЧАЛОПЕРИОДА(ВыработкаОбороты.Период, МЕСЯЦ) = НАЧАЛОПЕРИОДА(Нормы.Период, МЕСЯЦ))
Обратите внимание на использование функции ЕСТЬNULL. Если в регистре сведений не окажется записи для конкретного сотрудника, система вернет NULL. Математические операции с NULL всегда дают NULL, поэтому для расчета отклонений (Факт - Норма) нам необходимо преобразовать пустоту в ноль.
Выясним причину, по которой автор темы жаловался на вывод «всей информации по норме». В регистре сведений НормаМесяц записи хранятся за разные даты (январь, февраль, март...). Если мы просто соединим по сотруднику, то к факту за сентябрь приклеятся нормы за все месяцы года.
Рассмотрим два способа решения:
СрезПоследних(&КонецПериода).ГДЕ или в условии соединения нужно ограничить период регистра сведений:
ГДЕ
Нормы.Период МЕЖДУ &НачалоПериода И &КонецПериода
Однако помните, что если в СКД выбран период "Сентябрь", то &НачалоПериода будет 01.09.2023 00:00:00, а &КонецПериода — 30.09.2023 23:59:59. Если норма в регистре записана датой 01.09.2023 00:00:00, отбор сработает корректно.
Для того чтобы пользователю было удобно выбирать период, выполним следующие настройки в схеме компоновки данных:
СтандартныйПериод.НачалоПериода и КонецПериода установим ограничение доступности (галочка "Ограничение доступности").НачалоПериода пропишем: &ПериодОтчета.ДатаНачала.КонецПериода пропишем: &ПериодОтчета.ДатаОкончания.Таким образом, пользователь выбирает понятный интервал (например, "Прошлый месяц"), а технические параметры, используемые в запросе, заполняются автоматически.
В сложных отчетах часто лучше не объединять всё в один огромный запрос, а использовать два набора данных. Рассмотрим этот метод:
1. Создаем НаборДанных1 (Запрос) к регистру накопления (Факт).
2. Создаем НаборДанных2 (Запрос) к регистру сведений (Норма).
3. Переходим на вкладку "Связи наборов данных".
Здесь мы указываем "Источник связи" (Факт) и "Приемник" (Норма). В условиях связи указываем Сотрудник = Сотрудник. Это позволяет СКД более интеллектуально управлять выборкой, особенно если один из регистров содержит гораздо больше измерений, чем другой.
Важный нюанс: Чтобы связь по периоду работала в этом случае, в обоих наборах данных должно быть поле Период, приведенное к единому формату (например, через НАЧАЛОПЕРИОДА(..., МЕСЯЦ)), и это поле также должно участвовать в связи наборов данных.
Для корректной работы отбора по периоду в сложных отчетах 1С необходимо соблюдать три правила: всегда передавать даты в параметры виртуальных таблиц, всегда связывать таблицы по ключевым измерениям (Сотрудник, Номенклатура и т.д.) и всегда ограничивать выборку данных из периодических регистров сведений, чтобы не получить "хвосты" из прошлых периодов.