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

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

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

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

---

Анализ исходного кода и классическая ошибка оптимизации

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


Запрос = Новый Запрос; 
Запрос.Текст = "ВЫБРАТЬ 
| ЗаявленияПоступающихСрезПоследних.UIDНаправленияПодготовки КАК UIDНаправленияПодготовки, 
| ЗаявленияПоступающихСрезПоследних.ФизическоеЛицо КАК ФизическоеЛицо 
|ИЗ 
| РегистрСведений.ЗаявленияПоступающих.СрезПоследних КАК ЗаявленияПоступающихСрезПоследних 
|ГДЕ 
| ЗаявленияПоступающихСрезПоследних.ФизическоеЛицо = &ФизическоеЛицо"; 

Запрос.УстановитьПараметр("ФизическоеЛицо", Объект.ФизическоеЛицо.Ссылка); 
РезультатЗапроса = Запрос.Выполнить; 
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать; 

Если ВыборкаДетальныеЗаписи.Следующий() Тогда 
    Объект.UIDНаправленияПодготовки = ВыборкаДетальныеЗаписи.UIDНаправленияПодготовки; 
КонецЕсли;

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

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

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

Ошибка 2. Избыточное обращение к свойству «Ссылка». Конструкция Объект.ФизическоеЛицо.Ссылка является избыточной и потенциально опасной. Если реквизит ФизическоеЛицо на форме уже имеет тип ссылки (что стандартно для конфигураций), обращение через точку порождает дополнительный неявный запрос к базе данных для получения этой ссылки. Более того, если значение реквизита не заполнено или имеет некорректный составной тип, такое обращение может вернуть неожиданный результат.

Давайте исправим эти архитектурные недочеты и перепишем запрос в правильном, оптимизированном виде:


Запрос = Новый Запрос; 
Запрос.Текст = "ВЫБРАТЬ 
| ЗаявленияПоступающихСрезПоследних.UIDНаправленияПодготовки КАК UIDНаправленияПодготовки, 
| ЗаявленияПоступающихСрезПоследних.ФизическоеЛицо КАК ФизическоеЛицо 
|ИЗ 
| РегистрСведений.ЗаявленияПоступающих.СрезПоследних(, ФизическоеЛицо = &ФизическоеЛицо) КАК ЗаявленияПоступающихСрезПоследних"; 

Запрос.УстановитьПараметр("ФизическоеЛицо", Объект.ФизическоеЛицо); 
РезультатЗапроса = Запрос.Выполнить(); 

Если Не РезультатЗапроса.Пустой() Тогда
    ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать(); 
    Если ВыборкаДетальныеЗаписи.Следующий() Тогда 
        Объект.UIDНаправленияПодготовки = ВыборкаДетальныеЗаписи.UIDНаправленияПодготовки; 
    КонецЕсли;
КонецЕсли;

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

---

Глубокие технические причины расхождения результатов запроса

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

1. Работа в «обреченной» транзакции (Отказ = Истина)

Одна из наиболее частых скрытых причин — выполнение кода внутри транзакции, которая была помечена к отмене. Например, код выполняется в обработчике события ПередЗаписью или ПриЗаписи, где в какой-то момент бизнес-логика установила флаг Отказ = Истина, либо произошло неявное исключение, перехваченное блоком Попытка...Исключение.

С технической точки зрения на уровне СУБД (особенно ярко это проявляется в PostgreSQL) транзакция в этот момент переходит в состояние aborted. Любые последующие SQL-запросы в рамках этой сессии СУБД игнорирует или блокирует, возвращая пустые наборы данных вместо генерации явной ошибки в 1С. В то же время Консоль запросов запускается вне этой транзакции, в отдельном контексте сеанса, и без проблем считывает данные напрямую из таблиц базы.

2. Рассинхронизация и повреждение локального кэша метаданных

В процессе разработки, особенно при интенсивном использовании Хранилища конфигурации 1С, локальный кэш конфигурации на компьютере разработчика может повредиться или рассинхронизироваться с базой данных. Папки кэша, расположенные по путям %localappdata%\1C\1Cv8 и %appdata%\1C\1Cv8, сохраняют устаревшие структуры метаданных.

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

3. Ограничения прав доступа на уровне записей (RLS)

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

Если для текущего пользователя настроен механизм RLS (Row Level Security) для регистра сведений ЗаявленияПоступающих или справочника ФизическиеЛица, платформа неявно модифицирует результирующий SQL-запрос, добавляя скрытые условия ограничения доступа. В итоге СУБД отсекает записи, к которым у пользователя нет прав, и запрос в коде возвращает пустое значение, в то время как администратор в консоли видит все строки.

4. Нюансы передачи параметров составного типа

Если измерение регистра сведений имеет составной тип данных (например, это может быть ссылка на справочник ФизическиеЛица или справочник Контрагенты), то передача параметра требует особой точности:

---

Пошаговый алгоритм отладки проблемы

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

  1. Проверьте статус транзакции. Убедитесь, что запрос не выполняется после того, как в коде была вызвана отмена транзакции или произошло неотыгранное исключение СУБД. Попробуйте выполнить этот же запрос в самом начале процедуры, до выполнения основных проверок.
  2. Принудительно очистите кэш. Закройте конфигуратор и предприятие, удалите папки кэша 1С из директорий Local и Roaming или запустите базу с параметром командной строки /ClearCache.
  3. Проверьте привилегированный режим. Оберните вызов запроса в коде в привилегированный модуль или временно включите привилегированный режим с помощью конструкции УстановитьПривилегированныйРежим(Истина). Если запрос вернет данные — проблема однозначно кроется в настройках RLS.
  4. Сверьте Уникальный Идентификатор (UUID). В отладчике получите UUID передаваемой ссылки: Объект.ФизическоеЛицо.УникальныйИдентификатор() и сравните его строковое представление с UUID элемента, который вы выбираете в консоли запросов. Они должны совпадать символ в символ.
  5. Используйте замер производительности и технологический журнал. Настройте технологический журнал (ТЖ) на сбор событий DBMSSQL или DBPOSTGRS. Это позволит увидеть реальный текст SQL-запроса, который платформа отправляет на СУБД, и проанализировать, не добавляет ли система неявные отборы вроде WHERE 1=0.

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

← На главную