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