Почему запрос в коде 1С не возвращает данные, хотя в консоли все работает?

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

В практике разработчика 1С часто встречается загадочная ситуация: один и тот же текст запроса, запущенный в Консоли запросов, выдает ожидаемые строки, а при выполнении внутри модуля формы — возвращает пустой результат. Особенно это сбивает с толку, когда конфигурация на тестовой и основной базе идентична, и на тесте всё работает корректно. Разберем эту ситуацию на практическом примере из конфигурации «Зарплата и Управление Персоналом».

Анализ исходной ситуации

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

Посмотрим на фрагмент кода, который устанавливает параметры запроса:


Запрос.УстановитьПараметр("тДатаНач", НачалоМесяца(ДатаНачалаБП));
Запрос.УстановитьПараметр("тДатаКон", КонецМесяца(ДатаОкончанияБП));
Запрос.УстановитьПараметр("ГоловнаяОрганизация", Справочники.Организации.НайтиПоКоду("000000001"));
Запрос.УстановитьПараметр("тГрафик", Справочники.ГрафикиРаботы.НайтиПоНаименованию("Пятидневка 40"));
Запрос.УстановитьПараметр("парамПодразделение", УдалитьПодразделениеОрганизации);

Причина проблемы: ловушка методов поиска по строке

Проанализируем используемые методы НайтиПоКоду и НайтиПоНаименованию. Это «слабое звено» в коде. В рассматриваемом нами случае выяснилось, что в справочнике ГрафикиРаботы существовало два элемента с одинаковым наименованием «Пятидневка 40». Один из них был корректно заполнен, а второй — пустой и помеченный на удаление.

Метод НайтиПоНаименованию возвращает первую попавшуюся ссылку, соответствующую строке. В основной базе система нашла именно помеченный на удаление «пустой» график. В результате параметр &тГрафик получил ссылку на объект, к которому в регистре ГрафикиРаботыПоВидамВремени не было привязано ни одной записи. Естественно, условие связи в запросе не сработало, и вся выборка «схлопнулась» до нуля.

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

Чтобы избежать подобных ошибок, мы рекомендуем отказаться от жесткого кодирования строковых наименований. Рассмотрим более надежные способы:

  1. Использование Предопределенных элементов: Если объект (организация, график, вид расчета) является базовым для логики системы, его лучше сделать предопределенным. Тогда обращение будет выглядеть так: Справочники.Организации.ГлавнаяОрганизация.
  2. Использование Констант или Регистров сведений: Создайте настройку в программе, где пользователь сам выберет нужный график из справочника. Код будет считывать эту настройку (ссылку), что исключит ошибки поиска по строке.
  3. Проверка на пустую ссылку: Если вы все же используете поиск, всегда проверяйте результат перед установкой параметра:
    
    ГрафикСсылка = Справочники.ГрафикиРаботы.НайтиПоНаименованию("Пятидневка 40");
    Если ГрафикСсылка.Пустая() Тогда
        Сообщить("График работы не найден!");
        Возврат;
    КонецЕсли;
    

Разбор структуры сложного запроса

Посмотрим на структуру самого запроса. Он содержит множество соединений. В таких случаях очень сложно понять, в каком именно месте «теряются» данные. Проанализируем типичный фрагмент из примера:


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

Важный момент: Если в первом подзапросе (источнике связи) нет данных, то ЛЕВОЕ СОЕДИНЕНИЕ не поможет — общий результат все равно будет пустым. Мы рекомендуем использовать Временные таблицы вместо вложенных подзапросов.

Оптимизация с помощью Менеджера временных таблиц

Разобьем сложную задачу на этапы. Это не только ускорит работу СУБД, но и значительно упростит отладку. Мы можем пошагово проверить содержимое каждой временной таблицы.


МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;

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

// Шаг 2: Теперь мы можем проверить, есть ли данные в ВТ_Параметры
Запрос.Текст = "ВЫБРАТЬ КОЛИЧЕСТВО(*) ИЗ ВТ_Параметры";
РезультатПроверки = Запрос.Выполнить().Выгрузить()[0][0];
// Если РезультатПроверки = 0, значит проблема в срезе последних или параметрах связи.

Дополнительные факторы, влияющие на результат

Если параметры установлены верно, но данных все равно нет, проанализируем следующие системные механизмы:

1. Row Level Security (RLS)

Выясним, под каким пользователем запускается код. Консоль запросов часто запускается под «Полными правами», которые игнорируют ограничения доступа. В то же время модуль формы подчиняется правилам RLS. Если у пользователя нет прав на чтение конкретного подразделения или организации, запрос в коде вернет пустую таблицу. Для проверки можно временно использовать привилегированный режим:


УстановитьПривилегированныйРежим(Истина);
РезЗапроса = Запрос.Выполнить().Выгрузить();
УстановитьПривилегированныйРежим(Ложь);

2. Параметры виртуальных таблиц

Обратим внимание на работу со СрезПоследних. В исходном коде автора условие по организации стоит внутри подзапроса в секции ГДЕ. Это классическая ошибка. Для виртуальных таблиц условия нужно передавать обязательно в параметры самой таблицы:

Плохо: ...СрезПоследних(&тДатаКон) ГДЕ Организация = &Орг

Хорошо: ...СрезПоследних(&тДатаКон, Организация = &Орг)

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

3. Кэш сервера и статистика СУБД

В клиент-серверном варианте работы (SQL) данные могут «застревать» в кэше. Если вы только что изменили данные, а запрос их не видит — попробуйте очистить кэш или перезапустить сеанс. Также для баз ЗУП критична актуальность статистики SQL-сервера, так как сложные соединения при неверном плане выполнения могут просто не завершаться вовремя.

Резюме и рекомендации

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

← На главную