Как в запросе 1С сопоставить каждого пользователя со всеми показателями из справочника?

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

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

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

Разбор логики решения задачи

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

  1. Подготовка исходных данных: Нам нужно четко определить список "субъектов" (в нашем случае — уникальные пользователи из регистра) и список "разрезов" (все показатели из справочника).
  2. Создание декартова произведения: Мы должны соединить каждую запись первой таблицы с каждой записью второй таблицы. В результате получится полная сетка всех возможных комбинаций.
  3. Соединение с реальными данными: К полученной сетке мы подтягиваем фактические значения из регистра сведений.

Рассмотрим по шагам, как это выглядит в коде запроса.

Шаг 1. Выборка уникальных субъектов

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


ВЫБРАТЬ РАЗЛИЧНЫЕ
    ЗначенияПоказателей.Пользователь КАК Пользователь
ПОМЕСТИТЬ ВТ_Пользователи
ИЗ
    РегистрСведений.КоэффициентыПоказателейАктивности.СрезПоследних(&ДатаСреза, ) КАК ЗначенияПоказателей
;

Важный момент: Если бы нам требовались вообще все пользователи, заведенные в системе, мы бы просто обратились к Справочник.Пользователи. Но условие задачи требует ограничиться теми, кто фигурирует в регистре.

Шаг 2. Перемножение таблиц (Декартово произведение)

Теперь нам нужно создать таблицу ВТ_Умножение. В языке запросов 1С для этого используется соединение без указания условия или выборка из нескольких таблиц через запятую. Проанализируем пример кода:


ВЫБРАТЬ
    ВТ_Пользователи.Пользователь,
    СпрПоказатели.Ссылка КАК Показатель
ПОМЕСТИТЬ ВТ_Умножение
ИЗ
    ВТ_Пользователи КАК ВТ_Пользователи,
    Справочник.ПоказателиАктивности КАК СпрПоказатели
;

В результате выполнения этого пакета в таблице ВТ_Умножение для каждого пользователя из ВТ_Пользователи будет создано столько строк, сколько записей содержится в справочнике ПоказателиАктивности. Это и есть наша искомая "пустая" матрица.

Шаг 3. Финальное соединение с данными регистра

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


ВЫБРАТЬ
    ВТ_Умножение.Пользователь КАК Пользователь,
    ВТ_Умножение.Показатель КАК Показатель,
    ЕСТЬNULL(ЗначенияПоказателей.ЗначениеПоказателя, 0) КАК Значение
ИЗ
    ВТ_Умножение КАК ВТ_Умножение
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КоэффициентыПоказателейАктивности.СрезПоследних(&ДатаСреза, ) КАК ЗначенияПоказателей
        ПО ВТ_Умножение.Пользователь = ЗначенияПоказателей.Пользователь
            И ВТ_Умножение.Показатель = ЗначенияПоказателей.Показатель
УПОРЯДОЧИТЬ ПО
    Пользователь,
    Показатель
;

Обратите внимание на использование функции ЕСТЬNULL. Проанализируем ее роль: если для конкретной пары пользователя и показателя в регистре нет записи, соединение вернет значение NULL. Функция ЕСТЬNULL(..., 0) заменяет пустоту на ноль, что делает результат более наглядным для конечного пользователя или для последующих математических вычислений.

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

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

  1. Индексация временных таблиц: При создании ВТ_Умножение крайне рекомендуется добавить индексацию по полям Пользователь и Показатель. Это значительно ускорит финальное левое соединение.
    
    ИНДЕКСИРОВАТЬ ПО
        Пользователь,
        Показатель
    
  2. Параметры виртуальных таблиц: Всегда передавайте условия (например, отбор по группе пользователей или периоду) непосредственно в параметры СрезПоследних. Это позволит платформе 1С отсечь лишние данные еще на уровне СУБД, не выкачивая весь регистр в оперативную память.
  3. Альтернативный метод "ПО ИСТИНА": Существует сокращенная запись декартова произведения через ЛЕВОЕ СОЕДИНЕНИЕ ... ПО ИСТИНА. Хотя этот метод рабочий, использование временных таблиц (как показано выше) считается более "чистым" и надежным подходом в среде 1С, так как позволяет легче отлаживать запрос в консоли.

Использование в Системе компоновки данных (СКД)

Если ваша задача — построить отчет, то перемножение таблиц внутри запроса может быть не самым оптимальным путем. В 1С:Предприятие 8 реализован мощный механизм дополнения значений в СКД.

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

Таким образом, мы рассмотрели классический алгоритм решения задачи "на каждую запись одного объекта повторить все записи другого". Использование временных таблиц и последовательное соединение — это стандарт де-факто для разработки сложных аналитических запросов в 1С.

← На главную