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