Как получить данные из виртуальной таблицы СрезПоследних на разные даты для каждой строки

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В практике разработки на платформе 1С часто возникает задача: получить значения из регистра сведений (например, цены товаров или курсы валют) не на одну конкретную дату, а на разные даты, указанные в строках другой таблицы. Попытка написать в запросе конструкцию вида РегистрСведений.Цены.СрезПоследних(Таблица.Дата) приводит к синтаксической ошибке. В этой статье мы подробно разберем, почему так происходит, и изучим несколько эффективных способов решения этой задачи.

Почему нельзя передать поле в параметры виртуальной таблицы?

Разберем причину ограничения. Виртуальные таблицы в 1С — это «синтаксический сахар». При выполнении запроса платформа транслирует обращение к СрезПоследних в сложный вложенный SQL-запрос. Параметры, которые мы указываем в скобках (Период, Условие), передаются напрямую внутрь этого вложенного запроса (это называется push-down predicate). Это позволяет СУБД сначала отфильтровать данные, а затем выполнять соединение.

Однако язык запросов 1С устроен так, что параметры виртуальной таблицы должны быть константами или параметрами запроса (значениями, известными до начала выполнения всего запроса). Поле другой таблицы, участвующей в соединении, становится известным только в момент выполнения соединения, поэтому платформа не может «пробросить» его внутрь виртуальной таблицы. Если бы это было разрешено, СУБД пришлось бы выполнять отдельный подзапрос для каждой строки основной таблицы, что катастрофически сказалось бы на производительности.

Проанализируем основные способы обхода этого ограничения.

Способ 1. Использование связей наборов данных в СКД

Если ваша задача решается в рамках Системы компоновки данных (СКД), то это самый простой и элегантный путь. Система компоновки данных умеет делать то, что недоступно обычному запросу — передавать значения из одного набора данных в параметры другого.

Рассмотрим алгоритм настройки:

  1. Создадим НаборДанных1 (запрос), который выбирает основные данные, например: Товар и НужнаяДата.
  2. Создадим НаборДанных2 (запрос), который обращается к виртуальной таблице:
    
    ВЫБРАТЬ
        ЦеныСрезПоследних.Цена КАК Цена,
        ЦеныСрезПоследних.Товар КАК Товар
    ИЗ
        РегистрСведений.Цены.СрезПоследних(&Период, ) КАК ЦеныСрезПоследних
    
  3. Перейдем на закладку Связи наборов данных.
  4. Укажем в качестве источника связи НаборДанных1, а в качестве приемника — НаборДанных2.
  5. В колонке Условие связи соединим поля Товар обоих наборов.
  6. В колонке Параметры для поля НужнаяДата укажем параметр Период.

В этом случае СКД для каждой строки из первого набора будет вызывать второй набор, подставляя дату в параметр &Период. Это решает проблему «среза на разные даты» без написания сложных конструкций.

Способ 2. Классический метод через физическую таблицу (без СКД)

Если отчет строится не в СКД или использование связей наборов данных нецелесообразно из-за производительности, мы используем классический алгоритм работы с физической таблицей регистра. Посмотрим на этот процесс по шагам.

Шаг 1. Подготовка исходных данных. Поместим во временную таблицу (ВТ) те данные, для которых нужно найти срез. Важно оставить только уникальные комбинации измерений и дат, чтобы не делать лишнюю работу.


ВЫБРАТЬ РАЗЛИЧНЫЕ
    ИсходнаяТаблица.Товар КАК Товар,
    ИсходнаяТаблица.ДатаСреза КАК ДатаСреза
ПОМЕСТИТЬ ВТ_ТребуемыеДаты
ИЗ
    &ТаблицаДанных КАК ИсходнаяТаблица
;

Шаг 2. Поиск ближайшей даты записи в регистре. Соединяем нашу ВТ с физической таблицей регистра. Условие: дата в регистре должна быть меньше или равна нашей дате среза. Нам нужно найти максимальную такую дату для каждого товара.


ВЫБРАТЬ
    ВТ_ТребуемыеДаты.Товар КАК Товар,
    ВТ_ТребуемыеДаты.ДатаСреза КАК ДатаСреза,
    МАКСИМУМ(Цены.Период) КАК ПериодВРегистре
ПОМЕСТИТЬ ВТ_МаксимальныеДаты
ИЗ
    ВТ_ТребуемыеДаты КАК ВТ_ТребуемыеДаты
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Цены КАК Цены
        ПО ВТ_ТребуемыеДаты.Товар = Цены.Товар
            И ВТ_ТребуемыеДаты.ДатаСреза >= Цены.Период
ГРУППИРОВАТЬ ПО
    ВТ_ТребуемыеДаты.Товар,
    ВТ_ТребуемыеДаты.ДатаСреза
;

Шаг 3. Получение ресурсов. Теперь, когда у нас есть таблица, где для каждого товара и «желаемой» даты известна «реальная» дата последней записи в регистре, мы просто соединяем её с регистром еще раз, чтобы получить саму цену.


ВЫБРАТЬ
    ВТ_МаксимальныеДаты.Товар,
    ВТ_МаксимальныеДаты.ДатаСреза,
    Цены.Цена
ИЗ
    ВТ_МаксимальныеДаты КАК ВТ_МаксимальныеДаты
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Цены КАК Цены
        ПО ВТ_МаксимальныеДаты.Товар = Цены.Товар
            И ВТ_МаксимальныеДаты.ПериодВРегистре = Цены.Период

Этот метод является наиболее производительным для больших объемов данных, так как он эффективно использует индексы по полям Товар и Период.

Способ 3. Использование временной таблицы в параметрах виртуальной таблицы

Если нам нужно отфильтровать виртуальную таблицу по списку значений, но на одну дату, мы можем использовать ВТ прямо внутри параметров. Рассмотрим ситуацию, когда нам нужен срез последних на текущую дату, но только по тем товарам, которые есть в нашем документе.

Проанализируем запрос:


ВЫБРАТЬ
    ТоварыДокумента.Товар КАК Товар
ПОМЕСТИТЬ ВТ_Фильтр
ИЗ
    &ТаблицаТоваров КАК ТоварыДокумента
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ЦеныСрезПоследних.Товар,
    ЦеныСрезПоследних.Цена
ИЗ
    РегистрСведений.Цены.СрезПоследних(&Период, Товар В (ВЫБРАТЬ Товар ИЗ ВТ_Фильтр)) КАК ЦеныСрезПоследних

Важно: Этот способ не решит проблему разных дат, но он критически важен для производительности, когда дата одна. Никогда не делайте соединение с виртуальной таблицей без фильтров в параметрах, если в регистре миллионы записей. Всегда старайтесь ограничить выборку внутри () виртуальной таблицы.

Рекомендации по производительности

Проанализируем основные ошибки, которые допускают разработчики при работе со срезами данных:

Подведем итог: если вы работаете в СКД — используйте связи наборов данных. Если вы пишете запрос в коде и вам нужно получить данные на разные даты — используйте алгоритм с физической таблицей и поиском МАКСИМУМ(Период). Эти подходы гарантируют корректность результата и высокую скорость работы системы.

← На главную