Разработка на платформе 1С:Предприятие 7.7 требует особого подхода к написанию запросов, так называемых «черных запросов». В отличие от более современных версий платформы, механизмы 7.7 крайне чувствительны к количеству группировок и глубине обращения к реквизитам через «точку». В этой статье мы подробно разберем, почему «простенький» запрос может выполняться часами, как устранить ошибки индексных файлов CDX и какими способами можно ускорить выборку данных в десятки раз.
Рассмотрим ситуацию: программисту необходимо выгрузить данные по реализации за месяц (номенклатура, количество, цена, контрагент). При написании стандартного запроса часто совершается ошибка — избыточное использование группировок. В 1С 7.7 каждая группировка создает дополнительный уровень вложенности итогов, что при большом объеме данных приводит к экспоненциальному росту нагрузки на дисковую подсистему и оперативную память. Если в запросе присутствуют группировки по числовым полям (Цена, Количество, Сумма), система пытается построить дерево уникальных значений для каждого числа, что практически лишает запрос смысла и «вешает» базу.
Основные причины деградации производительности:
Контрагент.ОсновноеКонтактноеЛицо.Телефоны внутри запроса инициируют неявные соединения таблиц. В файловом варианте (DBF) это приводит к постоянному переключению между индексами разных файлов.Первым делом проанализируем структуру запроса. Если наша задача — получить строки документов, нам не нужно группировать данные по цене или количеству. Вместо этого следует использовать функции. Группировку стоит оставить только для тех объектов, которые определяют структуру выборки (например, документ и номенклатура).
Рассмотрим правильный подход к построению запроса. Мы убираем лишние группировки и заменяем их функциями для агрегации данных. Это позволит 1С корректно обработать итоги.
// Оптимизированный вариант запроса
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса =
"//{{ЗАПРОС(СформироватьЗаказы)
|Период с ВыбНачПериода по ВыбКонПериода;
|Обрабатывать НеПомеченныеНаУдаление;
|РасходнаяНакладная = Документ.РасходнаяНакладная.ТекущийДокумент;
|Номенклатура = Документ.РасходнаяНакладная.Номенклатура;
|Количество = Документ.РасходнаяНакладная.Количество;
|Сумма = Документ.РасходнаяНакладная.Сумма;
|Цена = Документ.РасходнаяНакладная.Цена;
|Подразделение = Документ.РасходнаяНакладная.Подразделение;
|Функция ВсегоКол = Сумма(Количество);
|Функция ВсегоСумма = Сумма(Сумма);
|Группировка РасходнаяНакладная;
|Группировка Номенклатура без групп;
|"//}}ЗАПРОС
;
Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
Возврат;
КонецЕсли;
Проанализируем изменения: мы оставили только две группировки. Теперь запрос получит список накладных, а внутри них — список уникальной номенклатуры. Данные о цене и подразделении можно будет получить непосредственно из выборки при обходе результатов запроса. Это значительно снижает нагрузку на временные файлы CDX.
Если цель — просто выгрузить все строки документов за период в файл CSV без сложной фильтрации по остаткам или регистрам, то использование объекта Запрос может быть вообще нецелесообразным. В 1С 7.7 прямое сканирование документов через ВыбратьДокументы() зачастую работает быстрее и стабильнее, так как оно последовательно читает данные из файлов DH* (шапки) и DT* (табличные части), не создавая громоздких временных таблиц в памяти.
Разберем алгоритм по шагам:
Документ.РасходнаяНакладная.ВыбратьДокументы().ВыбратьСтроки().
Док = СоздатьОбъект("Документ.РасходнаяНакладная");
Док.ВыбратьДокументы(ВыбНачПериода, ВыбКонПериода);
Пока Док.ПолучитьДокумент() = 1 Цикл
Если Док.ПометкаУдаления() = 1 Тогда
Продолжить;
КонецЕсли;
// Получаем реквизиты шапки (быстро, так как объект уже в памяти)
ТекКонтрагент = Док.Контрагент;
// Обращение к телефонам лучше делать через справочник напрямую, если нужно ускорить
ТекТелефон = ТекКонтрагент.ОсновноеКонтактноеЛицо.Телефоны;
Док.ВыбратьСтроки();
Пока Док.ПолучитьСтроку() = 1 Цикл
// Обработка данных каждой строки:
// Док.Номенклатура, Док.Количество, Док.Цена и т.д.
// Запись в CSV...
КонецЦикла;
КонецЦикла;
Этот метод исключает возникновение ошибки CDX при выполнении запроса, так как данные считываются порциями. Это «золотой стандарт» для простых выгрузок в 1С 7.7.
Для баз с огромным количеством документов (десятки и сотни тысяч в месяц) даже перебор может занять значительное время. В среде 1С 7.7 для решения таких задач принято использовать внешние компоненты, такие как 1C++ или 1SQLite. Они позволяют выполнять прямые SQL-запросы к базе данных (даже если она в формате DBF, через драйверы или библиотеку SQLite).
Рассмотрим пример логики прямого запроса. Мы объединяем таблицу шапок документов и таблицу строк по идентификатору IDDOC. Это выполняется на уровне движка базы данных и работает в десятки раз быстрее «черного запроса».
// Пример логики прямого запроса (с использованием SQLite)
ТекстЗапроса = "
|SELECT
| Шапка.iddoc [Документ :Документ.РасходнаяНакладная],
| Шапка.spXXX as Контрагент,
| Строки.spYYY as Номенклатура,
| Строки.spZZZ as Сумма
|FROM
| [Документ.РасходнаяНакладная] as Шапка
|LEFT JOIN
| [ДокументСтроки.РасходнаяНакладная] as Строки ON Шапка.iddoc = Строки.iddoc
|WHERE
| Шапка.date BETWEEN :НачДата AND :КонДата
|";
Важно: Прямые запросы требуют понимания внутренней структуры таблиц 1С (имена полей вида sp123), но это единственный способ мгновенного получения данных на больших объемах.
Если вы используете Scripting.FileSystemObject (FSO) для записи данных, обратите внимание на следующие моменты:
WriteLine происходит обращение к диску. Для ускорения процесса можно накапливать строки в большой текстовой переменной и записывать их порциями (например, по 500-1000 строк).Подведем итог: для исправления ситуации с четырехчасовым ожиданием нам нужно либо радикально упростить группировки в запросе, заменив их функциями, либо перейти на перебор объектов. В большинстве случаев для задач выгрузки данных второй вариант оказывается более стабильным и предсказуемым.