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

Программист 1С v8.3 (Обычные формы) Управленческий учет Торговля и дистрибуция
← На главную

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

Анализ исходного запроса

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


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

Запрос.УстановитьПараметр("Ссылка", ВыборМЗ);
Запрос.УстановитьПараметр("Реализация", ЭтаФорма.ВладелецФормы.ТекущаяСтрока);
ТаблицаСтатуса = Запрос.Выполнить().Выгрузить();

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

Проблема 1: Использование ТекущаяСтрока в обычных формах

Разберем наиболее вероятную причину ошибки, связанную с параметром &Реализация. Автор кода использует конструкцию ЭтаФорма.ВладелецФормы.ТекущаяСтрока. В 1С (особенно в обычных формах) поведение этого свойства зависит от того, какой объект является источником данных для табличного поля.

Проанализируем ситуацию: если ВладелецФормы — это форма списка, то ТекущаяСтрока действительно может вернуть ссылку на документ. Но если это табличное поле, связанное с Таблицей Значений, то свойство ТекущаяСтрока возвращает не саму строку (и не ссылку), а индекс строки (число) или внутренний идентификатор. В итоге в запрос передается значение, тип которого не соответствует ожидаемой ссылке на документ РеализацияТоваровУслуг. Запрос не выдаст ошибку типа, но и соответствий в базе данных не найдет, возвращая пустой результат или некорректную выборку.

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


// Вместо этого:
// Запрос.УстановитьПараметр("Реализация", ЭтаФорма.ВладелецФормы.ТекущаяСтрока);

// Используем это:
Если ЭтаФорма.ВладелецФормы.ЭлементыФормы.ИмяТабличногоПоля.ТекущиеДанные <> Неопределено Тогда
    Запрос.УстановитьПараметр("Реализация", ЭтаФорма.ВладелецФормы.ЭлементыФормы.ИмяТабличногоПоля.ТекущиеДанные.Ссылка);
КонецЕсли;

Проблема 2: Виртуальные таблицы и левые соединения

Обратим внимание на работу с виртуальной таблицей СрезПоследних. В исходном запросе она используется без параметров: РегистрСведений.СтатусыДокументов.СрезПоследних(, ). Выясним, почему это плохо.

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

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


// Оптимизированный фрагмент запроса:
|   ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СтатусыДокументов.СрезПоследних(&Период, Документ = &Реализация) КАК СтатусыДокументовСрезПоследних
|   ПО РеализацияТоваровУслуг.Ссылка = СтатусыДокументовСрезПоследних.Документ

Проблема 3: Логическая ловушка в секции ГДЕ

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

Если условие МаршрутноеЗаданиеДокументыРеализации.Реализация = &Реализация не выполняется, вся строка будет исключена из результата. В консоли запросов вы можете вручную выбирать параметры, которые "подходят" под условия, а в коде значение &Реализация может приходить пустым или неверным, из-за чего выборка оказывается пустой или содержит не те данные.

Проблема 4: Права доступа и Row Level Security (RLS)

Выясним причину, по которой консоль может "лгать". Консоль запросов часто запускается под правами Администратора (Полные права). При выполнении кода в режиме пользователя могут вступать в силу ограничения RLS.

Как это проверить:

  1. Запустите консоль запросов под тем же пользователем, у которого возникает проблема.
  2. Проверьте, видны ли документы "Реализация" и записи регистра "Статусы документов" этому пользователю в обычных списках.
  3. Если в консоли под пользователем данных нет, значит, проблема в настройках прав доступа (профили групп доступа).

Проблема 5: Кэширование и обновление интерфейса

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

После выгрузки результата запроса в ТаблицаСтатуса, убедитесь, что вы правильно связали данные. Если это ТаблицаЗначений на форме, используйте метод СоздатьКолонки() (если структура меняется) и проверьте, не перекрываются ли данные в памяти.

Итоговые рекомендации по исправлению

Чтобы ваш запрос всегда отрабатывал корректно и данные в обработке соответствовали данным в консоли, выполните следующие шаги:

Шаг 1. Проверка параметров. Используйте отладчик, чтобы убедиться, что &Реализация — это ДокументСсылка.РеализацияТоваровУслуг, а не число или строка. Шаг 2. Перенос фильтров. Условия по ссылке и реализации лучше перенести из секции ГДЕ в параметры соответствующих виртуальных таблиц или в условия связей ПО. Шаг 3. Очистка кэша. Если ошибка "плавающая", попробуйте очистить локальный кэш пользователя 1С. Шаг 4. Использование ТекущиеДанные. Всегда обращайтесь к данным строки через ТекущиеДанные, чтобы избежать получения индекса строки вместо объекта.

Соблюдение этих правил гарантирует идентичность работы запроса в любых инструментах разработки и в реальной эксплуатации системы.

← На главную