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