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

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

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

Разбор проблемы: некорректный обход иерархической выборки

Проанализируем ситуацию, когда в тексте запроса используется предложение ИТОГИ. В этом случае результат запроса перестает быть линейным и превращается в древовидную структуру. Если мы используем метод Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам), крайне важно соблюдать вложенность циклов Пока ... Следующий().

Посмотрим на пример кода, который часто приводит к ошибкам:


ВыборкаПоПериоду = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаПоПериоду.Следующий() Цикл        
    // Здесь мы находимся на уровне группировки "Период"
    ВыборкаПоНумератору = ВыборкаПоПериоду.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
    Пока ВыборкаПоНумератору.Следующий() Цикл
        // Здесь мы находимся на уровне группировки "Нумератор"
        Сообщить(ВыборкаПоНумератору.Нумератор);
    КонецЦикла;
КонецЦикла;

Важный момент: Если вы используете ИТОГИ ПО Период, Нумератор, система создает несколько уровней иерархии. Первый уровень — это общий итог по всей таблице, второй — по периоду, третий — по нумератору. Если пропустить один из уровней или вызвать Выбрать() без параметров внутри иерархического обхода, вы рискуете получить либо пустые данные, либо многократное повторение одних и тех же записей. Выясним причину: при обходе по группировкам каждая вышестоящая выборка является фильтром для подчиненной.

Критическая ошибка: запрос внутри цикла

Разберем архитектурную ошибку, представленную во втором сообщении темы. Автор выполняет основной запрос, получает выборку реализаций и внутри цикла этой выборки создает новый объект Запрос, новый МенеджерВременныхТаблиц и выполняет тяжелое обращение к базе данных.

Проанализируем ситуацию: если в первой выборке будет 1000 документов, система выполнит 1000 дополнительных запросов к СУБД. Это создает колоссальную нагрузку на сервер и сеть. Запомните: никогда не выполняйте запросы внутри цикла Пока ... Следующий().

Как сделать правильно:

  1. Сначала соберите все необходимые ссылки или условия (например, массив ссылок на документы или узлы) в массив или таблицу значений.
  2. Передайте этот массив в качестве параметра в один единственный запрос.
  3. Используйте временную таблицу для связи ваших условий с данными базы.
  4. Получите один общий результат и обрабатывайте его в памяти.

Работа с таблицами изменений (.Изменения)

В коде упоминается обращение к таблицам вида Документ.ПриходныйКассовыйОрдер.Изменения. Это служебные таблицы, в которых хранятся данные о зарегистрированных изменениях для планов обмена. Рассмотрим нюансы работы с ними:

При чтении данных из таблиц изменений напрямую через запрос, мы обходим стандартный механизм ПланыОбмена.ВыбратьИзменения(). Это допустимо в специфических задачах, но требует осторожности. Проанализируем условия в запросе:


ГДЕ
    ПриходныйКассовыйОрдер.Узел = &Узел
    И НАЧАЛОПЕРИОДА(ПриходныйКассовыйОрдер.Ссылка.ДатаПробитияЧека, ДЕНЬ) = &ПериодРеализации

Обратите внимание на использование функции НАЧАЛОПЕРИОДА в условии ГДЕ. Это снижает производительность, так как СУБД не может эффективно использовать индексы по полю Дата. Лучше передавать в параметры запроса границы периода (начало и конец дня) и использовать оператор МЕЖДУ.

Оптимизация работы с временными таблицами

В примере автора МенеджерВременныхТаблиц создается в каждой итерации цикла. Это избыточно. Разберем по шагам, как работать с МенеджерВременныхТаблиц и ВыполнитьПакет():

Объект МенеджерВременныхТаблиц нужен только в том случае, если вы планируете использовать одни и те же временные таблицы в разных объектах Запрос в рамках одной процедуры. Если ваш запрос содержит пакет (несколько запросов, разделенных символом ";"), система сама управляет временными таблицами.

Также важно правильно обрабатывать результат метода ВыполнитьПакет(). Этот метод возвращает массив результатов. Рассмотрим пример:


// Запрос содержит: 
// 1. ПОМЕСТИТЬ ВТ_Данные;
// 2. ВЫБРАТЬ * ИЗ ВТ_Данные;
Результаты = Запрос.ВыполнитьПакет();
// Результаты[0] - это результат выполнения команды ПОМЕСТИТЬ (он пустой)
// Результаты[1] - это реальная выборка данных
Выборка = Результаты[1].Выбрать();

Рекомендации по соединению таблиц по датам

В первом сообщении автор пытался соединить таблицы по условию НумераторПКО.Дата = Документы.Период. В 1С тип Дата содержит время до секунды. Вероятность того, что две даты совпадут секунда в секунду, крайне мала, если это не специально подготовленные данные.

Рассмотрим правильный подход: если нужно соединить данные в пределах дня, подготовьте временную таблицу, в которой даты уже приведены к началу дня с помощью НАЧАЛОПЕРИОДА(Дата, ДЕНЬ). Проиндексируйте это поле во временной таблице, и только потом выполняйте ЛЕВОЕ СОЕДИНЕНИЕ. Это обеспечит и точность сопоставления, и высокую скорость работы.

Итоги и выводы

Подведем итог нашему анализу. Чтобы избежать проблем, с которыми столкнулся автор темы, придерживайтесь следующих правил:

  1. Соблюдайте иерархию: При использовании ИТОГИ всегда делайте вложенные циклы обхода для каждой группировки.
  2. Массовая обработка: Вместо запроса в цикле используйте один запрос с передачей массива параметров.
  3. Индексация: Всегда индексируйте поля во временных таблицах, по которым планируете делать СОЕДИНЕНИЕ.
  4. Типизация дат: Помните о составе даты (дата + время) и всегда приводите их к единому знаменателю перед сравнением.

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

← На главную