Срез последних на дату в запросе 1С: синтаксис, примеры и оптимизация

Разбираем, как получить СрезПоследних на вычисляемую дату прямо в запросе 1С, срез на каждую дату из таблицы и секреты оптимизации. Примеры кода внутри!

Бесплатная консультация юриста

+7 (800) 301-07-30

Реклама jurik.ru

Работа с регистрами сведений — одна из базовых задач разработчика 1С. Особую популярность пользуется виртуальная таблица СрезПоследних, позволяющая получить актуальные значения реквизитов на определенный момент времени. В этой статье подробно разберем, как корректно получить срез последних на дату, которая вычисляется или передается непосредственно в тексте запроса, а также рассмотрим продвинутые сценарии: получение среза на каждую дату из таблицы и оптимизацию таких запросов.

Базовый синтаксис виртуальной таблицы СрезПоследних

Виртуальная таблица СрезПоследних доступна для регистров сведений (независимых и подчиненных регистратору). Она возвращает записи, актуальные на заданную дату.

Синтаксис:

РегистрСведений.ИмяРегистра.СрезПоследних(<ДатаСреза>, <УсловиеОтбора>)

Параметры:

  • <ДатаСреза> (необязательный) — тип Дата. Момент времени, на который требуется получить срез. Если не указан, берется текущая дата сеанса (ТекущаяДатаСеанса). Важно: срез включает записи, у которых Период <= ДатаСреза.
  • <УсловиеОтбора> (необязательный) — строка, содержащая условие отбора на языке запросов (аналог секции ГДЕ). Позволяет отфильтровать измерения до выполнения соединения, что критически важно для производительности.

Пример простого запроса с параметром:

ВЫБРАТЬ
 ЦеныНоменклатурыСрезПоследних.Номенклатура КАК Номенклатура,
 ЦеныНоменклатурыСрезПоследних.Цена КАК Цена
ИЗ
 РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ДатаСреза, Номенклатура В (&СписокНоменклатуры) И ТипЦен = &ТипЦен) КАК ЦеныНоменклатурыСрезПоследних

В данном примере параметр &ДатаСреза передается извне (например, из СКД или кода 1С). Платформа автоматически добавляет условие отбора по полю Период и сортировку для поиска максимальной даты.

Срез последних на дату, вычисляемую ВНУТРИ запроса

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

Решение: Левое соединение (LEFT JOIN) с условием на дату

Классический и наиболее производительный паттерн — использование Левого соединения с регистром сведений (не виртуальной таблицей, а самой таблицей регистра) с условием отбора по периоду.

Алгоритм:

  1. Берем таблицу с датами, на которые нужен срез (Документы, ВременнаяТаблица, НаборЗаписей).
  2. Делаем ЛЕВОЕ СОЕДИНЕНИЕ с таблицей регистра сведений (физической таблицей).
  3. В условии соединения (ПО) указываем совпадение измерений И условие Регистр.Период <= ТаблицаСДатами.Дата.
  4. Группируем по полям таблицы с датами и измерениям регистра, выбирая МАКСИМУМ(Регистр.Период).
  5. Делаем второе соединение (или используем подзапрос/ВТ) для получения реквизитов по найденным максимальным датам.

Пример: Курс валюты на дату каждого документа

ВЫБРАТЬ
 Документы.Ссылка КАК Документ,
 Документы.Дата КАК ДатаДокумента,
 КурсыВалют.Курс КАК Курс,
 КурсыВалют.Кратность КАК Кратность
ИЗ
 (ВЫБРАТЬ Ссылка, Дата, Валюта ИЗ Документ.РеализацияТоваровУслуг ГДЕ Дата МЕЖДУ &НачалоПериода И &КонецПериода) КАК Документы
 ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют
 ПО Документы.Валюта = КурсыВалют.Валюта
 И КурсыВалют.Период <= Документы.Дата
ГДЕ
 (КурсыВалют.Период ЕСТЬ NULL ИЛИ КурсыВалют.Период = 
 (ВЫБРАТЬ МАКСИМУМ(КурсыВалютВнутр.Период)
 ИЗ РегистрСведений.КурсыВалют КАК КурсыВалютВнутр
 ГДЕ КурсыВалютВнутр.Валюта = Документы.Валюта
 И КурсыВалютВнутр.Период <= Документы.Дата))

Примечание: В современных версиях платформы (8.3.18+) оптимизатор запросов часто сам преобразует такой паттерн в эффективный план выполнения (Hash Match / Merge Join с TopN), поэтому явное использование подзапроса для MAX(Период) в секции ГДЕ или через ВТ не всегда требуется, но гарантирует корректность на любых версиях.

Паттерн «Срез последних на каждую дату» через временные таблицы (ВТ)

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

Этап 1: Подготовка набора дат

Создаем временную таблицу с уникальными датами, на которые нужен срез.

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

Этап 2: Нахождение максимального периода регистра для каждой даты среза

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

Этап 3: Получение реквизитов (Курс, Кратность) по найденным максимальным периодам

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

Преимущества: Четкое разделение этапов, возможность проиндексировать временные таблицы, понятный план запроса.

Недостатки: Несколько проходов по данным, накладные расходы на создание ВТ.

Современный подход: Один запрос без временных таблиц (для 1С 8.3.18+)

Начиная с платформы 8.3.18, оптимизатор научился эффективно выполнять паттерн «Срез на каждую дату» в одном запросе с двумя левыми соединениями (так называемый «Double Left Join» или «Apply» паттерн). Это часто работает быстрее ВТ за счет отсутствия материализации промежуточных результатов.

Структура запроса:

ВЫБРАТЬ
 База.Дата КАК ДатаСреза,
 База.Валюта КАК Валюта,
 Срез.Курс КАК Курс,
 Срез.Кратность КАК Кратность
ИЗ
 (ВЫБРАТЬ РАЗЛИЧНЫЕ Дата, Валюта ИЗ Документ.РеализацияТоваровУслуг ГДЕ Дата МЕЖДУ &Начало И &Конец) КАК База
 ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК Срез1
 ПО База.Валюта = Срез1.Валюта И Срез1.Период <= База.Дата
 ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК Срез
 ПО База.Валюта = Срез.Валюта 
 И Срез.Период = (ВЫБРАТЬ МАКСИМУМ(Срез2.Период) 
 ИЗ РегистрСведений.КурсыВалют КАК Срез2 
 ГДЕ Срез2.Валюта = База.Валюта И Срез2.Период <= База.Дата)

В некоторых случаях оптимизатор позволяет записать это еще компактнее, используя ВЫБРАТЬ ПЕРВЫЕ 1 в подзапросе секции ВЫБРАТЬ (если СУБД поддерживает APPLY / LATERAL JOIN), но вариант с двойным LEFT JOIN является стандартом де-факто для 1С.

Особенности использования в СКД (Система компоновки данных)

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

  • Создается Набор данных «РегистрСведений.КурсыВалют.СрезПоследних» (или запрос с LEFT JOIN).
  • В настройках набора данных указывается параметр ДатаСреза.
  • При объединении (Связи) с основным набором данных (Документы) в поле «Выражение» для параметра ДатаСреза указывается поле даты основного набора: [Документы.Дата].
  • СКД сама подставит значение даты текущей строки в параметр виртуальной таблицы при формировании запроса к БД (если используется виртуальная таблица) или корректно построит план с LEFT JOIN.

Важный нюанс: Если в отчете пользователю не нужны курсы валют для части строк, СКД все равно может запросить их (избыточные сведения), если не настроена отложенная выборка или параметры не используются в полях связей корректно.

Типичные ошибки и лучшие практики

Ошибка Последствие Решение
Использование СрезПоследних(&Дата) в запросе, где &Дата, это поле таблицы (не параметр). Синтаксическая ошибка или логическая ошибка (срез на одну дату для всех строк). Использовать LEFT JOIN с условием Период <= Таблица.Дата + агрегация MAX(Период).
Отсутствие индексов по измерениям и Периоду в регистре сведений. Полное сканирование таблицы регистра (Table Scan) при больших объемах. Проверить наличие индексов в конфигураторе (вкладка «Индексы» регистра). Обычно нужен индекс: (Измерения…, Период).
Использование ВЫБРАТЬ * из регистра в подзапросе MAX(Период). Лишние чтения данных, замедление. В подзапросе выбирать только МАКСИМУМ(Период) и ключи измерений.
Игнорирование NULL при LEFT JOIN. Потеря строк основной таблицы, если в регистре нет записей на дату. Всегда использовать ЛЕВОЕ СОЕДИНЕНИЕ и проверять ЕСТЬNULL(Реквизит, 0) при необходимости.
Передача даты с временем 00:00:00 для среза «на конец дня». Срез не включит записи, сделанные в этот день (Период регистра хранит дату и время). Использовать КОНЕЦДНЯ(&Дата) или ДАТАВРЕМЯ(Год, Месяц, День, 23, 59, 59).

Резюме: какой способ выбрать?

  1. Простой срез на одну дату параметра: Используйте виртуальную таблицу СрезПоследних(&Дата, Условие). Это самый быстрый и читаемый вариант.
  2. Срез на дату из другой таблицы (документы, справочники) — единичный запрос: Используйте паттерн LEFT JOIN + Подзапрос MAX(Период) в ГДЕ (или второй LEFT JOIN). Работает на всех версиях платформы.
  3. Массовый срез на множество дат (отчеты «на каждую дату»):
    • На платформе 8.3.18+: Попробуйте один запрос с двойным LEFT JOIN (Double Join). Часто выигрывает по скорости за счет Hash Match.
    • На старых платформах или при очень сложной логике: Используйте Временные Таблицы (ВТ) с индексами. Дает предсказуемую производительность.
  4. В СКД: Настраивайте связи наборов данных через поле даты, платформа сама выберет оптимальную стратегию.

Понимание механики работы СрезПоследних и умение переводить задачу «срез на каждую дату» на язык SQL (LEFT JOIN + MAX) — ключевой навык для написания быстрых и масштабируемых отчетов в 1С.