Почему виртуальная таблица СрезПоследних возвращает несколько записей и как получить последнюю запись в 1С

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

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

Анализ проблемы: как работает виртуальная таблица СрезПоследних

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

Если в регистре сведений созданы два измерения — например, Зона и Физлицо, — то ключом уникальности записи выступает пара Период + Зона + Физлицо. Когда для одной зоны в разные периоды времени привязываются разные физические лица, каждое такое сочетание измерений образует самостоятельную цепочку значений. В результате виртуальная таблица СрезПоследних вернет срез для каждой пары (Зона1, Физлицо1) и (Зона1, Физлицо2), то есть обе записи попадут в выборку.

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

Способ 1. Корректировка структуры регистра сведений (рекомендуемый)

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

Выполним следующие действия:

  1. Откроем конфигуратор и перейдем к нужному регистру сведений.
  2. Перенесем реквизит Физлицо из ветки Измерения в ветку Ресурсы.
  3. Оставим в Измерениях только поле Зона.

После этого срез последних будет формироваться исключительно в разрезе измерений (полю Зона), а в ресурсе Физлицо платформа автоматически вернет актуальное значение на указанную дату среза.

Текст запроса в этом случае становится предельно простым:


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

Способ 2. Запрос с поиском максимума периода через временные таблицы

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

Разберем по шагам алгоритм получения среза:

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

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


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

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

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

Способ 3. Использование оконных функций (для платформы 8.3.20 и выше)

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

Рассмотрим пример реализации:


ВЫБРАТЬ
    ВложенныйЗапрос.Зона КАК Зона,
    ВложенныйЗапрос.Физлицо КАК Физлицо,
    ВложенныйЗапрос.Период КАК Период
ИЗ
    (ВЫБРАТЬ
        Таблица.Зона КАК Зона,
        Таблица.Физлицо КАК Физлицо,
        Таблица.Период КАК Период,
        ROW_NUMBER() OVER(
            PARTITION BY Таблица.Зона 
            ORDER BY Таблица.Период УБЫВ, Таблица.НомерСтроки УБЫВ
        ) КАК НомерСтрокиЗаписи
    ИЗ
        РегистрСведений.ФизЛицаЗоныПоПериоду КАК Таблица) КАК ВложенныйЗапрос
ГДЕ
    ВложенныйЗапрос.НомерСтрокиЗаписи = 1

Оконная функция разбивает выборку на секции по измерению Зона, упорядочивает записи внутри каждой секции по убыванию даты и присваивает первой (самой последней по времени) записи номер 1.

Способ 4. Реализация в Системе компоновки данных (СКД) через связи наборов

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

  1. Создадим НаборДанных1: запрос со списком зон (например, выборка из справочника зон или запрос ВЫБРАТЬ РАЗЛИЧНЫЕ Зона ИЗ ...).
  2. Создадим НаборДанных2: запрос к виртуальной таблице РегистрСведений.ФизЛицаЗоныПоПериоду.СрезПоследних(, Зона = &Зона).
  3. На вкладке Связи наборов данных свяжем НаборДанных1 (источник) с НаборДанных2 (приемник) по полю Зона, передав значение в параметр Зона.

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

← На главную