Как исправить ошибку «Недостаточно фактических параметров» при обходе выборки запроса и правильно получить данные табличной части в 1С?

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

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

Причина ошибки при вызове метода НайтиСледующий()

Проанализируем фрагмент кода, вызвавший исключение:


ВыборкаПункты = Запрос.Выполнить().Выбрать();
Пока ВыборкаПункты.НайтиСледующий() Цикл
    Сообщить(ВыборкаПункты.Содержание);
КонецЦикла;

При выполнении этого цикла платформа сообщает: Недостаточно фактических параметров. Причина заключается в ошибочном вызове метода НайтиСледующий() вместо стандартного метода Следующий().

Рассмотрим подробнее синтаксис объекта ВыборкаИзРезультатаЗапроса:

  1. Метод Следующий() — предназначен для последовательного обхода записей выборки от первой до последней. У него нет параметров, он возвращает значение Истина, если переход на очередную запись прошел успешно, и Ложь, если выборка закончилась. Именно этот метод применяется в абсолютном большинстве циклов обработки результатов запроса:
    
    Пока ВыборкаПункты.Следующий() Цикл
        Сообщить(ВыборкаПункты.Содержание);
    КонецЦикла;
    
  2. Метод НайтиСледующий() — служит для поиска записей по определенным критериям внутри уже полученной выборки (так называемый пост-отбор). В отличие от простого перехода к следующей записи, этот метод требует обязательных параметров, из-за отсутствия которых и возникает ошибка.

Посмотрим на доступные варианты синтаксиса метода НайтиСледующий():

Вариант 1: поиск по структуре значений.


СтруктураПоиска = Новый Структура("Содержание", "Вводная часть");
Пока ВыборкаПункты.НайтиСледующий(СтруктураПоиска) Цикл
    // Обработка найденной записи
КонецЦикла;

Вариант 2: поиск по конкретному значению и именам колонок.


// НайтиСледующий(<Значение>, <Колонки>)
Пока ВыборкаПункты.НайтиСледующий("Вводная часть", "Содержание") Цикл
    // Обработка найденной записи
КонецЦикла;

Таким образом, если требуется просто перебрать все строки результата запроса, всегда следует вызывать Следующий().

Оптимизация текста запроса к табличной части

Обратим внимание на сам запрос. В исходном варианте разработчик использовал внутреннее соединение таблицы самой с собой:


ВЫБРАТЬ
    Программа.Ссылка КАК ПрограммаСсылка,
    Программы.Содержание
ИЗ
    Справочник.Мероприятия.Программа КАК Программа
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Мероприятия.Программа КАК Программы
        ПО Программа.Ссылка = Программы.Ссылка
        И Программы.Ссылка = &Мероприятие

Такое соединение избыточно и бессмысленно нагружает СУБД. Внутренняя таблица базы данных, хранящая табличную часть, уже содержит поле Ссылка (ссылка на элемент-владелец), НомерСтроки и все реквизиты самой табличной части. Для получения строк конкретного объекта достаточно простого условия в секции ГДЕ.

Кроме того, реляционная СУБД не гарантирует порядок выборки записей, если он не задан явно. Чтобы строки табличной части вернулись в том порядке, в котором они были внесены пользователем, добавим сортировку по полю НомерСтроки.

Посмотрим на корректный и лаконичный вариант запроса:


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|   Программа.Ссылка КАК ПрограммаСсылка,
|   Программа.Содержание КАК Содержание
|ИЗ
|   Справочник.Мероприятия.Программа КАК Программа
|ГДЕ
|   Программа.Ссылка = &Мероприятие
|УПОРЯДОЧИТЬ ПО
|   Программа.НомерСтроки";

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

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    Сообщить(Выборка.Содержание);
КонецЦикла;

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

Альтернативное решение: обход табличной части без запроса

Если задача выполняется в контексте формы объекта (например, справочника Мероприятия), обращение к табличной части через запрос к базе данных имеет существенные подводные камни:

  1. Новые объекты: если пользователь создал новый элемент и еще не нажал кнопку записи, значение Объект.Ссылка представляет собой пустую ссылку. Запрос к базе данных ничего не вернет, даже если в табличной части на экране уже введены строки.
  2. Несохраненные изменения: если пользователь отредактировал существующие строки или добавил новые, запрос к базе прочитает старое состояние данных из СУБД, проигнорировав ввод пользователя на форме.
  3. Лишний контекст: передача всей структуры Объект в неконтекстный серверный метод приводит к избыточной передаче данных между клиентом и сервером. Если нужен только запрос, достаточно передать реквизит Объект.Ссылка.

Если мы находимся в форме и нам нужны текущие данные, находящиеся перед глазами пользователя, выполнять запрос вовсе не требуется. Табличная часть уже находится в памяти клиента или серверного контекста формы в виде коллекции ДанныеФормыКоллекция (реквизит Объект.Программа).

Разберем, как получить значения простым прямым обходом коллекции:


Для Каждого СтрокаПрограммы Из Объект.Программа Цикл
    Сообщить(СтрокаПрограммы.Содержание);
КонецЦикла;

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

← На главную