Как в 1С СКД получить данные, оформленные после даты определенного документа или события?

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

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

В этой статье мы подробно и по шагам разберем четыре эффективных способа реализации этой задачи. Мы начнем с классического метода запросов и закончим современными возможностями платформы «1С:Предприятие 8», такими как связи наборов данных и аналитические (оконные) функции.

Способ 1. Использование временной таблицы (ВТ) и левого соединения в одном запросе

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

Проанализируем этот запрос по шагам. Сначала создадим пакетный запрос:


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

ИНДЕКСИРОВАТЬ ПО
    Контрагент,
    ДатаПервойПокупки
;

////////////////////////////////////////////////////////////////////////////////
// Шаг 2. Выбираем последующие продажи, которые произошли позже полученной даты
ВЫБРАТЬ
    Продажи.Контрагент КАК Контрагент,
    Продажи.Период КАК Период,
    Продажи.Номенклатура КАК Номенклатура,
    Продажи.СуммаОборот КАК Сумма
ИЗ
    РегистрНакопления.Продажи КАК Продажи
    // Используем внутреннее соединение, чтобы исключить клиентов без покупок
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ПерваяПокупка КАК ВТ_ПерваяПокупка
        ПО Продажи.Контрагент = ВТ_ПерваяПокупка.Контрагент
        И Продажи.Период > ВТ_ПерваяПокупка.ДатаПервойПокупки

Рассмотрим важные нюансы оптимизации этого решения:

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

Способ 2. Связи наборов данных в СКД (разделение логики на два запроса)

Система компоновки данных предоставляет мощный встроенный механизм — связи наборов данных. Вместо создания одного сложного SQL-запроса мы можем разделить логику на два простых, независимых набора данных (запроса), а СКД сама выполнит их соединение и передаст дату как параметр.

Разберем, как это настроить:

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

Почему важен флаг «Список параметров»? Если этот флаг не установлен, платформа будет выполнять запрос-приемник отдельно для каждого контрагента (в цикле). Это критически снизит производительность отчета. При установленном флаге СКД передаст список контрагентов и дат единым пакетом, что отработает мгновенно.

Способ 3. Использование оконных функций (для платформы 1С 8.3.20 и выше)

Если разработка ведется на современных версиях платформы «1С:Предприятие 8» (начиная с релиза 8.3.20), то в языке запросов стал доступен стандарт SQL-оконных функций. Это позволяет нам полностью отказаться от классического соединения таблиц и группировок, вычислив минимальную дату «на лету» в каждой строке.

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


ВЫБРАТЬ
    Продажи.Контрагент КАК Контрагент,
    Продажи.Период КАК Период,
    Продажи.СуммаОборот КАК Сумма,
    // Вычисляем минимальную дату в разрезе контрагента прямо в строке выборки
    МИНИМУМ(Продажи.Период) OVER (PARTITION BY Продажи.Контрагент) КАК ДатаПервойПокупки
ПОМЕСТИТЬ ВТ_ПродажиСДатойПервойПокупки
ИЗ
    РегистрНакопления.Продажи КАК Продажи
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.Контрагент,
    ВТ.Период,
    ВТ.Сумма
ИЗ
    ВТ_ПродажиСДатойПервойПокупки КАК ВТ
ГДЕ
    ВТ.Период > ВТ.ДатаПервойПокупки

В данном случае конструкция OVER (PARTITION BY ...) указывает СУБД, что нужно рассчитать минимальное значение поля Период для каждого конкретного контрагента, не сворачивая при этом строки в группировку. Во втором запросе пакета нам остается лишь отфильтровать строки, где текущий период больше полученного минимального.

Способ 4. Расчет накопительного итога в СКД через функцию ВычислитьВыражение

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

Выясним, как это настроить в схеме компоновки данных:

  1. В запросе мы выбираем все продажи контрагентов в хронологическом порядке.
  2. Переходим на вкладку «Ресурсы».
  3. Для поля Сумма пишем выражение, которое будет рассчитывать нарастающий итог, начиная со второй записи в группировке (то есть после первой покупки):

ВычислитьВыражение("Сумма(Сумма)", "Период", , "Вторая", "Текущая", "Контрагент")

Параметры функции ВычислитьВыражение в данном случае означают следующее:

Подведем итоги

Каждый из рассмотренных способов имеет свои преимущества в зависимости от условий задачи:

← На главную