Как сравнить цены двух поставщиков или филиалов из одного регистра в отчете 1С на СКД?

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

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

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

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

Основные подходы к получению двух цен из одного источника в СКД

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

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

Один из наиболее интуитивных и гибких методов — это добавление одного и того же регистра в СКД несколько раз, но каждый раз присваивая ему уникальный псевдоним. Это позволяет нам обращаться к одному и тому же регистру как к разным сущностям и устанавливать для каждого из этих "виртуальных" источников свои параметры отбора.

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

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


ВЫБРАТЬ
    ЦеныФилиала1.Номенклатура КАК Номенклатура,
    ЦеныФилиала1.Цена КАК ЦенаПервогоФилиала,
    ЦеныФилиала2.Цена КАК ЦенаВторогоФилиала
ИЗ
    РегистрСведений.Цены КАК ЦеныФилиала1 // Первый экземпляр регистра с псевдонимом ЦеныФилиала1
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Цены КАК ЦеныФилиала2 // Второй экземпляр регистра с псевдонимом ЦеныФилиала2
        ПО ЦеныФилиала1.Номенклатура = ЦеныФилиала2.Номенклатура
        И ЦеныФилиала1.ТипЦены = ЦеныФилиала2.ТипЦены // Важно учесть все ключевые измерения
        И ЦеныФилиала2.Филиал = &ФилиалВторой // Отбор для второго филиала
ГДЕ
    ЦеныФилиала1.Филиал = &ФилиалПервый // Отбор для первого филиала

В этом запросе мы явно указываем, какую цену мы берем из какого "виртуального" источника, обеспечивая их независимость. СКД предоставляет удобный интерфейс для построения таких связей без необходимости вручную писать сложный SQL-подобный запрос.

2. Использование параметров в запросе СКД с конструкцией ВЫБОР КОГДА

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

  1. Создание параметров СКД: На вкладке "Параметры" в схеме компоновки данных мы создаем два пользовательских параметра, например, &Филиал1 и &Филиал2. Тип данных для них должен соответствовать типу поля Филиал из вашего регистра (например, СправочникСсылка.Филиалы). Эти параметры будут использоваться для выбора конкретных филиалов пользователем.
  2. Формирование запроса с ВЫБОР КОГДА: Переходим на вкладку "Наборы данных" и открываем конструктор запросов для вашего регистра. В основном запросе набора данных СКД мы используем конструкцию ВЫБОР КОГДА для условного выбора цен, проецируя их в разные колонки.

Рассмотрим подробнее пример запроса, который мы можем сформировать в конструкторе запросов:


ВЫБРАТЬ
    РегистрЦен.Номенклатура КАК Номенклатура,
    МАКСИМУМ(ВЫБОР
        КОГДА РегистрЦен.Филиал = &Филиал1
        ТОГДА РегистрЦен.Цена
        ИНАЧЕ NULL
    КОНЕЦ) КАК ЦенаФилиал1,
    МАКСИМУМ(ВЫБОР
        КОГДА РегистрЦен.Филиал = &Филиал2
        ТОГДА РегистрЦен.Цена
        ИНАЧЕ NULL
    КОНЕЦ) КАК ЦенаФилиал2
ИЗ
    РегистрСведений.Цены КАК РегистрЦен // Используем наш регистр
ГДЕ
    РегистрЦен.Филиал В (&Филиал1, &Филиал2) // Ограничиваем выборку только нужными филиалами
СГРУППИРОВАТЬ ПО
    РегистрЦен.Номенклатура // Группируем по номенклатуре для получения одной строки на товар

Давайте проанализируем этот запрос и его ключевые элементы:

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

3. Использование временных таблиц

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

  1. Создание первой временной таблицы: Мы выполняем первый запрос, в котором выбираем все цены для первого филиала (&Филиал1) и помещаем их во временную таблицу. Назовем ее, например, ВТ_ЦеныФилиала1. Этот запрос будет простым и отфильтрованным только по первому филиалу.
  2. Создание второй временной таблицы: Аналогично, мы выполняем второй запрос, выбирая цены для второго филиала (&Филиал2) и помещаем их во временную таблицу ВТ_ЦеныФилиала2.
  3. Соединение временных таблиц: На заключительном этапе мы соединяем эти две временные таблицы по Номенклатуре (и другим ключевым полям, таким как ТипЦены), чтобы получить агрегированный результат.

Пример структуры запроса с использованием временных таблиц:


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

// Шаг 2: Получаем цены для второго филиала и помещаем во временную таблицу
ВЫБРАТЬ
    Цены.Номенклатура КАК Номенклатура,
    Цены.Цена КАК Цена
ПОМЕСТИТЬ ВТ_ЦеныФилиала2
ИЗ
    РегистрСведений.Цены КАК Цены
ГДЕ
    Цены.Филиал = &Филиал2
    И Цены.ТипЦены = &ТипЦены // Если тип цены также является параметром
;

////////////////////////////////////////////////////////////////////////////////
// Шаг 3: Соединяем временные таблицы для получения окончательного результата
ВЫБРАТЬ
    ВТ_ЦеныФилиала1.Номенклатура КАК Номенклатура,
    ВТ_ЦеныФилиала1.Цена КАК ЦенаПервогоФилиала,
    ВТ_ЦеныФилиала2.Цена КАК ЦенаВторогоФилиала
ИЗ
    ВТ_ЦеныФилиала1 КАК ВТ_ЦеныФилиала1
        ПОЛНОЕ СОЕДИНЕНИЕ ВТ_ЦеныФилиала2 КАК ВТ_ЦеныФилиала2
        ПО ВТ_ЦеныФилиала1.Номенклатура = ВТ_ЦеныФилиала2.Номенклатура

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

4. Вычисляемые поля в СКД для разницы и разницы в процентах

Как только мы успешно получили поля ЦенаФилиал1 и ЦенаФилиал2 (любым из вышеперечисленных способов), расчет разницы и разницы в процентах становится стандартной задачей для вычисляемых полей в СКД. СКД предоставляет удобный механизм для создания таких полей.

  1. Поле "Разница":

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

    ЦенаФилиал1 - ЦенаФилиал2

    Это поле покажет, насколько цена первого филиала отличается от цены второго.

  2. Поле "РазницаПроцент":

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

    
    ВЫБОР
        КОГДА ЦенаФилиал2 = 0
            ТОГДА 0 // Если цена второго филиала равна нулю, процентную разницу считать как 0 или
                     // можно использовать NULL, если требуется явное отсутствие значения.
        ИНАЧЕ (ЦенаФилиал1 - ЦенаФилиал2) / ЦенаФилиал2 * 100
    КОНЕЦ
    

    Если требуется показать отсутствие процента при нулевой цене второго филиала, мы можем изменить выражение на:

    
    ВЫБОР
        КОГДА ЦенаФилиал2 = 0
            ТОГДА NULL // Явно указываем отсутствие значения при делении на ноль
        ИНАЧЕ (ЦенаФилиал1 - ЦенаФилиал2) / ЦенаФилиал2 * 100
    КОНЕЦ
    

    Не забудьте настроить формат вывода для поля РазницаПроцент, чтобы оно отображалось в виде процентов (например, с двумя знаками после запятой).

Почему возникала проблема одинаковых цен?

Проблема, когда поля ЦенаПервая и ЦенаВторая показывали одинаковые значения, обычно возникает, если мы неявно или не совсем корректно пытаемся получить два разных значения из одного и того же поля в одном наборе данных без надлежащего разделения контекста. Это происходит, если:

Применяя подходы, описанные выше (множественные источники данных, ВЫБОР КОГДА в запросе или временные таблицы), мы явно разделяем логику получения цен для каждого филиала, тем самым гарантируя, что каждая колонка будет содержать цену именно того филиала, для которого она предназначена. Это позволяет избежать путаницы и получить корректное сравнение.

Заключение

Сравнение цен различных поставщиков или филиалов на одну и ту же номенклатуру — это типовая задача для аналитических отчетов, и она хорошо решается с помощью мощного инструментария СКД. Используя возможности Системы Компоновки Данных и языка запросов 1С, мы можем эффективно получить и сравнить эти данные.

Мы рассмотрели три основных метода: использование нескольких источников данных в СКД с последующим соединением, применение конструкции ВЫБОР КОГДА непосредственно в запросе, а также подход с временными таблицами. Каждый из них имеет свои преимущества и может быть более или менее подходящим в зависимости от сложности вашей задачи и объема данных. Однако, для большинства случаев сравнения двух сущностей из одного регистра, метод с использованием ВЫБОР КОГДА в запросе СКД является наиболее оптимальным по производительности и простоте реализации.

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

← На главную