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