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

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

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

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

Метод 1: Использование ЛЕВОГО СОЕДИНЕНИЯ и функции ЕСТЬNULL

Этот способ считается классическим и наиболее производительным в среде 1С. Основная идея заключается в том, что мы соединяем нашу основную выборку (например, таблицу товаров) с виртуальной таблицей СрезПоследних. Если в регистре сведений нет записи на указанную дату, поля из этой таблицы примут значение NULL.

Разберем по шагам, как это работает:

  1. Мы выбираем данные из основной таблицы (например, Справочник.Номенклатура или временная таблица со списком нужных позиций).
  2. Присоединяем РегистрСведений.ЦеныНоменклатуры.СрезПоследних по левому соединению.
  3. Используем функцию ЕСТЬNULL, чтобы подставить значение из второго источника, если первый вернул NULL.

Проанализируем пример кода:


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

В данном примере, если ЦеныСрез.Цена окажется NULL, система автоматически перейдет к вычислению цены из регистра бухгалтерии. Это происходит быстро, так как СУБД эффективно обрабатывает подобные соединения.

Метод 2: Приоритетное объединение через ОБЪЕДИНИТЬ ВСЕ и ПЕРВЫЕ 1

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

Выясним причину популярности этого метода: он гарантирует, что мы возьмем данные из первого источника, если они там есть, и только при их отсутствии — из второго.

Рассмотрим структуру такого запроса:


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

    ОБЪЕДИНИТЬ ВСЕ

    ВЫБРАТЬ
        Поступления.Цена,
        2
    ИЗ
        Документ.ПоступлениеТоваровУслуг.Товары КАК Поступления
    ГДЕ
        Поступления.Номенклатура = &Номенклатура
    ) КАК Т
УПОРЯДОЧИТЬ ПО
    Т.Приоритет

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

Метод 3: Проверка через агрегатную функцию КОЛИЧЕСТВО (с использованием временных таблиц)

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

Сначала поместим результат среза последних во временную таблицу ВТСрез:


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

Затем в финальном запросе мы можем применить конструкцию ВЫБОР, проверяя наличие данных в этой таблице. Однако, важно помнить: если ВТСрез пустая, то обычное соединение с ней (ВНУТРЕННЕЕ СОЕДИНЕНИЕ) уничтожит весь результат запроса. Поэтому используем только ЛЕВОЕ СОЕДИНЕНИЕ.

Пример реализации финальной части:


ВЫБРАТЬ
    Список.Номенклатура,
    ВЫБОР
        КОГДА ВТСрез.Номенклатура ЕСТЬ НЕ NULL ТОГДА ВТСрез.Цена
        ИНАЧЕ ВТАльтернатива.Цена
    КОНЕЦ КАК РезультатЦена
ИЗ
    ВТ_Список КАК Список
        ЛЕВОЕ СОЕДИНЕНИЕ ВТСрез КАК ВТСрез ПО Список.Номенклатура = ВТСрез.Номенклатура
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_АльтернативныеЦены КАК ВТАльтернатива ПО Список.Номенклатура = ВТАльтернатива.Номенклатура

Метод 4: Логика на стороне встроенного языка (1С:Предприятие)

Не всегда стоит пытаться решить задачу одним огромным запросом. Как было справедливо замечено в обсуждении, иногда эффективнее выполнить два простых и последовательных запроса в коде 1С. Это особенно актуально, если альтернативный источник (например, регистр бухгалтерии) содержит миллионы записей и обращение к нему «на всякий случай» внутри сложного соединения может существенно замедлить работу системы.

Разберем пример кода:


Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Цена ИЗ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, Номенклатура = &Номенклатура)";
Запрос.УстановитьПараметр("Дата", Период);
Запрос.УстановитьПараметр("Номенклатура", ВыбраннаяНоменклатура);

Результат = Запрос.Выполнить();

Если Результат.Пустой() Тогда
    // Если в РС ничего не нашли, идем в Регистр Бухгалтерии
    Запрос.Текст = "ВЫБРАТЬ СуммаОстаток / КоличествоОстаток КАК Цена ИЗ РегистрБухгалтерии.Хозрасчетный.Остатки(...)";
    Результат = Запрос.Выполнить();
КонецЕсли;

Преимущества такого подхода:

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

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

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

Подводя итог, можно сказать: если ваша задача — построить быстрый отчет по тысячам позиций, используйте Метод 1 (ЕСТЬNULL). Если же вам нужно получить значение для одной строки в форме документа, лучше и надежнее будет Метод 4 (Два последовательных запроса в коде).

← На главную