Как правильно и быстро выбрать данные из табличных частей разных видов документов в 1С

Программист 1С v8.3 (Управляемые формы) Управленческий учет Торговля и дистрибуция
← На главную

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

Проблема запросов в цикле и поиск оптимального решения

Рассмотрим стандартную ситуацию: в базе есть несколько десятков видов документов (ПоступлениеТоваров, ОприходованиеТоваров, ПеремещениеТоваров и др.), и у каждого есть табличная часть Товары. Программист часто пытается решить задачу «в лоб»:


МассивНужныхВидовДокументов = Новый Массив;
МассивНужныхВидовДокументов.Добавить("Документ.ПоступлениеТоваров");
МассивНужныхВидовДокументов.Добавить("Документ.ОприходованиеТоваров");

Для Каждого ТекСтрокаВидаДокумента Из МассивНужныхВидовДокументов Цикл
    ТекстЗапроса = "ВЫБРАТЬ Ссылка ИЗ " + ТекСтрокаВидаДокумента + ".Товары ГДЕ ...";
    // Выполнение запроса
КонецЦикла;

Проанализируем этот подход. Каждый вызов метода Запрос.Выполнить() — это отдельное обращение приложения к серверу 1С, а сервера 1С — к СУБД (SQL Server, PostgreSQL и т.д.). Если документов 58, мы получим 58 сетевых задержек и 58 операций открытия/закрытия курсоров в базе данных. Это создает избыточную нагрузку на планировщик запросов СУБД. Правильный путь — объединить все подзапросы в один большой запрос с помощью конструкции ОБЪЕДИНИТЬ ВСЕ (UNION ALL).

Способ 1. Программное формирование текста запроса с ОБЪЕДИНИТЬ ВСЕ

Выясним, как собрать один эффективный запрос, если список документов нам известен заранее. Основная хитрость здесь заключается в правильной работе со строками. Использование оператора «+» для сложения строк в цикле — плохая практика, так как при каждой итерации выделяется новый блок памяти. Вместо этого мы будем использовать Массив и функцию СтрСоединить.

Рассмотрим пример кода:


МассивЗапросов = Новый Массив;
ШаблонЗапроса = 
    "ВЫБРАТЬ
    |    ТчТовары.Ссылка КАК Ссылка,
    |    ТчТовары.Упаковка КАК Упаковка
    |ИЗ
    |    &ИмяТаблицы КАК ТчТовары
    |ГДЕ
    |    ТчТовары.Упаковка <> ЗНАЧЕНИЕ(Справочник.УпаковкиНоменклатуры.ПустаяСсылка)";

СписокДокументов = "ПоступлениеТоваров,ОприходованиеТоваров,ПеремещениеТоваров"; // и так далее
МассивВидов = СтрРазделить(СписокДокументов, ",");

Для Каждого ВидДокумента Из МассивВидов Цикл
    ПолноеИмяТаблицы = "Документ." + ВидДокумента + ".Товары";
    ТекстПодзапроса = СтрЗаменить(ШаблонЗапроса, "&ИмяТаблицы", ПолноеИмяТаблицы);
    МассивЗапросов.Добавить(ТекстПодзапроса);
КонецЦикла;

ИтоговыйТекстЗапроса = СтрСоединить(МассивЗапросов, Символы.ПС + "ОБЪЕДИНИТЬ ВСЕ" + Символы.ПС);

Запрос = Новый Запрос(ИтоговыйТекстЗапроса);
Результат = Запрос.Выполнить();

Этот подход позволяет СУБД построить единый план выполнения. Важный нюанс: использование ОБЪЕДИНИТЬ ВСЕ предпочтительнее простого ОБЪЕДИНИТЬ (UNION), так как исключает избыточную операцию проверки на уникальность строк (DISTINCT), что существенно ускоряет выполнение.

Способ 2. Динамический поиск документов через Метаданные

А что делать, если мы не знаем точный список документов? Например, в конфигурацию могут быть добавлены новые виды документов, и мы хотим, чтобы наш код автоматически подхватывал их ТЧ Товары. Проанализируем ситуацию, используя объект Метаданные.

Разберем алгоритм по шагам:

  1. Перебираем все документы в коллекции Метаданные.Документы.
  2. Для каждого документа проверяем наличие табличной части с именем Товары.
  3. Если ТЧ найдена, добавляем имя таблицы в массив для формирования запроса.

Посмотрим на программную реализацию:


МассивЗапросов = Новый Массив;
Шаблон = "ВЫБРАТЬ Ссылка КАК Ссылка ИЗ Документ.[ИмяДокумента].Товары КАК Тч";

Для Каждого МетаДокумент Из Метаданные.Документы Цикл
    Если МетаДокумент.ТабличныеЧасти.Найти("Товары") <> Неопределено Тогда
        ТекстПодзапроса = СтрЗаменить(Шаблон, "[ИмяДокумента]", МетаДокумент.Имя);
        МассивЗапросов.Добавить(ТекстПодзапроса);
    КонецЕсли;
КонецЦикла;

Если МассивЗапросов.Количество() > 0 Тогда
    Запрос = Новый Запрос(СтрСоединить(МассивЗапросов, Символы.ПС + "ОБЪЕДИНИТЬ ВСЕ" + Символы.ПС));
    // Выполняем...
КонецЕсли;

Такой подход делает ваше решение универсальным и отказоустойчивым к изменениям структуры метаданных.

Оптимизация производительности: Временные таблицы и пакетные запросы

Когда документов много (например, 58, как упоминалось в обсуждении), итоговый текст запроса может стать очень объемным. Если данных в табличных частях миллионы строк, простая выборка может занять значительное время. В таких случаях мы рекомендуем использовать временные таблицы.

Проанализируем, как это работает: результат объединения помещается во временную таблицу ПОМЕСТИТЬ ВтВсеТовары. После этого к временной таблице можно применить дополнительные фильтры, группировки или соединения с другими справочниками. Это позволяет СУБД проиндексировать промежуточный результат и работать с ним быстрее.

Также стоит помнить об ограничениях СУБД. Например, в MS SQL Server существует ограничение на количество объединяемых таблиц в одном запросе (около 256). Для 58 документов это не критично, но если вы работаете с крайне сложной системой, возможно, придется разбивать запросы на несколько пакетов.

Заключение и рекомендации

Подводя итоги, выделим основные правила работы с множественными табличными частями:

  1. Никогда не делайте запросы в цикле. Это главная заповедь производительности 1С.
  2. Используйте конструкцию ОБЪЕДИНИТЬ ВСЕ. Она объединяет данные без затрат на поиск дублей.
  3. Формируйте текст через Массив и СтрСоединить. Это сэкономит время процессора на операциях со строками.
  4. Используйте Метаданные для автоматического поиска нужных объектов, чтобы код не требовал правок при обновлении конфигурации.
  5. Рассматривайте использование фоновых заданий. Если документов действительно много и обработка занимает более 10-20 секунд, лучше вынести этот процесс в фоновое задание, чтобы не блокировать интерфейс пользователя.

Соблюдение этих простых правил позволит вам писать чистый, быстрый и масштабируемый код, который будет эффективно работать даже на крупных корпоративных базах данных.

← На главную