Как получить срез последних цен на дату каждого документа в отчете СКД?

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

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

Суть проблемы: почему обычное соединение не работает?

Рассмотрим ситуацию, описанную пользователем. Есть документ Списание недостач товаров от 09.01.19. В регистре сведений Цены номенклатуры есть следующие записи для товара:

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

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


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

Как справедливо заметил автор темы, условие СписаниеТовары.Ссылка.Дата <= Цены.Период не решает задачу, так как оно лишь фильтрует уже полученный «срез», а не заставляет систему искать ближайшую дату «назад» для каждой строки. Выясним, какие методы решения являются правильными.

Способ №1: Связь двух наборов данных в СКД (Рекомендуемый)

Это самый эффективный и «правильный» способ для системы компоновки данных. Идея заключается в том, чтобы разделить получение списка документов и получение цен на два разных запроса, а затем связать их средствами самой СКД.

Шаг 1: Создание первого набора данных (НаборДанных1).

Создадим набор данных — запрос, который выбирает основные данные документов:


ВЫБРАТЬ
    СписаниеТовары.Ссылка КАК Документ,
    СписаниеТовары.Ссылка.Дата КАК ДатаДокумента,
    СписаниеТовары.Номенклатура КАК Номенклатура,
    СписаниеТовары.Количество КАК Количество
ИЗ
    Документ.СписаниеНедостачТоваров.Товары КАК СписаниеТовары
ГДЕ
    СписаниеТовары.Ссылка.Дата МЕЖДУ &НачалоПериода И &КонецПериода

Шаг 2: Создание второго набора данных (НаборДанных2).

Создадим второй набор данных — запрос к виртуальной таблице регистра сведений. Очень важно добавить параметр в саму виртуальную таблицу:


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

Обратите внимание на параметр &ДатаСреза. Мы не будем задавать его на вкладке параметров, он нужен нам для связи.

Шаг 3: Настройка связи наборов данных.

Перейдем на закладку Связи наборов данных и добавим новую связь:

  1. Источник связи: НаборДанных1.
  2. Приемник связи: НаборДанных2.
  3. Условие связи: Номенклатура = Номенклатура.
  4. Параметры: в колонке «Параметры» выберите наш параметр ДатаСреза, а в колонке «Выражение источника» укажите ДатаДокумента.

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

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

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

Разберем этот метод по шагам:

  1. Помещение документов во временную таблицу: Выбираем все нужные документы и их номенклатуру в таблицу ВТ_Документы.
  2. Поиск актуальных дат записей регистра: Соединяем документы с физической таблицей регистра сведений (не со срезом!). Условие соединения: Регистр.Период <= ВТ_Документы.Дата. Группируем результат по документу и номенклатуре, выбирая МАКСИМУМ(Регистр.Период). Это даст нам дату последней записи в регистре, которая предшествует документу.
  3. Получение конечных данных: Полученную таблицу с максимальными датами соединяем еще раз с физической таблицей регистра, чтобы получить само значение ресурса (цену) по этой дате.

Пример реализации такого подхода:


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

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

Сравнение методов: что выбрать?

Проанализируем ситуацию с точки зрения производительности:

Важный совет: При использовании Метода №1 в СКД убедитесь, что в настройках связи наборов данных установлена галочка «Обязательная связь», если вы не хотите видеть в отчете документы без цен. Также помните, что в параметрах виртуальной таблицы среза последних во втором наборе данных желательно указывать не только дату, но и другие фильтры (например, по виду цен), чтобы максимально ограничить выборку на стороне сервера базы данных.

← На главную