При переходе с платформы 1С:Предприятие 8 на версию 7.7 разработчики нередко сталкиваются с неожиданным поведением встроенного языка запросов. Типичная ситуация: пишется простой запрос для отбора конкретного документа по значению реквизита, запрос успешно выполняется, однако результат выгрузки в объект ТаблицаЗначений оказывается пустым или содержит только системную колонку НомерСтроки. Разберем по шагам, почему так происходит, какие правила синтаксиса запросов платформы 1С 7.7 были нарушены и как правильно получить найденный документ.
Проанализируем исходную попытку построения выборки:
НомерНаСайте = "12148857";
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса =
"//{{ЗАПРОС(Заказ)
|Период с (НачГода(ТекущаяДата())) по (КонГода(ТекущаяДата()));
|ОбрабатыватьДокументы все;
|Обрабатывать НеПомеченныеНаУдаление;
|ТекущийДокумент = Документ.ЗаказПокупателя.ТекущийДокумент;
|Условие(НомерНаСайте = НомерНаСайте);
|"//}}ЗАПРОС
;
Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
Возврат;
КонецЕсли;
ТТ = СоздатьОбъект("ТаблицаЗначений");
Запрос.Выгрузить(ТТ, 0, 0);
Выясним причину, по которой данный код не возвращает нужных данных. Здесь допущены сразу три ключевые ошибки, характерные для архитектуры 1С 7.7:
Условие(НомерНаСайте = НомерНаСайте) сравнивает внешнюю переменную саму с собой, потому что реквизит документа с таким именем внутри запроса вообще не был объявлен. Внутренняя переменная запроса обязательно должна быть связана с реквизитом через оператор присваивания.Запрос.Выгрузить() формирует колонки в таблице значений исключительно по переменным, для которых задано предложение Группировка, либо по агрегатным функциям (таким как Сумма или Количество). Переменные, описанные просто через присваивание (в нашем случае ТекущийДокумент), в колонки результирующей таблицы не попадают.Рассмотрим, как должен выглядеть рабочий запрос к документам конфигурации. Для того чтобы документ отобразился в результатах, мы должны:
зНомерНаСайте = Документ.ЗаказПокупателя.НомерНаСайте;.СокрЛП() внутри условия или предварительно подготовить строку нужной длины.Группировка по документу.Посмотрим на исправленный вариант программного кода:
НомерНаСайте = "12148857";
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса =
"//{{ЗАПРОС(Заказ)
|Период с (НачГода(ТекущаяДата())) по (КонГода(ТекущаяДата()));
|ОбрабатыватьДокументы все;
|Обрабатывать НеПомеченныеНаУдаление;
|ТекущийДокумент = Документ.ЗаказПокупателя.ТекущийДокумент;
|зНомерНаСайте = Документ.ЗаказПокупателя.НомерНаСайте;
|Группировка ТекущийДокумент;
|Условие(СокрЛП(зНомерНаСайте) = СокрЛП(НомерНаСайте));
|"//}}ЗАПРОС
;
Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
Предупреждение("Ошибка выполнения запроса!");
Возврат;
КонецЕсли;
ТТ = СоздатьОбъект("ТаблицаЗначений");
Запрос.Выгрузить(ТТ, 0, 0);
ТТ.ВыбратьСтроки();
Пока ТТ.ПолучитьСтроку() = 1 Цикл
НайденныйДокумент = ТТ.ТекущийДокумент;
Сообщить("Найден документ: " + НайденныйДокумент);
КонецЦикла;
Обратим внимание: выгружать весь результат в ТаблицаЗначений ради поиска одного объекта не обязательно. Гораздо эффективнее и экономнее по памяти выполнить прямой обход группировок запроса:
Пока Запрос.Группировка("ТекущийДокумент") = 1 Цикл
НайденныйДокумент = Запрос.ТекущийДокумент;
Сообщить("Найден документ: " + НайденныйДокумент);
Прервать; // Завершаем цикл, если нужен только первый найденный документ
КонецЦикла;
В ходе обсуждений нередко звучит совет использовать метод НайтиПоРеквизиту(). Важно помнить: в платформе 1С 7.7 метод НайтиПоРеквизиту() существует только у объекта Справочник. У объекта Документ такого метода нет.
Если документов за период немного, можно обойтись стандартной выборкой по периоду с помощью метода ВыбратьДокументы(). По скорости работы в файловой базе (DBF) последовательный перебор документов одного вида практически не уступает запросу, так как внутренний механизм запроса 1С 7.7 в файловом режиме также последовательно считывает записи журнала.
Разберем пример реализации через объект Документ:
ИскомыйНомер = СокрЛП(НомерНаСайте);
мДок = СоздатьОбъект("Документ.ЗаказПокупателя");
// Открываем выборку за требуемый интервал дат
мДок.ВыбратьДокументы(НачГода(ТекущаяДата()), КонГода(ТекущаяДата()));
Пока мДок.ПолучитьДокумент() = 1 Цикл
Если СокрЛП(мДок.НомерНаСайте) = ИскомыйНомер Тогда
Сообщить("Документ успешно найден: " + мДок.ТекущийДокумент());
Прервать;
КонецЕсли;
КонецЦикла;
Если поиск конкретного документа по стороннему номеру или идентификатору сайта требуется выполнять регулярно и быстро, последовательное сканирование журналов за целый год создаст сильную нагрузку на дисковую подсистему. В таких случаях применяют штатный механизм отбора:
Графа отбора (либо включается признак «Отбор» у общего реквизита).ВыбратьПоЗначению(). Обратите внимание: данный метод работает исключительно с нетипизированным объектом СоздатьОбъект("Документ"). Попытка вызвать его у объекта определенного вида (например, Документ.ЗаказПокупателя) приведет к ошибке выполнения программы.Рассмотрим пример быстрого индексного отбора:
ИскомыйНомер = СокрЛП(НомерНаСайте);
ВыборкаДок = СоздатьОбъект("Документ");
// "НомерНаСайте" здесь - имя Графы отбора в конфигураторе
ВыборкаДок.ВыбратьПоЗначению(НачГода(ТекущаяДата()), КонГода(ТекущаяДата()), "НомерНаСайте", ИскомыйНомер);
Пока ВыборкаДок.ПолучитьДокумент() = 1 Цикл
Если ВыборкаДок.Вид() = "ЗаказПокупателя" Тогда
Сообщить("Найден целевой заказ: " + ВыборкаДок.ТекущийДокумент());
Прервать;
КонецЕсли;
КонецЦикла;
Таким образом, для поиска документа запросом в 1С 7.7 необходимо обязательно связывать реквизиты метаданных с внутренними переменными запроса, контролировать длину строковых реквизитов и всегда объявлять секцию Группировка для получения выходных полей в выборке.