Pular para o conteúdo

Time intelligence (YoY, PoP, YTD, rolling declarativos)

O Mondrian-4 suporta um elemento de schema <TimeCalc> que declara métricas comuns de time intelligence. O loader do schema desugar cada declaração em um <CalculatedMember> validado em [Measures] — então você declara o que você quer em vez de escrever e manter fórmulas MDX à mão.

Por que time intelligence declarativo?

Sem <TimeCalc>, crescimento year-over-year exige um calculated member como:

<CalculatedMember name="Revenue YoY" dimension="Measures">
<Formula>
([Measures].[Revenue] - (ParallelPeriod([Calendar].[Year], 1, [Calendar].CurrentMember),
[Measures].[Revenue]))
/ (ParallelPeriod([Calendar].[Year], 1, [Calendar].CurrentMember),
[Measures].[Revenue])
</Formula>
<CalculatedMemberProperty name="FORMAT_STRING" value="0.0%"/>
</CalculatedMember>

Com <TimeCalc> a mesma métrica é:

<TimeCalc name="Revenue YoY" type="yoy" measure="Revenue"
timeDimension="Calendar" formatString="0.0%"/>

O loader gera o MDX para você, valida que a medida referenciada e a dimensão de tempo existem, e dispara um erro no momento da carga em vez de produzir um resultado silenciosamente errado.

Pré-requisito: uma dimensão Time tipada

<TimeCalc> exige que o cubo tenha uma dimensão Time tipada — uma <Dimension> com type="TIME" cuja hierarquia tem levels nomeados para year, quarter e month. O level de year deve carregar levelType="TimeYears", e os levels de quarter e month devem carregar levelType="TimeQuarters" e levelType="TimeMonths" respectivamente. Os cálculos within-year (ytd, pop, rolling) exigem no mínimo um level de mês na hierarquia.

Uma dimensão Calendar mínima que satisfaz o requisito:

<Dimension name="Calendar" type="TIME" table="dim_date" key="Date">
<Attributes>
<Attribute name="Year" keyColumn="year_num" levelType="TimeYears"/>
<Attribute name="Quarter" keyColumn="quarter_key" levelType="TimeQuarters"/>
<Attribute name="Month" keyColumn="month_key" levelType="TimeMonths"/>
<Attribute name="Date" keyColumn="date_key" levelType="TimeDays"/>
</Attributes>
<Hierarchies>
<Hierarchy name="Calendar" allMemberName="All Time">
<Level attribute="Year"/>
<Level attribute="Quarter"/>
<Level attribute="Month"/>
<Level attribute="Date"/>
</Hierarchy>
</Hierarchies>
</Dimension>

Posição no schema

Elementos <TimeCalc> são envoltos em um bloco <TimeCalcs> dentro de um <Cube>, no mesmo nível de <CalculatedMembers>:

<Cube name="Monthly Revenue">
<Dimensions>
<Dimension source="Calendar"/>
<!-- other dimensions -->
</Dimensions>
<MeasureGroups>
<MeasureGroup name="Revenue" table="monthly_revenue_fact">
<Measures>
<Measure name="Revenue" column="revenue" aggregator="sum"/>
</Measures>
<DimensionLinks>
<ForeignKeyLink dimension="Calendar" foreignKeyColumn="month_key"/>
</DimensionLinks>
</MeasureGroup>
</MeasureGroups>
<TimeCalcs>
<TimeCalc name="Revenue YoY" type="yoy" measure="Revenue" timeDimension="Calendar" formatString="0.0%"/>
<TimeCalc name="Revenue PoP" type="pop" measure="Revenue" timeDimension="Calendar" formatString="0.0%"/>
<TimeCalc name="Revenue YTD" type="ytd" measure="Revenue" timeDimension="Calendar"/>
<TimeCalc name="Revenue R3" type="rolling" measure="Revenue" timeDimension="Calendar" window="3" function="avg"/>
</TimeCalcs>
</Cube>

Referência de atributos

AtributoChave XML / YAMLObrigatórioDescrição
namenamesimO nome do calculated member gerado. Aparece em [Measures] como qualquer outra medida.
typetypesimO tipo de métrica: yoy, pop, ytd ou rolling. Veja Tipos de métrica abaixo.
measuremeasuresimO nome de um <Measure> existente no cubo. O loader rejeita uma medida desconhecida no momento da carga do schema.
timeDimensiontime_dimensioncondicionalO nome de uma dimensão type="TIME". Pode ser omitido quando o cubo tem exatamente uma dimensão TIME; obrigatório quando tem mais de uma.
windowwindowapenas rollingNúmero inteiro de períodos a incluir na janela rolling.
functionfunctionapenas rollingFunção de agregação sobre a janela: sum (default) ou avg.
formatStringformat_stringnãoFormat string MDX aplicada ao membro gerado, ex.: "0.0%" ou "#,###".

Tipos de métrica

yoy — crescimento year-over-year

Reporta a variação percentual comparada ao mesmo período no ano anterior.

Formato da fórmula:

([Measures].[<measure>] -
(ParallelPeriod([<dim>].[<YearLevel>], 1, [<dim>].CurrentMember),
[Measures].[<measure>]))
/
(ParallelPeriod([<dim>].[<YearLevel>], 1, [<dim>].CurrentMember),
[Measures].[<measure>])

A chamada ParallelPeriod navega para a mesma posição relativa um ano atrás usando o level TimeYears. O resultado é NULL para o primeiro ano completo de dados (sem ano anterior disponível).

pop — crescimento period-over-period

Reporta a variação percentual comparada ao período imediatamente anterior (o período logo antes do atual no mesmo level).

Formato da fórmula:

([Measures].[<measure>] -
(PrevMember([<dim>].CurrentMember), [Measures].[<measure>]))
/
(PrevMember([<dim>].CurrentMember), [Measures].[<measure>])

PrevMember volta uma posição na ordenação natural da dimensão. Resultados são NULL para o primeiríssimo membro de um level (sem predecessor).

ytd — cumulativo year-to-date

Reporta o valor cumulativo da medida do início do ano atual até o período atual.

Formato da fórmula:

Aggregate(Ytd([<dim>].CurrentMember), [Measures].[<measure>])

Ytd() retorna o conjunto de todos os períodos do primeiro período do ano atual até o período atual. Aggregate aplica a agregação nativa da medida (tipicamente sum) sobre esse conjunto.

rolling — janela rolling

Reporta o agregado da medida sobre os últimos window períodos, usando sum ou avg.

Formato da fórmula (avg, window=3):

Avg(LastPeriods(3, [<dim>].CurrentMember), [Measures].[<measure>])

Formato da fórmula (sum, window=N):

Sum(LastPeriods(N, [<dim>].CurrentMember), [Measures].[<measure>])

LastPeriods(N, member) retorna o conjunto dos N períodos terminando no membro atual. Se menos que N períodos estão disponíveis (ex.: cedo no histórico de dados), a janela encolhe para o número de períodos que existem — ela não preenche com zeros.

Comportamento de validação

O loader é fail-closed: a carga do schema é abortada com uma mensagem de erro clara se qualquer das seguintes condições é detectada.

CondiçãoErro
measure nomeia um membro que não existe no cuboTimeCalc "X": measure "Y" not found in cube
Nenhum timeDimension especificado e o cubo tem zero dimensões TIMETimeCalc "X": cube has no TIME dimension
Nenhum timeDimension especificado e o cubo tem mais de uma dimensão TIMETimeCalc "X": cube has multiple TIME dimensions — specify timeDimension
timeDimension nomeia uma dimensão que não existe no cuboTimeCalc "X": timeDimension "Y" not found
type="rolling" e window está faltando ou não é um inteiro positivoTimeCalc "X": rolling type requires a positive integer window
type="yoy" mas não existe nenhum level TimeYears na dimensãoTimeCalc "X": TIME dimension has no TimeYears level

Não há resultado silenciosamente errado — toda configuração ruim é pega antes da primeira consulta rodar.

Exemplo trabalhado: receita mensal do demo Bank

O demo Bank traz um cubo Monthly Revenue sobre uma série de receita mensal. Os dados brutos para dois anos:

YearMonthRevenue
2024Jan100
2024Feb200
2024Mar300
2025Jan150
2025Feb250
2025Mar350

O cubo declara todos os quatro tipos de <TimeCalc> contra a dimensão Calendar (Year > Quarter > Month):

<TimeCalcs>
<TimeCalc name="Revenue YoY" type="yoy" measure="Revenue" timeDimension="Calendar" formatString="0.0%"/>
<TimeCalc name="Revenue PoP" type="pop" measure="Revenue" timeDimension="Calendar" formatString="0.0%"/>
<TimeCalc name="Revenue YTD" type="ytd" measure="Revenue" timeDimension="Calendar"/>
<TimeCalc name="Revenue R3" type="rolling" measure="Revenue" timeDimension="Calendar" window="3" function="avg"/>
</TimeCalcs>

Resultados de ouro

CélulaValorComo
Revenue YoY em [Calendar].[2025].[Q1].[Jan 2025]0.5 (50%)(150 − 100) / 100 = 0.5
Revenue PoP em [Calendar].[2024].[Q1].[Feb 2024]1.0 (100%)(200 − 100) / 100 = 1.0
Revenue YTD em [Calendar].[2024].[Q1].[Mar 2024]600100 + 200 + 300 = 600
Revenue R3 em [Calendar].[2025].[Q1].[Mar 2025]250avg(150, 250, 350) = 250

Consulta MDX de exemplo

SELECT
{ [Measures].[Revenue],
[Measures].[Revenue YoY],
[Measures].[Revenue PoP],
[Measures].[Revenue YTD],
[Measures].[Revenue R3] } ON COLUMNS,
[Calendar].[Month].Members ON ROWS
FROM [Monthly Revenue]

Resultado parcial (2024–2025 Jan até Mar):

MonthRevenueYoYPoPYTDR3
Jan 2024100100100
Feb 2024200100.0%300150
Mar 202430050.0%600200
Jan 202515050.0%−50.0%150216.7
Feb 202525025.0%66.7%400233.3
Mar 202535016.7%40.0%750250

Traços (—) indicam NULL — sem dados de ano anterior ou sem período predecessor disponível.

Relação com <CalculatedMembers>

Declarações <TimeCalc> dessugar no momento da carga em elementos <CalculatedMember> em [Measures]. Os membros gerados são indistinguíveis de calculated members manuais no momento da consulta: aparecem em enumerações de membro XMLA, respondem a FORMAT_STRING e podem ser referenciados por outros calculated members.

Se você precisa de uma fórmula que <TimeCalc> não pode expressar — por exemplo, uma métrica blended customizada ou uma razão multi-medida antes de uma comparação de tempo — use um <CalculatedMember> plano diretamente. As duas abordagens podem coexistir no mesmo cubo.

Veja Avançado — Calculated members para a referência completa de <CalculatedMember>.