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

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

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

Анализ проблемы: почему запрос возвращает пустой результат

Посмотрим на исходный пример, в котором производился поиск документа Договор по строковому реквизиту НомерДоговора:


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

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

Если Запрос.Группировка(1) = 1 Тогда
    Возврат Запрос.ЗначениеУпорядочивания(1);
КонецЕсли;

Выясним причину, по которой такой код не находит существующий документ. В механизме так называемых «черных запросов» 1С 7.7 кроется сразу несколько неочевидных нюансов:

  1. Отсутствие указания периода выборки. Это главная причина сбоя. Если в тексте запроса по документам явно не задана директива Период с ... по ...;, платформа по умолчанию строит выборку не за всю историю существования информационной базы, а только на момент Точки Актуальности (ТА) либо на текущую рабочую дату. Все документы, проведенные или записанные ранее этой даты, просто исключаются из рассмотрения до применения условий.
  2. Статус проведения документов. По умолчанию объект Запрос отбирает только проведенные документы. Если искомый документ не проведен, его невозможно получить без явного указания директивы |ОбрабатыватьДокументы Все; или |Обрабатывать НеПомеченныеНаУдаление;.
  3. Длина строковых реквизитов и пробелы. В 1С 7.7 реквизиты документов строкового типа имеют фиксированную длину и в таблицах базы дополняются концевыми пробелами. Если переменная _НомерДоговора передана из реквизита диалога формы, она также может содержать невидимые хвостовые пробелы. При сравнении строк без принудительной обрезки условие равенства не сработает.
  4. Ошибочное использование оператора «в». Попытка заменить знак = на ключевое слово в в секции Условие() приводит к нежелательному поведению. В 1С 7.7 оператор в предназначен для проверки вхождения в СписокЗначений, группу справочника или поиска подстроки. Для строгого сопоставления значений нужно использовать исключительно оператор =.
  5. Некорректное чтение результатов выборки. Метод Запрос.ЗначениеУпорядочивания() используется преимущественно при сложных сортировках. Для получения ссылки на документ следует обращаться напрямую к переменной запроса — Запрос.ТекущийДокумент.

Способ 1. Исправление текста запроса 1С 7.7

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


// Подготовим искомую строку, убрав лишние пробелы
_ИскомыйНомер = СокрЛП(_НомерДоговора);

Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса = 
"//{{ЗАПРОС(ПоискДоговорПоНомеру)
|Период с '01.01.1980' по '31.12.2099';
|Обрабатывать НеПомеченныеНаУдаление;
|НомерДоговора   = Документ.Договор.НомерДоговора;
|ТекущийДокумент = Документ.Договор.ТекущийДокумент;
|Группировка ТекущийДокумент без групп;
|Условие(СокрЛП(НомерДоговора) = _ИскомыйНомер);
|"//}}ЗАПРОС
;

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

Если Запрос.Группировка(1) = 1 Тогда
    // Получаем ссылку на найденный документ напрямую
    НайденныйДок = Запрос.ТекущийДокумент;
    Возврат НайденныйДок;
КонецЕсли;

Обратим внимание: секция Условие() в штатных запросах 1С 7.7 не использует индексы таблиц СУБД. Платформа сначала физически вычитывает все записи за указанный период, а затем во внутреннем цикле фильтрует их. Если документов в базе десятки тысяч, такой запрос может выполняться медленно.

Способ 2. Быстрый поиск через Графу отбора (рекомендуемый штатный метод)

В отличие от справочников, у объекта Документ в платформе 1С 7.7 отсутствует метод НайтиПоРеквизиту(). Штатный поиск по номеру документа (НайтиДокумент) не подходит, когда номер хранится в отдельном реквизите шапки. Чтобы получить максимальное быстродействие без прямого сканирования всех таблиц, используем механизм граф отбора.

  1. Откроем конфигурацию в режиме Конфигуратор.
  2. Перейдем в ветку метаданных Графы отбора и создадим новую графу (например, НомерДоговораОтбор).
  3. В свойствах графы включим реквизит НомерДоговора нужного вида документа. Платформа автоматически построит индекс по этому полю.
  4. Применим изменения в метаданных и напишем программный код поиска:

Док = СоздатьОбъект("Документ");
// Пустые даты позволяют выполнить поиск по индексу за всю историю базы данных
Док.ВыбратьПоЗначению("", "", "НомерДоговораОтбор", СокрЛП(_НомерДоговора));

Если Док.ПолучитьДокумент() = 1 Тогда
    НайденныйДок = Док.ТекущийДокумент;
    Возврат НайденныйДок;
Иначе
    Возврат "";
КонецЕсли;

Метод ВыбратьПоЗначению() работает на несколько порядков быстрее запроса, так как платформа выполняет точечное позиционирование по сформированному индексу журнала документов.

Способ 3. Прямой перебор методом «ВыбратьДокументы»

Если вносить изменения в структуру метаданных конфигурации нельзя, а документов конкретного вида в базе относительно немного, можно отказаться от объекта Запрос в пользу прямого перебора через объект Документ. Это снижает расход памяти и избавляет от накладных расходов на инициализацию механизма запросов.


Док = СоздатьОбъект("Документ.Договор");
_ИскомыйНомер = СокрЛП(_НомерДоговора);

// Выборка за всю историю базы данных
Док.ВыбратьДокументы();
Пока Док.ПолучитьДокумент() = 1 Цикл
    // Проверяем только не помеченные на удаление документы
    Если Док.ПометкаУдаления() = 1 Тогда
        Продолжить;
    КонецЕсли;
    
    Если СокрЛП(Док.НомерДоговора) = _ИскомыйНомер Тогда
        // Документ найден, прерываем цикл для экономии времени
        Возврат Док.ТекущийДокумент;
    КонецЕсли;
КонецЦикла;

Возврат "";

Проанализировав все варианты, можно сделать вывод: для разовых поисков в небольших базах достаточно корректно указать Период в тексте запроса или использовать ВыбратьДокументы(). Для высоконагруженных систем с большим объемом документооборота оптимальным штатным решением всегда является добавление реквизита в Графу отбора.

← На главную