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

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

В практике разработки на платформе 1С:Предприятие часто возникает ситуация, когда стандартная виртуальная таблица СрезПоследних работает не так, как ожидается. Представим регистр сведений «Цены номенклатуры» с измерениями Номенклатура и ВидЦены. Если для одного товара установлены и оптовая, и розничная цены, то СрезПоследних вернет две строки. Но что делать, если нам нужно получить только одну — ту, которая была внесена в базу самой последней, независимо от вида цены?

В этой статье мы подробно разберем, почему так происходит.

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

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

Способ 1. Последовательная фильтрация через временные таблицы

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

Шаг 1. Получение максимального периода для каждого объекта.

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


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

Шаг 2. Выбор конкретной записи, если на один период пришлось несколько движений.

Рассмотрим ситуацию: что если в одну и ту же секунду были записаны и розничная, и оптовая цены? Чтобы запрос не вернул две строки, мы должны применить дополнительную группировку. Например, выберем максимальный ВидЦены или используем МАКСИМУМ(Регистратор).


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

Шаг 3. Получение итоговых данных.

Теперь, когда у нас есть уникальный ключ (Номенклатура + Период + ВидЦены), мы можем легко получить любые ресурсы или реквизиты этой записи.


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

Способ 2. Использование физической таблицы и условия «В (ВЫБРАТЬ...)»

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


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

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

Как гарантировать уникальность? Понятие Момента Времени

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

  1. Период — дата и время события.
  2. Регистратор — ссылка на документ, сделавший запись (более поздний документ имеет «большую» ссылку в терминах БД).
  3. НомерСтроки — если один документ делает несколько записей по одному объекту.

В запросах для этого удобно использовать связку Период и Регистратор, либо поле МоментВремени (если работа ведется с объектной техникой, а не чистыми запросами к регистрам сведений).

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

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

Используя предложенные методики, вы сможете гарантированно получать только одну, актуальную на текущий момент запись, корректно обрабатывая наличие нескольких измерений в регистрах сведений 1С.

← На главную