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

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

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

Разбор исходной задачи и структуры запроса

Представим типичную ситуацию: у нас есть документ РасходнаяНакладная с табличной частью Услуги. Нам необходимо дополнить строки этой таблицы ценами из регистра сведений длгЦеныКонтрагентов. Измерения регистра: Контрагент, Номенклатура, ЕдиницаИзмерения. Ресурс: Цена.

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

Шаг 1. Подготовка данных табличной части во временной таблице

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


ВЫБРАТЬ
    РасходнаяНакладнаяУслуги.Ссылка КАК Ссылка,
    &Период КАК Период,
    &Контрагент КАК Контрагент,
    РасходнаяНакладнаяУслуги.Номенклатура КАК Номенклатура,
    РасходнаяНакладнаяУслуги.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
    РасходнаяНакладнаяУслуги.Количество КАК Количество
ПОМЕСТИТЬ втТЧУслуги
ИЗ
    Документ.РасходнаяНакладная.Услуги КАК РасходнаяНакладнаяУслуги
ГДЕ
    РасходнаяНакладнаяУслуги.Ссылка = &Ссылка
;

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

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

Перейдем к основной части — получению цен. Существует два подхода к фильтрации данных в виртуальных таблицах: использование параметров самой таблицы и использование условий в секции ГДЕ или ПО при соединении. Разберем, почему первый вариант предпочтительнее.

Параметры виртуальной таблицы vs Условия соединения

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


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

Этот подход является наиболее верным с точки зрения производительности SQL. Выясним причину: когда мы передаем условия в параметры виртуальной таблицы СрезПоследних(Период, Условие), платформа 1С формирует SQL-запрос таким образом, что выборка ограничивается еще на этапе получения среза в базе данных. Если же мы просто соединим таблицу и наложим условие в ПО, система может сначала попытаться рассчитать срез последних для всех записей регистра (что для больших баз фатально), а потом уже отсечь лишнее.

Однако использование конструкции В (ВЫБРАТЬ...) внутри параметров имеет свои нюансы. Если во временной таблице втТЧУслуги содержится огромное количество строк (например, тысячи), производительность может снизиться. В таких случаях лучше сначала получить список уникальной номенклатуры из ТЧ во вспомогательную временную таблицу.

Нюансы работы с Моментом Времени и Границами

Часто программисты сталкиваются с тем, что цена «не подхватывается», хотя в регистре она точно есть. Разберем важный технический момент: Граница периода.

Если в качестве параметра &Период передается просто Дата документа, то записи в регистре, сделанные той же секундой, что и документ, могут не попасть в выборку. Это происходит потому, что СрезПоследних работает по принципу «меньше или равно». В 1С для документов рекомендуется использовать объект МоментВремени() или Граница.

В коде 1С при установке параметров запроса это выглядит так:


Запрос.УстановитьПараметр("Период", Новый Граница(Объект.Дата, ВидГраницы.Включая));
// Или для существующего документа:
Запрос.УстановитьПараметр("Период", Объект.МоментВремени());

Это гарантирует, что если цена была установлена одновременно с проведением документа, она будет учтена.

Типичные ошибки при соединении данных

Проанализируем ситуацию, описанную в Сообщении 4: «база косяков с единицами измерения». Это классическая проблема несоответствия типов или значений измерений.

  1. Несоответствие Единиц Измерения: Если в документе указана «шт.», а в регистре цен запись сделана для «Коробка», то левое соединение не найдет соответствия и вернет NULL в поле цены. Всегда проверяйте, чтобы измерения в регистре и поля в ТЧ совпадали по смыслу и типу данных.
  2. Пустые значения (NULL): При использовании ЛЕВОЕ СОЕДИНЕНИЕ, если цены в регистре нет, поле Цена будет содержать NULL. В дальнейших расчетах (например, при умножении на количество) это приведет к ошибке или пустому результату. Рекомендуем всегда использовать функцию ЕСТЬNULL.
  3. Дублирование строк: Если в регистре сведений по какой-то причине образовались записи с одинаковыми измерениями (например, если не все измерения указаны в фильтре запроса), количество строк в результате запроса может увеличиться. Чтобы этого избежать, используйте СГРУППИРОВАТЬ ПО или убедитесь, что фильтр в параметрах виртуальной таблицы покрывает все измерения регистра.

Рассмотрим финальный, оптимизированный вариант запроса с учетом всех исправлений:


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

Рекомендации по индексации

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

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

← На главную