Как получить одну последнюю запись из регистра сведений, если в нем несколько измерений?

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

Каждый разработчик 1С рано или поздно сталкивается с ситуацией, когда виртуальная таблица СрезПоследних ведет себя не так, как ожидалось. Типичный пример: в периодическом регистре сведений «Цены номенклатуры» есть два измерения — Номенклатура и Поставщик. Мы хотим получить актуальную цену товара, но запрос возвращает несколько строк для одной и той же номенклатуры (по одной на каждого поставщика). Разберем подробнее, почему это происходит и как решить эту задачу правильно.

Почему «Срез последних» возвращает несколько записей

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

Рассмотрим пример. В регистре зафиксированы следующие данные:

  1. 01.01.2023 — ООО «Ромашка» — Свистелка — 100 руб.
  2. 01.02.2023 — ООО «Одуванчик» — Свистелка — 120 руб.
  3. 01.03.2023 — ООО «Ромашка» — Свистелка — 110 руб.

Если мы запросим СрезПоследних на текущую дату, платформа выдаст две строки:

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

Решение 1: Пересмотр архитектуры регистра

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

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

Решение 2: Использование временных таблиц и группировок

Если архитектуру менять нельзя (например, регистр типовой), воспользуемся методом «обратного соединения по максимальной дате». Рассмотрим алгоритм по шагам:

  1. Выберем все актуальные данные из СрезПоследних во временную таблицу.
  2. Из этой временной таблицы получим максимальные даты в разрезе нужной нам аналитики (номенклатуры).
  3. Соединим результат со срезом последних, чтобы оставить только записи с этими датами.

Посмотрим на пример кода запроса:


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

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

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

Внимание: Если в одну и ту же секунду (период) были записаны цены от разных поставщиков, этот запрос все равно вернет две строки. В таком случае в последнем пакете запроса необходимо добавить еще одну группировку или выбрать МАКСИМУМ(Поставщик), чтобы гарантированно получить одну строку.

Решение 3: Прямой запрос к физической таблице

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


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

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

Этот метод часто оказывается производительнее на больших объемах данных, так как позволяет СУБД более эффективно использовать индексы по полям Номенклатура + Период.

Особенности работы с отборами

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

Рассмотрим технический нюанс: если в запросе к виртуальной таблице вы не выбираете «лишние» измерения в списке полей (ВЫБРАТЬ Номенклатура, Цена...), платформа 1С на уровне SQL всё равно будет их запрашивать для определения актуальности записи. Поэтому «скрыть» проблему, просто не выбирая поле Поставщик, не получится — вы получите те же дублирующиеся строки номенклатуры, просто без указания поставщика.

Подведем итог:

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

Эти методы позволят вам всегда получать корректные и ожидаемые данные из регистров сведений любой сложности.

← На главную