MDX-скрипты особенно сильны в работе с иерархиями, позволяя применять логику на разных уровнях детализации и агрегации. Это включает в себя как естественные иерархии (например, Год -> Квартал -> Месяц), так и иерархии родитель-потомок (например, структура организации).
Применение логики к определенным уровням или членам иерархииВы можете использовать функции навигации по иерархии (.Children, .Descendants, .Parent, .Ancestor, .Level) внутри SCOPE для точного определения области.
Пример 3: Расчет взвешенной средней прибыли для подкатегорий продуктов AdventureWorks
Предположим, мы хотим рассчитать взвешенную среднюю прибыль для каждой подкатегории, где вес — это количество заказов. Но мы хотим применить это только к подкатегориям, а не к отдельным продуктам или категориям верхнего уровня.
CALCULATE;
-- Определяем расчетную меру для взвешенной средней прибыли
CREATE MEMBER CURRENTCUBE.[Measures].[Weighted Average Profit] AS
IIF(
[Measures].[Order Quantity] = 0,
NULL,
([Measures].[Profit] * [Measures].[Order Quantity]) / [Measures].[Order Quantity]
);
-- Применяем SCOPE только к уровню подкатегорий
SCOPE (
[Product].[Product].[Subcategory].Members -- Область действия: все подкатегории
);
-- Здесь мы можем переопределить стандартную агрегацию для Weighted Average Profit
-- для подкатегорий, если это необходимо, или применить другую логику.
-- Например, если бы Weighted Average Profit был бы только на уровне подкатегорий,
-- а на более высоких уровнях он бы агрегировался по-другому.
-- Для демонстрации, пусть это будет простое присвоение,
-- но в реальной жизни здесь могла бы быть сложная логика.
[Measures].[Weighted Average Profit] =
Sum(
[Product].[Product].CurrentMember.Children, -- Для текущей подкатегории суммируем по ее продуктам
[Measures].[Weighted Average Profit] -- Используем уже определенную меру
);
END SCOPE;
Разбор:
● Мы сначала определяем базовую логику [Weighted Average Profit] как расчетную меру.
● Затем SCOPE ([Product].[Product].[Subcategory].Members) ограничивает область действия только подкатегориями. Все, что внутри этого SCOPE, будет применяться только к ячейкам на уровне подкатегорий. Здесь мы показываем, как можно было бы переопределить агрегацию для [Weighted Average Profit] на уровне подкатегорий, если бы стандартное суммирование не подходило.
Работа с иерархиями родитель-потомокИерархии родитель-потомок (например, [Employee] в AdventureWorks, где сотрудники подчиняются другим сотрудникам) требуют особого внимания. MDX-скрипты могут использовать функции, специфичные для таких иерархий (.Children, .Descendants, .Parent, Ancestor(), Leaves()), для применения логики на разных уровнях организационной структуры.
Пример 4: Распределение расходов по сотрудникам в иерархии родитель-потомок (AdventureWorks)
Предположим, у нас есть общая сумма расходов на отдел, и мы хотим распределить ее по сотрудникам этого отдела.
CALCULATE;
-- Пример общей меры расходов на отдел (для демонстрации)
CREATE MEMBER CURRENTCUBE.[Measures].[Department Overhead] AS 100000;
-- Расчетная мера для распределенных расходов на сотрудника
CREATE MEMBER CURRENTCUBE.[Measures].[Distributed Employee Overhead] AS
IIF(
[Measures].[Sales Amount] = 0, -- Если у сотрудника нет продаж, не распределяем
NULL,
([Measures].[Department Overhead], Ancestor(
[Employee].[Employees].CurrentMember,
[Employee].[Employees].[Department] -- Поднимаемся до уровня отдела
)) * ([Measures].[Sales Amount] / ([Measures].[Sales Amount], Ancestor(
[Employee].[Employees].CurrentMember,
[Employee].[Employees].[Department]
)))
);
-- Применяем SCOPE к листьям иерархии сотрудников (отдельным сотрудникам)
SCOPE (
[Employee].[Employees].Levels("Employee").Members -- Уровень листьев иерархии сотрудников
);
[Measures].[Distributed Employee Overhead] =
([Measures].[Department Overhead], Ancestor(
[Employee].[Employees].CurrentMember,
[Employee].[Employees].[Department]
)) * ([Measures].[Sales Amount] / ([Measures].[Sales Amount], Ancestor(
[Employee].[Employees].CurrentMember,
[Employee].[Employees].[Department]
)));
END SCOPE;
Разбор:
● Мы определяем [Department Overhead] как меру (может быть физической).
● [Distributed Employee Overhead] — это расчетная мера, которая распределяет эти расходы.
● Ancestor([Employee].[Employees].CurrentMember, [Employee].[Employees].[Department]): Эта функция позволяет подняться по иерархии родитель-потомок от текущего сотрудника до его отдела, чтобы получить общие расходы отдела и общие продажи отдела.
● SCOPE применяется к листьям иерархии (Levels("Employee").Members), чтобы гарантировать, что распределение происходит на уровне отдельных сотрудников.
Этот пример демонстрирует, как MDX-скрипты могут работать с иерархиями родитель-потомок для выполнения сложных распределений или агрегаций, которые зависят от положения члена в иерархии.