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