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