Почему не выводится склад из регистра сведений при использовании ВЫБРАТЬ ПЕРВЫЕ 1 в запросе?

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

В практике разработки на платформе 1С:Предприятие часто возникает задача получения уникальных данных из регистра сведений, например, актуального склада для основного средства или цены для номенклатуры. Одной из самых распространенных ошибок начинающих разработчиков является использование оператора ВЫБРАТЬ ПЕРВЫЕ 1 внутри пакета запроса для создания временной таблицы. Разберем подробно, почему этот подход приводит к потере данных и как правильно составить запрос, чтобы получить корректный результат.

Проблема использования оператора ВЫБРАТЬ ПЕРВЫЕ 1

Проанализируем ситуацию: автору необходимо сопоставить данные об оборотах из регистра накопления ПартииТоваровНаСкладах с данными о соответствии основного средства и склада из регистра сведений СкладыХраненияОстатковГСМвОС. Когда автор использует ВЫБРАТЬ ПЕРВЫЕ 1, запрос перестает выводить данные для большинства строк. Выясним причину этого поведения.

Оператор ВЫБРАТЬ ПЕРВЫЕ 1 ограничивает весь результат выборки первой же попавшейся строкой, удовлетворяющей условиям. Если во временную таблицу Склады выбирается только одна запись, то при последующем соединении с основной таблицей оборотов (где могут быть сотни различных складов и регистраторов), данные «подтянутся» только к той строке, которая совпадет с этой единственной записью. Для всех остальных строк в полях временной таблицы будет стоять значение NULL.

Рассмотрим пример ошибочного кода:


ВЫБРАТЬ ПЕРВЫЕ 1
    СкладыХраненияОстатковГСМвОССрезПоследних.ОсновноеСредство КАК ОсновноеСредство,
    СкладыХраненияОстатковГСМвОССрезПоследних.Склад КАК Склад
ПОМЕСТИТЬ Склады
ИЗ
    РегистрСведений.СкладыХраненияОстатковГСМвОС.СрезПоследних(&КонецПериода, ) КАК СкладыХраненияОстатковГСМвОССрезПоследних

Здесь во временную таблицу попадет всего одна пара ОсновноеСредство — Склад. Если в документе-регистраторе указано другое ОС или другой склад, соединение не сработает. Нам же требуется получить по одной записи для каждого уникального основного средства. В этом и заключается концептуальная разница.

Решение проблемы через группировку и агрегатные функции

Вместо жесткого ограничения количества строк во всем пакете, мы должны использовать механизм схлопывания дублей. Если для одного объекта (в нашем случае ОсновноеСредство) в регистре может быть несколько записей на одну и ту же дату, и нам «все равно какой» склад выбрать (или нужен какой-то один из них), следует применить СГРУППИРОВАТЬ ПО.

Разберем по шагам правильный алгоритм формирования временной таблицы:

  1. Выбираем ключевое поле (измерение), по которому будет идти связь (ОсновноеСредство).
  2. Применяем агрегатную функцию МАКСИМУМ или МИНИМУМ к ресурсу (Склад).
  3. Группируем результат по ключевому полю.

Посмотрим на пример правильного формирования временной таблицы:


ВЫБРАТЬ
    СкладыХраненияОстатковГСМвОССрезПоследних.ОсновноеСредство КАК ОсновноеСредство,
    МАКСИМУМ(СкладыХраненияОстатковГСМвОССрезПоследних.Склад) КАК Склад
ПОМЕСТИТЬ Склады
ИЗ
    РегистрСведений.СкладыХраненияОстатковГСМвОС.СрезПоследних(&КонецПериода, ) КАК СкладыХраненияОстатковГСМвОССрезПоследних
СГРУППИРОВАТЬ ПО
    СкладыХраненияОстатковГСМвОССрезПоследних.ОсновноеСредство

В данном случае платформа выберет для каждого основного средства ровно одну ссылку на склад. Функция МАКСИМУМ для ссылочных типов данных выбирает значение с наибольшим внутренним идентификатором (GUID). Это позволяет избежать дублирования строк при последующем соединении (JOIN).

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

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

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

Во-вторых, если записи имеют одинаковый период (секунду), система 1С использует дополнительное поле НомерЗаписи (в некоторых случаях) или просто выдает все актуальные на этот момент состояния. Именно здесь группировка становится обязательной.

Оптимизация соединения таблиц

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

ПО ПартииТоваровНаСкладахОбороты.Склад.Ссылка = Склады.Склад.Ссылка

Использование конструкции .Ссылка в запросах — это избыточное разыменование. Ссылочные поля в 1С уже содержат идентификатор ссылки. Лишнее обращение через точку заставляет SQL-сервер делать дополнительное соединение с таблицей справочника, что снижает производительность. Правильнее писать просто:

ПО ПартииТоваровНаСкладахОбороты.Склад = Склады.Склад

Также проанализируем выбор типа соединения. Автор использовала ПОЛНОЕ СОЕДИНЕНИЕ. Это редко оправдано в отчетах по документам. Обычно нам нужны все записи из регистра накопления и только те данные из регистра сведений, которые им соответствуют. Для этого идеально подходит ЛЕВОЕ СОЕДИНЕНИЕ.

Итоговый вариант оптимизированного запроса будет выглядеть так:


ВЫБРАТЬ
    СкладыХраненияОстатковГСМвОССрезПоследних.ОсновноеСредство КАК ОсновноеСредство,
    МАКСИМУМ(СкладыХраненияОстатковГСМвОССрезПоследних.Склад) КАК Склад
ПОМЕСТИТЬ ВТ_Склады
ИЗ
    РегистрСведений.СкладыХраненияОстатковГСМвОС.СрезПоследних(&КонецПериода, ) КАК СкладыХраненияОстатковГСМвОССрезПоследних
СГРУППИРОВАТЬ ПО
    СкладыХраненияОстатковГСМвОССрезПоследних.ОсновноеСредство
;

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

Дополнительные советы по повышению эффективности

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

Таким образом, мы выяснили, что ошибка заключалась в глобальном ограничении выборки оператором ПЕРВЫЕ 1. Заменив его на группировку с агрегацией, мы получили надежный механизм получения уникальных данных для каждой строки отчета.

← На главную