При разработке на платформе 1С:Предприятие 8 программисты часто сталкиваются с ситуацией, когда запрос, который кажется абсолютно верным, возвращает пустой результат или вызывает недоумение при просмотре данных в отладчике. Ошибка «Ошибка чтения значения» в окне «Табло» или «Вычислить выражение» — один из таких камней преткновения. В этой статье мы подробно разберем, почему возникает это сообщение, как правильно работать с выборкой запроса и какие нюансы использования ЛЕВОЕ СОЕДИНЕНИЕ могут привести к потере данных.
Рассмотрим типичную ситуацию. Программист выполняет запрос, получает объект выборки и пытается сразу же посмотреть значение поля в отладчике. В этот момент платформа выдает сообщение: «Ошибка чтения значения». Проанализируем причину такого поведения системы.
Когда мы вызываем метод Выбрать(), мы получаем объект типа ВыборкаИзРезультатаЗапроса. Важно понимать, что в этот момент «курсор» выборки еще не установлен ни на одну запись. Он находится в позиции «перед первой записью». Попытка обратиться к полям выборки, таким как Выборка.Ссылка, до того, как был вызван метод Следующий(), всегда будет приводить к ошибке, так как данные еще не считаны.
Правильный алгоритм работы с выборкой выглядит так:
Следующий().Следующий() (когда метод вернул Истина) можно обращаться к данным.Рассмотрим пример кода, где эта ошибка видна наиболее наглядно:
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Ссылка ИЗ Справочник.Номенклатура";
Выборка = Запрос.Выполнить().Выбрать();
// В этой точке в отладчике на переменной Выборка.Ссылка
// будет написано "Ошибка чтения значения"
Пока Выборка.Следующий() Цикл
// А вот здесь значение уже будет доступно
ТекСсылка = Выборка.Ссылка;
КонецЦикла;
Часто разработчики сталкиваются с тем, что в консоли запросов или программно запрос не возвращает строк, хотя данные в базе точно есть. Разберем ситуацию на примере автора темы. Был написан запрос, связывающий справочник «Запросы» и документ «Телефонный звонок» по левому соединению.
Проанализируем исходный код запроса:
ВЫБРАТЬ РАЗЛИЧНЫЕ
Запросы.Ссылка КАК Ссылка
ИЗ
Справочник.Запросы КАК Запросы
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ТелефонныйЗвонок КАК ТелефонныйЗвонок
ПО Запросы.Ссылка = ТелефонныйЗвонок.ВзаимодействиеОснование
ГДЕ
ТелефонныйЗвонок.ВзаимодействиеОснование = &Ссылка
В чем здесь кроется логическая ловушка? ЛЕВОЕ СОЕДИНЕНИЕ по своей сути должно возвращать все записи из левой таблицы (Справочник.Запросы), даже если для них не найдено соответствий в правой таблице (Документ.ТелефонныйЗвонок). Однако, как только мы накладываем условие в секции ГДЕ на поле из правой таблицы, наше левое соединение фактически превращается во внутреннее (INNER JOIN).
Это происходит потому, что для тех строк левой таблицы, для которых нет пары справа, все поля правой таблицы (включая ВзаимодействиеОснование) принимают значение NULL. Условие ТелефонныйЗвонок.ВзаимодействиеОснование = &Ссылка отсекает все строки с NULL, так как NULL не равен никакому конкретному значению ссылки. В итоге, если соответствие не найдено, запрос возвращает пустоту.
Если вам необходимо отфильтровать данные правой таблицы, но при этом сохранить все записи левой, условие нужно переносить из секции ГДЕ непосредственно в секцию связи ПО. Рассмотрим, как изменится логика:
ВЫБРАТЬ РАЗЛИЧНЫЕ
Запросы.Ссылка КАК Ссылка
ИЗ
Справочник.Запросы КАК Запросы
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ТелефонныйЗвонок КАК ТелефонныйЗвонок
ПО Запросы.Ссылка = ТелефонныйЗвонок.ВзаимодействиеОснование
И ТелефонныйЗвонок.Ссылка = &СсылкаДокумента // Условие внутри ПО
В этом случае платформа сначала отберет нужный «Телефонный звонок», а затем попытается «приклеить» его к справочнику. Если звонок не будет найден, запись справочника все равно останется в результате, просто поля звонка будут пустыми.
Однако в задаче автора требовалось найти именно конкретный «Запрос», на основании которого был сделан звонок. В таком случае наиболее оптимальным и простым решением будет обращение к данным самого документа, без лишних соединений, если нам заранее известна ссылка на звонок:
ВЫБРАТЬ
ТелефонныйЗвонок.ВзаимодействиеОснование КАК СсылкаНаЗапрос
ИЗ
Документ.ТелефонныйЗвонок КАК ТелефонныйЗвонок
ГДЕ
ТелефонныйЗвонок.Ссылка = &СсылкаНаЗвонок
Выясним еще одну важную причину, по которой запрос может работать некорректно. Поле ВзаимодействиеОснование во многих типовых конфигурациях 1С имеет составной тип данных. Оно может ссылаться на заказы, счета, запросы или другие документы.
Когда мы сравниваем поле составного типа с конкретной ссылкой в условии ГДЕ, 1С генерирует достаточно сложный SQL-запрос. Если производительность оставляет желать лучшего или возникают странности с получением полей через точку, рекомендуется использовать функцию ВЫРАЗИТЬ. Это позволяет системе заранее знать, к какой таблице обращаться.
Пример использования ВЫРАЗИТЬ в запросе:
ВЫБРАТЬ
ВЫРАЗИТЬ(ТелефонныйЗвонок.ВзаимодействиеОснование КАК Справочник.Запросы) КАК СсылкаНаЗапрос
ИЗ
Документ.ТелефонныйЗвонок КАК ТелефонныйЗвонок
ГДЕ
ТелефонныйЗвонок.ВзаимодействиеОснование ССЫЛКА Справочник.Запросы
И ТелефонныйЗвонок.Ссылка = &Ссылка
Использование ВЫРАЗИТЬ не только страхует от ошибок типизации, но и значительно ускоряет выполнение запроса на уровне СУБД, так как исключает необходимость сканирования всех таблиц, входящих в составной тип.
Проанализируем ситуацию, когда запрос в консоли под полными правами работает, а в коде под обычным пользователем — нет. В 1С существует механизм RLS (Row Level Security) — ограничение прав на уровне записей.
Особенности RLS:
Если вы уверены, что запрос написан верно, но данных нет — проверьте работу кода под пользователем с полными правами. Если данные появились, значит, необходимо корректировать роли и шаблоны ограничений доступа.
Столкнувшись с «Ошибкой чтения значения» и пустым результатом запроса, рекомендуем придерживаться следующего чек-листа:
Следующий() перед обращением к Выборка.Поле?ЛЕВОЕ СОЕДИНЕНИЕ во внутреннее из-за фильтра в секции ГДЕ?ВЫРАЗИТЬ и проверьте, передаете ли вы в параметр ссылку нужного типа.&Ссылка, уже записан в базу данных. Ссылка на новый, еще не сохраненный объект, существует в памяти, но в базе данных по ней ничего не найдется.Следуя этим правилам, вы сможете эффективно отлаживать запросы и избегать досадных ошибок при работе с данными в 1С.