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