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