Как в запросе 1С правильно выполнить отбор по полю параметра-ссылки

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

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

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

Почему возникает ошибка при обращении через точку от параметра?

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

Когда мы пишем &Ссылка.Склад, мы пытаемся заставить СУБД выполнить неявное соединение (Implicit Join) с таблицей документа прямо внутри механизма построения виртуальной таблицы. Платформа 1С:Предприятие не поддерживает такую конструкцию в параметрах виртуальных таблиц для обеспечения предсказуемости производительности. Кроме того, если параметр &Ссылка имеет составной тип, такое обращение заставит систему соединяться со всеми возможными таблицами объектов, входящих в этот тип, что крайне неэффективно.

Способ 1: Подготовка параметра в коде 1С (Рекомендуемый)

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

Рассмотрим пример кода на языке 1С:


// Сначала получаем значение склада из объекта или ссылки
СкладЗаказа = СсылкаНаЗаказ.Склад;

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

Запрос.УстановитьПараметр("ДатаНачала", ДатаНачала);
Запрос.УстановитьПараметр("ДатаОкончания", ДатаОкончания);
Запрос.УстановитьПараметр("ПараметрСклад", СкладЗаказа);

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

Этот способ считается "чистым", так как он исключает лишние соединения таблиц внутри запроса и упрощает его отладку. Однако бывают ситуации (например, в сложных схемах компоновки данных), когда нам нужно решить задачу исключительно средствами языка запросов.

Способ 2: Использование временных таблиц

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

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


// Шаг 1: Получаем склад из документа во временную таблицу
ВЫБРАТЬ
    ЗаказПоставщику.Склад КАК Склад
ПОМЕСТИТЬ ВТ_СкладИзЗаказа
ИЗ
    Документ.ЗаказПоставщику КАК ЗаказПоставщику
ГДЕ
    ЗаказПоставщику.Ссылка = &Ссылка;

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

Важное замечание: Обратите внимание на конструкцию Склад В (ВЫБРАТЬ Т.Склад ИЗ ВТ_СкладИзЗаказа КАК Т). В языке запросов 1С нельзя просто написать Склад = ВТ_СкладИзЗаказа.Склад. Использование вложенного запроса к временной таблице внутри параметров — это стандартный способ обхода ограничений платформы.

Способ 3: Применение функции ВЫРАЗИТЬ

В некоторых случаях (особенно если параметр имеет составной тип) помогает типизация параметра с помощью функции ВЫРАЗИТЬ. Это подсказывает планировщику запросов, к какой конкретно таблице нужно обратиться.

Пример использования:

Склад = ВЫРАЗИТЬ(&Ссылка КАК Документ.ЗаказПоставщику).Склад

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

Оптимизация производительности: работа с Номенклатурой

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

Частая ошибка начинающих — использование секции ГДЕ после получения данных из регистра. Посмотрим на разницу:

  1. Плохо: ИЗ Регистр...Обороты(...) ГДЕ Номенклатура В (...). В этом случае СУБД сначала прочитает все обороты по выбранному складу за период, и только потом отфильтрует их. Если товаров в базе много, это будет работать медленно.
  2. Хорошо: ИЗ Регистр...Обороты(&ДатаНачала, &ДатаОкончания, , Номенклатура В (ВЫБРАТЬ...)). Здесь СУБД сразу применит фильтр к индексам и прочитает только нужные записи.

Для реализации оптимального запроса сначала поместим товары заказа во временную таблицу:


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

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

Разбор типичной ошибки ИЗ &Ссылка

В сообщениях форума один из участников пытался использовать конструкцию: ИЗ &Ссылка КАК заказПоставщику. Это приводит к ошибке "Неверные параметры", потому что параметр &Ссылка — это атомарное значение (уникальный идентификатор), а не таблица. Источником данных в секции ИЗ может быть только таблица базы данных (например, Документ.ЗаказПоставщику) или временная таблица.

Чтобы получить данные из объекта, на который указывает ссылка, всегда нужно явно указывать таблицу метаданных и накладывать условие ГДЕ Ссылка = &Ссылка.

Подведем итоги

Для корректного выбора поля параметра в запросе 1С мы рекомендуем придерживаться следующего алгоритма:

  1. Если есть возможность, получите значение реквизита (склада, организации и т.д.) в коде и передайте его как готовое значение отдельным параметром.
  2. Если логика должна быть полностью внутри запроса, используйте временные таблицы для получения нужного значения и передавайте их в параметры виртуальных таблиц через вложенный запрос.
  3. Всегда стремитесь ограничивать выборку внутри скобок параметров виртуальной таблицы, а не в секции ГДЕ, чтобы обеспечить высокую скорость работы системы.

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

← На главную