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

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

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

Разбор первой ошибки: Результат запроса vs Выборка

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


РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
    Стр.СуммаУтвержд = РезультатЗапроса.Сумма; // Ошибка здесь
КонецЦикла;

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

Правильный способ обращения выглядит так:

Стр.СуммаУтвержд = ВыборкаДетальныеЗаписи.Сумма;

Загадочная «Ошибка чтения значения» в отладчике

Часто разработчики пугаются сообщения «Ошибка чтения значения» в окне «Вычислить выражение». Выясним причину этого поведения. В 1С Выборка — это динамический объект-указатель. До того как сработал первый Выборка.Следующий(), указатель находится «перед первой строкой». В этот момент полей выборки не существует для системы. Аналогично, когда цикл завершен, указатель уходит «за последнюю строку», и значения снова становятся недоступными.

Если при попытке вычислить выражение ВыборкаДетальныеЗаписи.Сумма внутри цикла вы всё равно видите ошибку, это может означать, что:

  1. Запрос вернул пустой результат, и цикл вообще не выполняется.
  2. Переменная Стр (строка табличной части) не инициализирована или недоступна для записи.
  3. В запросе поле имеет псевдоним, отличный от того, к которому вы обращаетесь.

Проблема типов данных и «Битая ссылка»

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

Рассмотрим пример установки параметра:

Запрос.УстановитьПараметр("СтатьяЗатрат", Стр.СтатьяЗатрат);

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

Оптимизация: Почему нельзя писать запрос в цикле

Проанализируем архитектурную проблему в исходном примере. Автор выполняет запрос внутри цикла по строкам табличной части документа. Это критическая ошибка производительности, известная как «Запрос в цикле».

Представим, что в документе 500 строк. Программа 500 раз прервет выполнение кода, отправит сетевой пакет на сервер SQL, дождется ответа и обработает его. Это создает огромную задержку. Разберем, как сделать правильно по шагам:

  1. Подготовка параметров: Сначала соберем все уникальные статьи затрат и активности из табличной части в массив.
  2. Один запрос: Выполним один запрос ко всей таблице регистра, передав массив в условие ГДЕ СтатьяЗатрат В (&МассивСтатей).
  3. Выгрузка в соответствие: Полученный результат запроса выгрузим в ТаблицуЗначений или Соответствие для быстрого поиска в памяти.
  4. Заполнение: В цикле по строкам документа просто найдем нужную сумму в подготовленной таблице значений.

Пример оптимизированного подхода:


// 1. Собираем массив статей
МассивСтатей = Объект.Товары.ВыгрузитьКолонку("СтатьяЗатрат");

// 2. Один запрос вместо сотни
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Ост.СтатьяЗатрат КАК Статья,
    |   СУММА(Ост.Сумма) КАК Сумма
    |ИЗ
    |   РегистрНакопления.ОстДетальный.Остатки(, СтатьяЗатрат В (&МассивСтатей)) КАК Ост
    |СГРУППИРОВАТЬ ПО
    |   Ост.СтатьяЗатрат";

Запрос.УстановитьПараметр("МассивСтатей", МассивСтатей);
ТаблицаЦен = Запрос.Выполнить().Выгрузить();

// 3. Заполняем документ из таблицы значений в памяти
Для Каждого Стр Из Объект.Товары Цикл
    СтрокаПоиска = ТаблицаЦен.Найти(Стр.СтатьяЗатрат, "Статья");
    Если СтрокаПоиска <> Неопределено Тогда
        Стр.СуммаУтвержд = СтрокаПоиска.Сумма;
    КонецЕсли;
КонецЦикла;

Физические таблицы против Виртуальных

В тексте запроса автора используется обращение к РегистрНакопления.ОстДетальный. Это обращение к физической таблице движений. Если ваша задача — получить текущий остаток или сумму на дату, использование физической таблицы является ошибкой.

Рассмотрим подробнее: физическая таблица содержит все записи за всё время существования базы (движения). Чтобы посчитать сумму, 1С придется перебирать тысячи записей. Виртуальные таблицы (например, .Остатки) работают с заранее рассчитанными итогами. Это работает в десятки и сотни раз быстрее, особенно на больших базах данных.

Обработка пустых значений (NULL)

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

Проанализируем защищенный способ написания запроса. Всегда используйте функцию ЕСТЬNULL в тексте запроса:

ВЫБРАТЬ ЕСТЬNULL(ОстДетальный.Сумма, 0) КАК Сумма ...

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

Заключение

Мы выяснили, что ошибка автора темы была комплексной: от простого синтаксического промаха (обращение не к тому объекту) до логических несоответствий в параметрах регистра. Чтобы ваши запросы всегда работали корректно, придерживайтесь следующих правил:

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

← На главную