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

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

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

Анализ проблемы: почему ОБЪЕДИНИТЬ ВСЕ не всегда подходит

Рассмотрим ситуацию: у нас есть справочник УсловияПредоставленияСкидокНаценок. В нем содержатся две табличные части: ВремяДействия (где указаны часы работы скидки) и Получатели (где указаны конкретные магазины или контрагенты). Нам нужно получить таблицу, в которой в одной строке будут указаны и получатель, и время действия.

Если мы используем конструкцию ОБЪЕДИНИТЬ ВСЕ, мы фактически "склеиваем" две разные таблицы по вертикали. Поскольку в первой таблице нет поля Получатель, а во второй — полей ВремяНачала и ВремяОкончания, нам приходится подставлять NULL. В итоге результат выглядит как шахматная доска, где полезные данные чередуются с пропусками. Это затрудняет дальнейшую обработку данных в коде или в компоновке данных.

Решение 1: Использование Левого Соединения (JOIN)

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

Посмотрим на пример запроса, который позволяет соединить данные горизонтально:


ВЫБРАТЬ
    УсловияПолучатели.Получатель КАК Получатель,
    ВременаДействия.ДеньНедели КАК ДеньНедели,
    ВременаДействия.ВремяНачала КАК ВремяНачала,
    ВременаДействия.ВремяОкончания КАК ВремяОкончания
ПОМЕСТИТЬ ВТ_ИтоговыеДанные
ИЗ
    Справочник.УсловияПредоставленияСкидокНаценок.ВремяДействия КАК ВременаДействия
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.УсловияПредоставленияСкидокНаценок.Получатели КАК УсловияПолучатели
        ПО ВременаДействия.Ссылка = УсловияПолучатели.Ссылка
ГДЕ
    ВременаДействия.ДеньНедели = &мДень
    И &мТекВремя МЕЖДУ ВременаДействия.ВремяНачала И ВременаДействия.ВремяОкончания
    И УсловияПолучатели.Получатель = &мПолучатель

В данном случае мы используем общую Ссылка (владельца табличных частей) для того, чтобы система "поняла", какие строки времени соответствуют каким получателям. Важный нюанс: такой подход эффективен, когда данных немного. Однако здесь кроется ловушка "Декартова произведения", которую мы разберем ниже.

Решение 2: Подстановка параметров в список выбора

Если ваша задача — просто проверить, действует ли скидка для конкретного получателя, и при этом получатель передан в запрос как параметр &мПолучатель, вам вообще не нужно обращаться ко второй табличной части в секции ИЗ. Мы можем просто "пробросить" этот параметр в результат запроса.

Проанализируем этот изящный способ:


ВЫБРАТЬ
    &мПолучатель КАК Получатель,
    ВременаДействия.ДеньНедели КАК ДеньНедели,
    ВременаДействия.ВремяНачала КАК ВремяНачала,
    ВременаДействия.ВремяОкончания КАК ВремяОкончания
ИЗ
    Справочник.УсловияПредоставленияСкидокНаценок.ВремяДействия КАК ВременаДействия
ГДЕ
    ВременаДействия.ДеньНедели = &мДень
    И &мТекВремя МЕЖДУ ВременаДействия.ВремяНачала И ВременаДействия.ВремяОкончания
    И ВременаДействия.Ссылка В (
        ВЫБРАТЬ Ссылка 
        ИЗ Справочник.УсловияПредоставленияСкидокНаценок.Получатели 
        ГДЕ Получатель = &мПолучатель
    )

Этот метод работает быстрее, так как мы не строим соединение таблиц "каждый с каждым", а лишь фильтруем основную таблицу по условию наличия записи во второй таблице через подзапрос.

Опасности соединения табличных частей (Декартово произведение)

Разберем ситуацию подробнее. Представим, что у одного условия скидки в таблице ВремяДействия заполнено 5 строк (на каждый рабочий день), а в таблице Получатели указано 10 магазинов.

Если мы выполним прямое СОЕДИНЕНИЕ по полю Ссылка, СУБД создаст комбинацию всех строк. В итоге мы получим не 15 строк (как при объединении), а 50 строк (5 умножить на 10). Это явление называется Декартовым произведением.

Как этого избежать?

  1. Если нужно просто наличие данных — используйте оператор ГДЕ Ссылка В (...).
  2. Если нужны данные из обеих таблиц, но без дублей — сначала сгруппируйте одну из таблиц во временную таблицу (ПОМЕСТИТЬ), и только потом соединяйте.
  3. Используйте ВЫБРАТЬ РАЗЛИЧНЫЕ, если дубликаты строк не несут смысловой нагрузки.

Работа с NULL и функцией ЕСТЬNULL

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

Рассмотрим применение функции ЕСТЬNULL() для очистки данных:


ВЫБРАТЬ
    ЕСТЬNULL(ВременаДействия.ВремяНачала, ДАТАВРЕМЯ(1, 1, 1)) КАК ВремяНачала,
    ЕСТЬNULL(УсловияПолучатели.Получатель, ЗНАЧЕНИЕ(Справочник.Магазины.ПустаяСсылка)) КАК Получатель
ИЗ ...

Выясним причину, почему это важно: функция ЕСТЬNULL позволяет заменить неопределенное состояние на типизированное пустое значение (пустую ссылку, пустую дату или ноль). Это гарантирует стабильную работу кода при обходе выборки.

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

Для крупных баз данных (ERP, УТ 11, Розница) прямые запросы к табличным частям могут быть медленными. Разберем по шагам правильную стратегию построения запроса:

  1. Первый этап: Выбираем только ссылки на объекты, которые удовлетворяют условиям первой табличной части (например, по получателю). Помещаем их во временную таблицу ВТ_ПодходящиеСсылки.
  2. Второй этап: Из основной таблицы (времена действия) выбираем данные только для тех ссылок, которые попали в ВТ_ПодходящиеСсылки.
  3. Итог: Мы работаем с небольшими порциями данных, что значительно ускоряет выполнение запроса и снижает нагрузку на SQL-сервер.

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

← На главную