Почему запрос в 1С 7.7 возвращает пустой результат при условии с фильтром по СпискуЗначений?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

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

Анализ проблемы: конфликт имен и область видимости переменных

Основная и наиболее неочевидная причина, из-за которой конструкция |Условие(Товар В СписокТоваров); или вызов метода |Условие(СписокТоваров.Принадлежит(Товар) = 1); перестает отбирать данные, заключается в конфликте контекстов переменных.

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

Проверим и исправим эту ситуацию следующими шагами:

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

Разница между оператором «В» и методом «Принадлежит()»

В текстах запросов 1С 7.7 часто применяют два варианта проверки на вхождение элемента в список:

  1. Оператор В: синтаксическая конструкция |Условие(Товар В СписокЗначений); обрабатывается внутренним оптимизатором запросов платформы. Это наиболее быстрый и стандартный способ отбора.
  2. Метод Принадлежит(): конструкция |Условие(СписокЗначений.Принадлежит(Товар) = 1); выполняет вызов функции встроенного языка на каждой итерации перебора записей. В файловых базах (DBF) это приводит к значительному замедлению и может приводить к ошибкам типизации, если реквизит в документе содержит неожиданное значение.

Рекомендуется использовать именно встроенный оператор В.

Правила использования итогов, функций и выгрузки в ТаблицуЗначений

Еще одна типичная ошибка при получении пустых данных связана с взаимодействием директивы Без итогов;, агрегатных функций и метода Выгрузить():

  1. Если в запросе нет директивы Без итогов;, в запросе обязательно должна присутствовать хотя бы одна агрегатная функция (например, Функция Счётчик = Счётчик(); или Сумма(...)). Без агрегатов и без директивы запрос не сможет корректно рассчитать итоги по группировкам.
  2. Если требуется получить линейный список строк (плоскую таблицу без многоуровневых итогов), обязательно указывается директива Без итогов;, а при выгрузке в таблицу значений передаются флаги отключения итогов: Запрос.Выгрузить(ТЗ, 0, 0);.

Рабочий пример построения запроса

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


Перем ТЗ;
Перем Запрос, ТекстЗапроса;
Перем СписокОтобранныхТоваров;

СписокОтобранныхТоваров = СоздатьОбъект("СписокЗначений");  
ДокИнвентаризация.ВыгрузитьТабличнуюЧасть(СписокОтобранныхТоваров, "Товар"); 

Если СписокОтобранныхТоваров.РазмерСписка() = 0 Тогда
    Предупреждение("Табличная часть документа инвентаризации пуста!");
    Возврат;
КонецЕсли;

Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса = 
"//{{ЗАПРОС(ВыборкаПоступлений)
|Период с ДатаНач по (ТекущаяДата());
|ОбрабатыватьДокументы все;
|Без итогов;
|ДокументПоступления = Документ.РС_ПоступлениеТоваров.ТекущийДокумент;
|Товар = Документ.РС_ПоступлениеТоваров.Товар;
|ЭлемШтрихКод = Документ.РС_ПоступлениеТоваров.ЭлемШтрихКод;
|ЭлемПартия = Документ.РС_ПоступлениеТоваров.ЭлемПартия;
|ПриходныйДокумент = Документ.РС_ПоступлениеТоваров.ЭлемПартия.ПриходныйДокумент;
|Группировка ДокументПоступления; 
|Группировка Товар без групп; 
|Группировка ЭлемШтрихКод; 
|Группировка ЭлемПартия; 
|Группировка ПриходныйДокумент; 
|Условие(Товар В СписокОтобранныхТоваров);
|"//}}ЗАПРОС
;

Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
    Сообщить("Ошибка выполнения запроса!");
    Возврат; 
КонецЕсли;  

ТЗ = СоздатьОбъект("ТаблицаЗначений");
Запрос.Выгрузить(ТЗ, 0, 0);

Сообщить("Найдено строк: " + ТЗ.КоличествоСтрок());
ТЗ.ВыбратьСтроку();

Особенности производительности и альтернативные решения

Проанализируем технические нюансы при работе с метаданными документов в 1С 7.7:

  1. Сканирование таблиц: Штатный запрос по документам вида Документ.ВидДокумента... не использует индексы табличной части и выполняет полный последовательный перебор всех документов выбранного вида за указанный интервал дат.
  2. Разыменование через точку: Конструкции вида Документ.РС_ПоступлениеТоваров.ЭлемПартия.ПриходныйДокумент заставляют платформу считывать связанный объект справочника партий для каждой строки документа, что на файловой базе (DBF) существенно увеличивает дисковые операции.
  3. Альтернатива через объектную выборку: Если документов немного, обычный цикл через ВыбратьДокументы() и ВыбратьСтроки() с проверкой СписокОтобранныхТоваров.Принадлежит(Товар) = 1 отрабатывает с сопоставимой скоростью и дает более прозрачный контроль над ошибками.
  4. Выборка через регистры: Если документ формирует движения по регистрам (например, учет партий или остатков товаров), выборка через виртуальные таблицы регистров с фильтром по номенклатуре работает в десятки раз быстрее благодаря использованию штатных индексов регистров.
← На главную