Введение
Временной анализ — это сердце любой системы бизнес-аналитики. Руководители хотят знать, как изменились продажи по сравнению с прошлым месяцем, какова динамика роста относительно прошлого года, какие тренды наблюдаются в данных. MDX предоставляет мощный набор функций для навигации по временным иерархиям, и в этом уроке мы изучим базовые инструменты, которые станут фундаментом для более сложного временного анализа.
Временное измерение отличается от других измерений своей естественной упорядоченностью и цикличностью. Месяцы следуют друг за другом в строгом порядке, кварталы повторяются каждый год, и эта структура позволяет использовать специализированные функции навигации. Понимание этих функций критически важно для создания любых отчетов с временной составляющей — от простого сравнения периодов до сложных трендовых анализов.
Теоретическая часть
Структура временной иерархии
В кубе Adventure Works временная иерархия [Date].[Calendar] организована следующим образом:
- All Periods (корень)
- Calendar Year (например, CY 2013)
- Calendar Semester (H1 CY 2013, H2 CY 2013)
- Calendar Quarter (Q1 CY 2013, Q2 CY 2013, Q3 CY 2013, Q4 CY 2013)
- Month (January 2013, February 2013, и т.д.)
- Date (конкретные даты)
Каждый уровень связан отношениями родитель-потомок, что позволяет эффективно навигировать между уровнями детализации.
Функции PrevMember и NextMember
PrevMember возвращает предыдущий элемент на том же уровне иерархии. Это самая простая функция для последовательной навигации. Если мы находимся на March 2013, то PrevMember вернет February 2013. Важная особенность: функция работает на всем уровне, поэтому для January 2013 функция PrevMember вернет December 2012 — она перейдет к предыдущему году.
NextMember работает аналогично, но в противоположном направлении. Для March 2013 она вернет April 2013. Эти функции незаменимы при расчете изменений месяц к месяцу или создании последовательных сравнений.
Когда использовать: Эти функции идеальны для сравнения с непосредственно предшествующим или следующим периодом. Например, расчет прироста продаж по сравнению с прошлым месяцем или прогноз на следующий квартал на основе текущего.
Подводные камни: Будьте осторожны на границах данных. Если вы примените PrevMember к самому первому периоду в кубе, функция вернет NULL, что может привести к ошибкам в вычислениях.
Функции Parent и Ancestor
Parent возвращает непосредственного родителя элемента в иерархии. Для March 2013 родителем будет Q1 CY 2013. Это простой способ подняться на один уровень вверх по иерархии.
Ancestor — более мощная функция, позволяющая подняться на любой указанный уровень. Синтаксис: Ancestor(Member, Level). Например, для March 2013 можно получить год: Ancestor([Date].[Calendar].[Month].[March 2013], [Date].[Calendar].[Calendar Year]).
Когда использовать: Parent полезен для быстрого перехода к агрегированным данным. Ancestor незаменим, когда нужно получить контекст более высокого уровня, например, найти год для любой даты или месяца.
Подводные камни: Parent вернет NULL для элементов верхнего уровня. Ancestor требует точного указания целевого уровня — если уровень не существует в иерархии, запрос вернет ошибку.
Функции FirstChild и LastChild
FirstChild возвращает первого потомка элемента. Для Q1 CY 2013 это будет January 2013. Можно использовать цепочки: [CY 2013].FirstChild.FirstChild вернет первый месяц первого квартала (January 2013).
LastChild возвращает последнего потомка. Для Q1 CY 2013 это March 2013. Аналогично можно строить цепочки для навигации через несколько уровней.
Когда использовать: Эти функции незаменимы для получения граничных периодов — первого и последнего месяца квартала, первого квартала года. Часто используются для анализа динамики внутри периода.
Подводные камни: У элементов нижнего уровня (например, конкретных дат) нет потомков, поэтому FirstChild и LastChild вернут NULL.
Функции Lag и Lead
Lag(n) смещает элемент на n позиций назад на том же уровне. [March 2013].Lag(2) вернет January 2013. Это более гибкая альтернатива многократному применению PrevMember.
Lead(n) смещает элемент на n позиций вперед. [March 2013].Lead(3) вернет June 2013.
Когда использовать: Эти функции идеальны для создания скользящих окон, сравнения с периодом N периодов назад, или построения временных рядов с фиксированным шагом.
Подводные камни: При выходе за границы доступных данных функции возвращают NULL. Lag(12) для месяца вернет месяц год назад, но убедитесь, что данные за этот период существуют.
Создание временных диапазонов
Оператор : (двоеточие) создает непрерывный диапазон между двумя элементами. [January 2013]:[March 2013] создаст набор из трех месяцев. Это основа для расчета сумм за период, средних значений и других агрегаций.
Когда использовать: Диапазоны необходимы для анализа периодов произвольной длины, создания накопительных итогов, расчета средних за период.
Подводные камни: Оба элемента должны быть на одном уровне иерархии. Нельзя создать диапазон от месяца до квартала.
Практическая часть
Пример 1: Навигация по временной иерархии
WITH
-- Определяем меру для текущего месяца (March 2013)
MEMBER [Measures].[Current Month] AS
([Measures].[Internet Sales Amount], [Date].[Calendar].[Month].[March 2013]),
FORMAT_STRING = "Currency"
-- Предыдущий месяц через PrevMember
MEMBER [Measures].[Previous Month] AS
([Measures].[Internet Sales Amount],
[Date].[Calendar].[Month].[March 2013].PrevMember),
FORMAT_STRING = "Currency"
-- Квартал текущего месяца через Parent
MEMBER [Measures].[Quarter Total] AS
([Measures].[Internet Sales Amount],
[Date].[Calendar].[Month].[March 2013].Parent),
FORMAT_STRING = "Currency"
SELECT
{[Measures].[Current Month],
[Measures].[Previous Month],
[Measures].[Quarter Total]} ON COLUMNS
FROM [Adventure Works]
Этот пример демонстрирует базовую навигацию: PrevMember для получения предыдущего периода и Parent для подъема на уровень выше. Обратите внимание на использование кортежей (круглые скобки) для связывания меры с конкретным периодом времени.
Пример 2: Использование Lag для сравнения периодов
WITH
-- Текущий месяц
MEMBER [Measures].[Current] AS
([Measures].[Internet Sales Amount], [Date].[Calendar].[Month].[June 2013]),
FORMAT_STRING = "Currency"
-- Три месяца назад
MEMBER [Measures].[3 Months Ago] AS
([Measures].[Internet Sales Amount],
[Date].[Calendar].[Month].[June 2013].Lag(3)),
FORMAT_STRING = "Currency"
-- Изменение за 3 месяца
MEMBER [Measures].[Change] AS
[Measures].[Current] - [Measures].[3 Months Ago],
FORMAT_STRING = "Currency"
SELECT
{[Measures].[Current],
[Measures].[3 Months Ago],
[Measures].[Change]} ON COLUMNS
FROM [Adventure Works]
Lag(3) эффективнее, чем троекратное применение PrevMember. Этот паттерн часто используется для квартальных сравнений или анализа сезонности.
Пример 3: Работа с диапазонами и граничными периодами
WITH
-- Диапазон первого квартала 2013
SET [Q1_Months] AS
[Date].[Calendar].[Month].[January 2013]:
[Date].[Calendar].[Month].[March 2013]
-- Сумма за весь диапазон
MEMBER [Measures].[Q1 Total] AS
SUM([Q1_Months], [Measures].[Internet Sales Amount]),
FORMAT_STRING = "Currency"
-- Первый месяц квартала через FirstChild
MEMBER [Measures].[Q1 Start] AS
([Measures].[Internet Sales Amount],
[Date].[Calendar].[Calendar Quarter].[Q1 CY 2013].FirstChild),
FORMAT_STRING = "Currency"
SELECT
{[Measures].[Q1 Total],
[Measures].[Q1 Start]} ON COLUMNS
FROM [Adventure Works]
Диапазон создается оператором : и включает все элементы между границами. FirstChild дает быстрый доступ к началу периода без явного указания месяца.
Заключение
В этом уроке мы изучили фундаментальные функции для работы со временем в MDX. PrevMember и NextMember обеспечивают последовательную навигацию, Parent и Ancestor позволяют подниматься по иерархии, FirstChild и LastChild дают доступ к граничным периодам, а Lag и Lead обеспечивают гибкое смещение на любое количество позиций.
Эти функции — строительные блоки для любого временного анализа. В следующих уроках мы изучим более специализированные инструменты, такие как ParallelPeriod для сравнения год к году и PeriodsToDate для накопительных итогов. Но уже сейчас, используя только базовые функции, вы можете создавать полноценные временные отчеты, сравнивать периоды и анализировать динамику показателей.
Практикуйтесь с этими функциями, экспериментируйте с разными уровнями иерархии и помните о граничных случаях. Твердое понимание базовых временных функций — это фундамент для освоения продвинутых техник временного анализа в MDX.