Как правильно объединять данные в запросах 1С и группировать результаты

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

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

Почему в результате запроса нет данных

Рассмотрим ситуацию, когда при выполнении запроса в консоли или программно результат оказывается пустым, хотя данные в базе точно есть. Одной из частых причин является использование конструкции ПОМЕСТИТЬ в последнем пакете запроса.

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

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


// Ошибка: данные уходят во временную таблицу, результат пуст
ВЫБРАТЬ
    ВтПриходы.Номенклатура,
    ВтПриходы.Сумма
ПОМЕСТИТЬ ВТПриходыГотово
ИЗ
    ВтПриходы;

// Правильно: данные возвращаются пользователю
ВЫБРАТЬ
    ВтПриходы.Номенклатура,
    ВтПриходы.Сумма
ИЗ
    ВтПриходы;

Дублирование строк при использовании объединения

Вторая распространенная проблема — появление нескольких строк по одной и той же номенклатуре после использования оператора ОБЪЕДИНИТЬ ВСЕ. Это происходит потому, что объединение в 1С просто «склеивает» результаты двух и более запросов один под другим.

Даже если внутри каждого отдельного запроса вы используете СГРУППИРОВАТЬ ПО, это сгруппирует данные только локально в рамках этой части объединения. После того как результаты соединятся, строки с одинаковой номенклатурой, пришедшие из разных частей объединения, останутся отдельными записями.

Выясним, как правильно «свернуть» такие данные. Для этого результат объединения нужно поместить во вложенный запрос или во временную таблицу, и уже по ней выполнить итоговую группировку. Рассмотрим правильную структуру:


ВЫБРАТЬ
    Т.Номенклатура КАК Номенклатура,
    СУММА(Т.СуммаПриходБюджет) КАК СуммаПриходБюджет,
    СУММА(Т.СуммаПриходНеБюджет) КАК СуммаПриходНеБюджет
ИЗ
    (ВЫБРАТЬ
        ВтПриходы.Номенклатура КАК Номенклатура,
        ВтПриходы.Сумма КАК СуммаПриходБюджет,
        0 КАК СуммаПриходНеБюджет
    ИЗ
        ВтПриходы
    ГДЕ Условие1
    
    ОБЪЕДИНИТЬ ВСЕ
    
    ВЫБРАТЬ
        ВтПриходы.Номенклатура,
        0,
        ВтПриходы.Сумма
    ИЗ
        ВтПриходы
    ГДЕ Условие2) КАК Т
СГРУППИРОВАТЬ ПО
    Т.Номенклатура

Важный нюанс: всегда используйте ОБЪЕДИНИТЬ ВСЕ вместо ОБЪЕДИНИТЬ, если планируете дальнейшую группировку. Обычное ОБЪЕДИНИТЬ заставляет систему искать и удалять дубликаты строк между частями запроса, что сильно замедляет работу БД, особенно на больших объемах данных.

Ловушка левого соединения: почему оно превращается во внутреннее

Разберем типичную ошибку при использовании ЛЕВОЕ СОЕДИНЕНИЕ. Если в секции ГДЕ установить условие на поле из правой (присоединяемой) таблицы, то все строки, где это поле равно NULL (т.е. соответствие не найдено), будут отсечены. В итоге ваше ЛЕВОЕ СОЕДИНЕНИЕ превратится во ВНУТРЕННЕЕ, и часть данных из основной таблицы пропадет.

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


// Ошибка: условие в ГДЕ отсекает строки, для которых нет заказа
ИЗ ВтПриходы КАК ВтПриходы
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказПоставщику КАК Заказ
ПО ВтПриходы.Заказ = Заказ.Ссылка
ГДЕ Заказ.Проведен = ИСТИНА // Строки с NULL (нет заказа) пропадут!

Чтобы избежать этой ошибки, перенесите условия по правой таблице непосредственно в блок связи ПО:


// Правильно: условие внутри ПО сохраняет строки ВтПриходы
ИЗ ВтПриходы КАК ВтПриходы
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказПоставщику КАК Заказ
ПО ВтПриходы.Заказ = Заказ.Ссылка
    И Заказ.Проведен = ИСТИНА

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

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

  1. Индексация временных таблиц: Если временная таблица (например, ВтПриходы) далее участвует в соединениях, обязательно указывайте ИНДЕКСИРОВАТЬ ПО для полей связи. Это критично для скорости работы SQL-сервера.
  2. Минимизация «точек»: Каждое обращение через точку вида Заказ.БюджетнаяОперация.Дата порождает неявное соединение таблиц. В сложных запросах это создает огромные «деревья» связей. Старайтесь выбирать нужные данные во временную таблицу один раз.
  3. Использование регистров накопления: Получение сумм напрямую из табличных частей документов (Документ.ПриходныйОрдерНаТовары.Товары) считается неэффективным. Для получения оборотов лучше использовать виртуальные таблицы регистров накопления (например, РегистрНакопления.Закупки.Обороты). Они хранят предварительно рассчитанные итоги, что в десятки раз ускоряет выполнение.
  4. Корректная работа с датами: При использовании условий МЕЖДУ &НачПериода И &КонПериода помните, что дата в 1С содержит время. Если в параметре &КонПериода передана дата без указания времени (т.е. 00:00:00), документы за последний день периода могут не попасть в выборку. Рекомендуется использовать функцию КОНЕЦПЕРИОДА(&КонПериода, ДЕНЬ).

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

← На главную