Как правильно использовать реквизиты объекта в запросе 1С?

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

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

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

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


Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ПериодН", Период.ДатаНачала);
Запрос.УстановитьПараметр("ПериодК", Период.ДатаОкончания);
Запрос.УстановитьПараметр("Сумма", Сумма);
Запрос.Текст = 
    "ВЫБРАТЬ
    |    ЗаказКлиента.Номер КАК Номер,
    |    ЗаказКлиента.Дата КАК Дата,
    |    ЗаказКлиента.СуммаДокумента КАК СуммаДокумента
    |ИЗ
    |    Документ.ЗаказКлиента КАК ЗаказКлиента
    |ГДЕ
    |    (ЗаказКлиента.Дата >= Период.ДатаНачала И 
    |    ЗаказКлиента.Дата <= Период.ДатаОкончания) И
    |    ЗаказКлиента.СуммаДокумента > Сумма";

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

При выполнении этого кода платформа выдаст ошибку "Поле не найдено: Период.ДатаНачала". Почему так происходит?

Дело в том, что СУБД (система управления базами данных), на стороне которой выполняется запрос, ничего не знает о переменных в оперативной памяти вашего сервера приложений или клиентского компьютера. Запрос "общается" исключительно с таблицами базы данных. СУБД пытается найти таблицу с именем Период и поле ДатаНачала в базе данных, не находит их и возвращает ошибку. Любые локальные данные перед отправкой запроса СУБД необходимо преобразовать в параметры.

Разберем по шагам: правильная передача параметров

Чтобы СУБД поняла, что мы хотим сравнить данные базы с локальными переменными нашего объекта, мы должны использовать параметры запроса. В тексте запроса параметры всегда обозначаются специальным префиксом — символом амперсанда &.

Посмотрим на пример правильного написания кода:


Запрос = Новый Запрос;

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

// В тексте запроса обращаемся к параметрам через префикс &
Запрос.Текст = 
    "ВЫБРАТЬ
    |    ЗаказКлиента.Номер КАК Номер,
    |    ЗаказКлиента.Дата КАК Дата,
    |    ЗаказКлиента.Партнер КАК Партнер,
    |    ЗаказКлиента.СуммаДокумента КАК СуммаДокумента
    |ИЗ
    |    Документ.ЗаказКлиента КАК ЗаказКлиента
    |ГДЕ
    |    ЗаказКлиента.Дата МЕЖДУ &ПериодНачала И &ПериодОкончания
    |    И ЗаказКлиента.СуммаДокумента > &ОграничениеПоСумме";

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

В данном случае метод Запрос.УстановитьПараметр() сопоставляет имя параметра (строковое значение без амперсанда) с конкретным значением из оперативной памяти. В самом SQL-запросе конструкция &ПериодНачала заменяется на переданное значение даты.

Что делать, если объект еще не записан в базу данных?

Рассмотрим более сложный, но крайне частый сценарий на практике. Допустим, пользователь заполняет новый документ на форме, данные еще не сохранены в базе (объект физически отсутствует в СУБД), но нам нужно выполнить по нему сложный расчет или проверку остатков.

Здесь важно разделять реквизиты шапки и данные табличных частей:

  1. Для реквизитов шапки: Проблема решается стандартным способом. Значения незаписанных реквизитов (например, склад, организация, контрагент) уже находятся в оперативной памяти объекта на сервере. Мы просто передаем их через метод УстановитьПараметр(), как было описано выше.
  2. Для табличных частей (ТЧ): Если нам нужно проанализировать строки незаписанной табличной части (например, проверить остатки товаров из ТЧ "Товары"), СУБД об этих строках ничего не знает. Напрямую прочитать их запросом из БД невозможно.

Решением этой проблемы является выгрузка табличной части в Таблицу Значений с последующей передачей ее в качестве параметра во временную таблицу запроса. Разберем этот алгоритм на примере:


// 1. Выгружаем незаписанную табличную часть "Товары" в Таблицу Значений в памяти
ТаблицаТоваров = ЭтотОбъект.Товары.Выгрузить();

// 2. Создаем запрос и передаем таблицу значений как параметр
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ВнешнийИсточникТоваров", ТаблицаТоваров);

// 3. Описываем создание временной таблицы в запросе
Запрос.Текст = 
    "ВЫБРАТЬ
    |    ТЧ.Номенклатура КАК Номенклатура,
    |    ТЧ.Количество КАК Количество
    |ПОМЕСТИТЬ ВТ_ТоварыДокумента
    |ИЗ
    |    &ВнешнийИсточникТоваров КАК ТЧ
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |    ВТ.Номенклатура КАК Номенклатура,
    |    СУММА(ВТ.Количество) КАК КоличествоВДокументе,
    |    ЕСТЬNULL(СвободныеОстатки.ВНаличииОстаток, 0) КАК ОстатокНаСкладе
    |ИЗ
    |    ВТ_ТоварыДокумента КАК ВТ
    |    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.СвободныеОстатки.Остатки КАК СвободныеОстатки
    |        ПО ВТ.Номенклатура = СвободныеОстатки.Номенклатура";

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

Благодаря конструкции ПОМЕСТИТЬ ВТ_ТоварыДокумента ИЗ &ВнешнийИсточникТоваров, платформа автоматически создает в СУБД временную таблицу и наполняет её строками из нашей оперативной памяти. После этого мы можем производить любые соединения и группировки с реальными таблицами базы данных.

Опасность разыменования ссылочных параметров

Иногда разработчики, чтобы не передавать множество параметров по отдельности, передают в запрос ссылку на сам текущий объект: Запрос.УстановитьПараметр("СсылкаНаОбъект", Ссылка);, а затем пишут условия вида:

ГДЕ ЗаказКлиента.Менеджер = &СсылкаНаОбъект.Менеджер

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

Правильный подход: Если объект уже записан, и вам нужно сопоставить его данные, делайте явное соединение таблиц по ссылке в секции ИЗ или передавайте конкретные примитивные значения (числа, даты, простые ссылки) отдельными параметрами.

Альтернатива запросам: использование функций БСП

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

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


// Пример 1: Получение одного реквизита по ссылке
ТекущийМенеджер = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаДокумент, "Менеджер");

// Пример 2: Получение структуры нескольких реквизитов за одно обращение
СтруктураДанных = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(СсылкаНаДокумент, "Партнер, СуммаДокумента, Валюта");

ИмяПартнера = СтруктураДанных.Партнер;
СуммаЗаказа = СтруктураДанных.СуммаДокумента;

Эти функции под капотом используют механизм кэширования и строят максимально производительные SQL-запросы "на лету", предотвращая типичные ошибки ручного написания кода и экономя ваше время.

Контекст выполнения (Клиент и Сервер)

В заключение напомним важное правило клиент-серверной архитектуры 1С:Предприятия. Любые запросы к базе данных могут выполняться строго на стороне сервера. Реквизиты экранных форм (переменные, объявленные на клиенте, или элементы управления формы) физически недоступны на сервере без их явной передачи.

Если пользователь ввел данные в поле ввода на форме, вы должны вызвать серверный метод (например, &НаСервере или &НаСервереБезКонтекста), передать эти данные в качестве параметров процедуры, и уже там установить их в качестве параметров объекта Запрос.

← На главную