В процессе разработки на платформе 1С:Предприятие 8 программисты часто сталкиваются с задачей, когда необходимо получить данные из регистра, ограничив выборку значением, которое хранится в одном из реквизитов документа или справочника, переданного в запрос как параметр. Типичный пример: в запрос передается ссылка на ЗаказПоставщику, и нужно получить обороты только по тому складу, который указан в этом заказе.
Попытка написать условие напрямую в параметрах виртуальной таблицы, например, Склад = &Ссылка.Склад, приводит к ошибке синтаксиса. В этой статье мы подробно разберем, почему возникает эта проблема, проанализируем ограничения платформы и рассмотрим несколько эффективных способов реализации такой задачи.
Разберем первопричину. Язык запросов 1С имеет специфическое ограничение: внутри параметров виртуальных таблиц (таких как Остатки, Обороты, СрезПоследних) нельзя использовать разыменование ссылочных параметров. Система ожидает там либо конкретное значение, либо простой параметр, либо вложенный запрос.
Когда мы пишем &Ссылка.Склад, мы пытаемся заставить СУБД выполнить неявное соединение (Implicit Join) с таблицей документа прямо внутри механизма построения виртуальной таблицы. Платформа 1С:Предприятие не поддерживает такую конструкцию в параметрах виртуальных таблиц для обеспечения предсказуемости производительности. Кроме того, если параметр &Ссылка имеет составной тип, такое обращение заставит систему соединяться со всеми возможными таблицами объектов, входящих в этот тип, что крайне неэффективно.
Самый надежный и производительный метод — это извлечь значение склада из заказа еще до выполнения запроса и передать его как отдельный готовый параметр. Это позволяет виртуальной таблице работать максимально быстро, так как она сразу получает конкретное значение для фильтрации индексов.
Рассмотрим пример кода на языке 1С:
// Сначала получаем значение склада из объекта или ссылки
СкладЗаказа = СсылкаНаЗаказ.Склад;
// Создаем запрос и устанавливаем параметры
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТоварыНаСкладахОбороты.Номенклатура КАК Номенклатура,
| ТоварыНаСкладахОбороты.ВНаличииРасход КАК Товарооборот
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Обороты(&ДатаНачала, &ДатаОкончания, , Склад = &ПараметрСклад) КАК ТоварыНаСкладахОбороты";
Запрос.УстановитьПараметр("ДатаНачала", ДатаНачала);
Запрос.УстановитьПараметр("ДатаОкончания", ДатаОкончания);
Запрос.УстановитьПараметр("ПараметрСклад", СкладЗаказа);
Результат = Запрос.Выполнить();
Этот способ считается "чистым", так как он исключает лишние соединения таблиц внутри запроса и упрощает его отладку. Однако бывают ситуации (например, в сложных схемах компоновки данных), когда нам нужно решить задачу исключительно средствами языка запросов.
Если нам принципиально получить склад внутри пакета запросов, мы можем воспользоваться механизмом временных таблиц. Сначала мы выберем склад из документа, поместим его во временную таблицу, а затем используем эту таблицу для фильтрации в основном запросе.
Проанализируем структуру такого запроса:
// Шаг 1: Получаем склад из документа во временную таблицу
ВЫБРАТЬ
ЗаказПоставщику.Склад КАК Склад
ПОМЕСТИТЬ ВТ_СкладИзЗаказа
ИЗ
Документ.ЗаказПоставщику КАК ЗаказПоставщику
ГДЕ
ЗаказПоставщику.Ссылка = &Ссылка;
////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Используем временную таблицу в параметрах виртуальной таблицы
ВЫБРАТЬ
ТоварыНаСкладахОбороты.Номенклатура,
СУММА(ТоварыНаСкладахОбороты.ВНаличииРасход) КАК Товарооборот
ИЗ
РегистрНакопления.ТоварыНаСкладах.Обороты(
&ДатаНачала,
&ДатаОкончания, ,
Склад В (ВЫБРАТЬ Т.Склад ИЗ ВТ_СкладИзЗаказа КАК Т)
) КАК ТоварыНаСкладахОбороты
СГРУППИРОВАТЬ ПО
ТоварыНаСкладахОбороты.Номенклатура;
Важное замечание: Обратите внимание на конструкцию Склад В (ВЫБРАТЬ Т.Склад ИЗ ВТ_СкладИзЗаказа КАК Т). В языке запросов 1С нельзя просто написать Склад = ВТ_СкладИзЗаказа.Склад. Использование вложенного запроса к временной таблице внутри параметров — это стандартный способ обхода ограничений платформы.
В некоторых случаях (особенно если параметр имеет составной тип) помогает типизация параметра с помощью функции ВЫРАЗИТЬ. Это подсказывает планировщику запросов, к какой конкретно таблице нужно обратиться.
Пример использования:
Склад = ВЫРАЗИТЬ(&Ссылка КАК Документ.ЗаказПоставщику).Склад
Однако стоит учитывать, что этот метод работает не во всех версиях платформы 1С и не во всех типах виртуальных таблиц. Если система выдает ошибку, следует вернуться к Способу 2 с временными таблицами или к Способу 1 с передачей параметра из кода.
Разберем еще один важный момент, упомянутый в обсуждении. Если ваша задача — получить обороты по товарам, которые содержатся в конкретном заказе, крайне важно перенести отбор по номенклатуре в параметры виртуальной таблицы.
Частая ошибка начинающих — использование секции ГДЕ после получения данных из регистра. Посмотрим на разницу:
ИЗ Регистр...Обороты(...) ГДЕ Номенклатура В (...). В этом случае СУБД сначала прочитает все обороты по выбранному складу за период, и только потом отфильтрует их. Если товаров в базе много, это будет работать медленно.ИЗ Регистр...Обороты(&ДатаНачала, &ДатаОкончания, , Номенклатура В (ВЫБРАТЬ...)). Здесь СУБД сразу применит фильтр к индексам и прочитает только нужные записи.Для реализации оптимального запроса сначала поместим товары заказа во временную таблицу:
ВЫБРАТЬ РАЗЛИЧНЫЕ
ЗаказТовары.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ВТ_ТоварыЗаказа
ИЗ
Документ.ЗаказПоставщику.Товары КАК ЗаказТовары
ГДЕ
ЗаказТовары.Ссылка = &Ссылка;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ТоварыОбороты.Номенклатура,
ТоварыОбороты.ВНаличииРасход
ИЗ
РегистрНакопления.ТоварыНаСкладах.Обороты(
&ДатаНачала,
&ДатаОкончания, ,
Номенклатура В (ВЫБРАТЬ Т.Номенклатура ИЗ ВТ_ТоварыЗаказа КАК Т)
И Склад = &ПараметрСклад
) КАК ТоварыОбороты;
В сообщениях форума один из участников пытался использовать конструкцию: ИЗ &Ссылка КАК заказПоставщику. Это приводит к ошибке "Неверные параметры", потому что параметр &Ссылка — это атомарное значение (уникальный идентификатор), а не таблица. Источником данных в секции ИЗ может быть только таблица базы данных (например, Документ.ЗаказПоставщику) или временная таблица.
Чтобы получить данные из объекта, на который указывает ссылка, всегда нужно явно указывать таблицу метаданных и накладывать условие ГДЕ Ссылка = &Ссылка.
Для корректного выбора поля параметра в запросе 1С мы рекомендуем придерживаться следующего алгоритма:
ГДЕ, чтобы обеспечить высокую скорость работы системы.Следуя этим правилам, вы избежите синтаксических ошибок и обеспечите оптимальную нагрузку на сервер базы данных.