acumulado (running total) em DAX: o que faz e quando usar (com exemplos)
Guia prático de acumulado (running total) DAX: o padrão CALCULATE com ALL e MAX, TOTALYTD, tabela de datas, ordenação e acumular por data ou categoria.
O acumulado do seu relatório está certo até alguém mudar a ordenação
Você monta a medida, o número bate com o Excel, apresenta para a diretoria e está tudo bem. Aí alguém arrasta o campo de mês para as colunas, filtra por região ou muda o eixo do gráfico, e o total que era para crescer degrau a degrau começa a repetir valores ou a somar tudo de uma vez. Se isso já aconteceu com você, o problema não é a conta em si. É como o acumulado (running total) DAX interage com o contexto de filtro e com a tabela de datas do modelo. Total acumulado parece o cálculo mais simples do mundo, e é justamente por isso que ele quebra em produção com tanta frequência.
Neste artigo eu vou direto ao ponto: o que o acumulado realmente faz por baixo dos panos, o padrão canônico com CALCULATE e FILTER(ALL(...)), quando usar TOTALYTD, por que a tabela de datas e a ordenação não são detalhe, e qual a diferença entre acumular por data e por categoria. São três exemplos de código que você reconhece do dia a dia. Se você ainda está firmando a base, vale ler antes o nosso guia de boas práticas de modelagem e DAX, porque aqui eu assumo que você já sabe o que é uma medida e o que é contexto de filtro.
O acumulado (running total) DAX soma tudo até a data corrente, não só o período
Uma medida normal, como SUM(Vendas[Valor]), respeita o contexto de filtro da célula. Na linha de março, ela soma só março. Um acumulado quer o oposto: na linha de março, ele precisa somar janeiro, fevereiro e março juntos. Ou seja, precisa ignorar o filtro que restringe a linha a um único mês e, ao mesmo tempo, aplicar um novo limite: some tudo o que for menor ou igual à última data visível naquela linha.
É essa dupla operação que confunde. O acumulado não é uma soma diferente, é a mesma soma calculada dentro de um contexto de filtro que a gente reescreve na marra. Errar um desses dois passos é o que faz o número dar 100% em toda linha ou repetir o mesmo total do começo ao fim.
Guarde esta ideia central, porque tudo depois dela é detalhe de sintaxe:
- Remover o filtro de data que a linha impõe, usando
ALLsobre a tabela de datas. - Reaplicar um teto móvel, geralmente com
MAXda data visível na linha atual.
O padrão CALCULATE com FILTER(ALL(Datas)) é a base de todo acumulado
O padrão canônico de acumulado em DAX combina três funções: CALCULATE para reescrever o contexto, ALL para apagar o filtro de data e FILTER com MAX para dizer até onde somar. Assumindo uma tabela de calendário chamada Calendario relacionada à fato por Calendario[Data], e uma medida base Total Vendas = SUM(Vendas[Valor]), o acumulado fica assim:
Acumulado Vendas =
CALCULATE (
[Total Vendas],
FILTER (
ALL ( 'Calendario' ),
'Calendario'[Data] <= MAX ( 'Calendario'[Data] )
)
)
Vale ler devagar, porque cada parte tem um papel. O ALL('Calendario') devolve todas as datas do calendário ignorando o filtro da célula, então dentro do FILTER você tem a tabela inteira à disposição. O MAX('Calendario'[Data]) é a parte esperta: ele é avaliado no contexto externo, ou seja, devolve a última data ainda visível naquela linha. Na linha de março de 2025, MAX retorna 31 de março de 2025. O FILTER então guarda só as datas menores ou iguais a esse teto, e o CALCULATE soma as vendas dessas datas. Resultado: cada linha acumula tudo desde o início até o seu próprio fim.
Um detalhe que separa quem entende de quem só copia código: use ALL('Calendario') e não ALL('Calendario'[Data]) quando quiser que o acumulado ande junto com o ano ou o mês que estiver no eixo. Se você precisa que o acumulado zere a cada ano, troque ALL por ALLEXCEPT preservando a coluna de ano. A lógica é sempre a mesma: você decide o que apagar e o que proteger.
Para o acumulado que reinicia a cada ano, o código muda pouco:
Acumulado no Ano =
CALCULATE (
[Total Vendas],
FILTER (
ALLEXCEPT ( 'Calendario', 'Calendario'[Ano] ),
'Calendario'[Data] <= MAX ( 'Calendario'[Data] )
)
)
Ao preservar Calendario[Ano] com ALLEXCEPT, o teto móvel continua funcionando, mas o acumulado nunca soma vendas de um ano anterior. Em dezembro de 2025 ele mostra o total de 2025, e em janeiro de 2026 ele volta ao valor de janeiro, exatamente o que um diretor financeiro espera.
TOTALYTD acumula no ano sem você escrever o FILTER na mão
O DAX tem uma família de funções de time intelligence justamente para poupar você de escrever FILTER(ALL(...)) toda vez. A mais usada é TOTALYTD, que faz o acumulado do início do ano até a data corrente:
Vendas YTD =
TOTALYTD (
[Total Vendas],
'Calendario'[Data]
)
Uma linha e o acumulado no ano está pronto. TOTALYTD recebe a expressão e a coluna de data, e internamente faz o mesmo que a versão manual: remove o filtro e reaplica o intervalo do primeiro dia do ano até a data visível. A vantagem não é só menos código, é legibilidade: quem abrir o modelo entende de imediato o que a medida faz.
Onde TOTALYTD brilha de verdade é no ano fiscal. Muitas empresas fecham o exercício em junho, não em dezembro. Com o padrão manual você teria que ajustar a lógica de reinício; com TOTALYTD basta um parâmetro opcional com o fim do ano fiscal:
Vendas YTD Fiscal =
TOTALYTD (
[Total Vendas],
'Calendario'[Data],
"06-30"
)
O "06-30" diz que o ano fiscal termina em 30 de junho, então o acumulado zera em 1 de julho. Existem irmãs para outros períodos: TOTALMTD para o mês corrente e TOTALQTD para o trimestre. A tabela abaixo resume quando cada abordagem faz sentido.
| Abordagem | Quando usar | Vantagem | Limitação |
|---|---|---|---|
CALCULATE + FILTER(ALL) | Acumulado total, sem reinício, ou lógica personalizada | Controle absoluto do teto e do que preservar | Mais verboso e fácil de errar o filtro |
TOTALYTD | Acumulado no ano civil ou fiscal | Uma linha, legível, suporta ano fiscal | Reinicia sempre no ano, não serve para acumulado infinito |
TOTALMTD / TOTALQTD | Acumulado no mês ou no trimestre corrente | Rápido para painéis de fechamento | Escopo fixo no período |
ALLEXCEPT + FILTER | Acumulado que reinicia por ano mantendo granularidade fina | Controle do reinício com performance boa | Exige entender o que proteger no ALLEXCEPT |
A regra prática que aplicamos nos projetos de Power BI da Fynx: se o requisito é acumulado no ano, comece por TOTALYTD. Só caia no padrão manual quando a regra de negócio fugir do calendário padrão, por exemplo um acumulado que atravessa anos ou que depende de uma dimensão que não é data.
Sem tabela de datas marcada e ordenação correta, o acumulado mente
Aqui está o ponto que mais derruba relatório e que quase ninguém coloca em destaque. Todo cálculo de acumulado e toda função de time intelligence dependem de uma tabela de datas dedicada, contínua e marcada como tabela de datas no modelo. Isso não é preferência estética, é requisito técnico. As funções TOTALYTD e companhia precisam de um calendário sem buracos para saber o que é "início do ano" e o que é "dia anterior".
Três condições precisam estar satisfeitas para o acumulado funcionar:
- A tabela de datas é contínua. Ela precisa ter uma linha para cada dia do intervalo, sem pular datas, mesmo dias sem venda. Se faltar dia, o intervalo do acumulado fica torto.
- A tabela está marcada como tabela de datas. No Power BI, isso ativa a inteligência temporal e garante que
TOTALYTDinterprete o contexto corretamente. Sem marcar, você pode ver resultados silenciosamente errados. - O eixo do visual usa a coluna da tabela de datas, não a data da fato. O acumulado precisa filtrar contra a dimensão de calendário para que
ALL('Calendario')faça sentido.
A ordenação também importa mais do que parece. Um acumulado não tem noção visual de "linha de cima" e "linha de baixo": ele soma tudo o que for menor ou igual à data corrente, e ponto. Se você acumula por nome de mês em vez de por um índice numérico de mês, o Power BI ordena "Abril" antes de "Janeiro" no alfabeto, mas o cálculo continua correto porque compara datas, não textos. O que quebra é quando alguém troca o eixo por uma coluna de texto sem relação de ordenação com a data. Por isso a recomendação: sempre ordene o mês por uma coluna numérica (Sort by column) e mantenha o eixo ancorado na data.
Existe ainda o efeito da cardinalidade. O motor VertiPaq, que comprime o modelo do Power BI, comprime melhor colunas de baixa cardinalidade, ou seja, com poucos valores distintos. Isso não muda o resultado do acumulado, mas em modelos grandes influencia a performance da medida. Guardar datetime com hora, minuto e segundo na chave da fato é um erro clássico: separe data de hora em colunas distintas para o VertiPaq comprimir a data e o acumulado voar. É o tipo de ajuste que fazemos nos trabalhos de engenharia de dados antes mesmo de escrever a primeira medida.
Acumular por data e acumular por categoria respondem perguntas diferentes
Aqui mora a confusão mais comum depois que a fórmula base já funciona. "Acumulado por data" e "acumulado por categoria" não são a mesma coisa, e a diferença está no que você remove no ALL.
Quando você usa ALL('Calendario'), apaga só o filtro de data. Qualquer outro filtro do visual, como categoria ou região, permanece. Isso significa que se você tiver Categoria nas linhas e a data no eixo, cada categoria acumula de forma independente. Bebidas tem seu próprio running total, Alimentos tem o dele, e nenhum invade o outro. Esse é o comportamento que quase todo mundo quer, e ele acontece naturalmente porque você removeu só a data.
O erro aparece quando alguém usa ALL() sem argumento ou ALL sobre a fato inteira para "resolver" um acumulado teimoso. Isso apaga também o filtro de categoria, e aí toda linha passa a somar o total geral, independentemente da categoria. O número infla e ninguém entende por quê. Veja o contraste:
-- Acumulado independente por categoria: remove só a data
Acumulado por Categoria =
CALCULATE (
[Total Vendas],
FILTER (
ALL ( 'Calendario' ),
'Calendario'[Data] <= MAX ( 'Calendario'[Data] )
)
)
-- Acumulado global: remove data E categoria, some tudo até a data
Acumulado Global =
CALCULATE (
[Total Vendas],
FILTER (
ALL ( 'Calendario' ),
'Calendario'[Data] <= MAX ( 'Calendario'[Data] )
),
ALL ( 'Produto'[Categoria] )
)
As duas medidas têm o mesmo esqueleto. A diferença é o ALL('Produto'[Categoria]) extra na segunda, que apaga o recorte de categoria de propósito. Se você quer o acumulado global mesmo com categorias no visual, você o pede explicitamente. Se não pede, cada categoria segue sua própria trilha. A tabela abaixo deixa a decisão clara.
| Objetivo do acumulado | O que remover no filtro | O que preservar | Função-chave |
|---|---|---|---|
| Running total por data, respeitando cada categoria | Só o filtro de data | Categoria, região, produto | ALL('Calendario') |
| Running total que zera a cada ano | Data, exceto o ano | Ano e demais recortes | ALLEXCEPT('Calendario','Calendario'[Ano]) |
| Acumulado global ignorando categoria | Data e categoria | Nada além do necessário | ALL('Calendario') + ALL(Categoria) |
| Acumulado no ano padrão | Tratado pela função | Contexto fora da data | TOTALYTD |
Escolher entre esses cenários é a maior parte do trabalho de um acumulado. A sintaxe é fácil. O difícil é traduzir a pergunta de negócio em "o que este número deveria estar somando" antes de digitar qualquer CALCULATE. É o tipo de decisão que separa um relatório confiável de um que a diretoria aprende a desconfiar, e é o coração dos nossos projetos de analytics avançado.
Erros de acumulado que a gente vê toda semana em produção
Um resumo honesto dos tropeços que mais aparecem quando revisamos modelos de clientes. Nenhum deles é sofisticado, e é por isso que passam despercebidos.
- Usar a data da fato no eixo em vez da tabela de calendário. O acumulado até calcula, mas o
ALL('Calendario')não tem efeito porque o filtro veio de outra tabela. - Esquecer de marcar a tabela como tabela de datas. As funções de time intelligence podem retornar valores incorretos sem avisar.
- Calendário com buracos. Sem dias contínuos, o intervalo do acumulado fica furado e alguns períodos somem da conta.
ALLgrande demais. Apagar filtros que deveriam ficar faz o acumulado inflar para o total geral.- Confundir acumulado com média móvel. Um soma desde o início, o outro pega uma janela fixa de N períodos. Misturar os dois gera medidas que ninguém consegue explicar depois.
Perguntas frequentes
Qual a diferença entre acumulado e TOTALYTD?
O acumulado com CALCULATE e FILTER(ALL) é genérico: você define o teto e o que preservar, e ele pode somar desde o início sem nunca reiniciar. O TOTALYTD é um atalho especializado que acumula do começo do ano até a data corrente e reinicia a cada ano. Se o requisito é acumulado no ano, use TOTALYTD. Se é acumulado infinito ou com regra fora do calendário, use o padrão manual.
Preciso mesmo de uma tabela de datas separada para fazer acumulado? Sim, na prática você precisa. As funções de time intelligence exigem uma tabela de datas contínua e marcada como tal, e mesmo o padrão manual fica muito mais confiável apoiado numa dimensão de calendário. Usar a coluna de data da própria fato leva a resultados que funcionam por acaso e quebram quando o modelo cresce.
Por que meu acumulado repete o mesmo valor em todas as linhas?
Quase sempre é o ALL apagando filtro demais ou a comparação sendo feita contra a tabela errada. Se você usou ALL() sobre a fato inteira, o filtro de data e o de categoria somem, e cada linha soma o total geral. Verifique se o ALL cobre só a tabela de datas e se o MAX está lendo a coluna do calendário que está no eixo.
Como faço o acumulado zerar a cada ano sem usar TOTALYTD?
Troque ALL('Calendario') por ALLEXCEPT('Calendario','Calendario'[Ano]) no FILTER. Assim você preserva o ano no contexto, o teto móvel continua funcionando com MAX, e o acumulado nunca soma vendas de anos anteriores. É a versão manual equivalente ao TOTALYTD, útil quando você precisa de ajustes que a função pronta não oferece.
O acumulado fica lento em modelos grandes. O que fazer?
Olhe a cardinalidade da coluna de data primeiro. O VertiPaq comprime melhor colunas com menos valores distintos, então guardar datetime com hora na chave infla o modelo. Separe data e hora em colunas diferentes e evite FILTER sobre a fato quando puder filtrar a dimensão. Esses ajustes costumam resolver a maior parte dos casos.
Acumulado por categoria e acumulado por data são a mesma medida?
Não necessariamente. Se você remove só a data com ALL('Calendario') e mantém a categoria no visual, cada categoria acumula de forma independente, que é o comportamento esperado na maioria dos relatórios. Se quer um acumulado global que ignora a categoria, precisa apagar esse filtro de propósito com um ALL adicional sobre a coluna de categoria. A pergunta de negócio define qual dos dois você quer.
Onde isso deixa você
Acumulado é um daqueles cálculos que parecem triviais e cobram caro quando você os trata como triviais. A fórmula cabe em cinco linhas, mas o resultado correto depende de três coisas que ninguém vê no código: uma tabela de datas contínua e marcada, uma ordenação ancorada na data e uma decisão consciente sobre o que remover e o que preservar no filtro. Domine esses três pontos e você para de rezar para o número bater na apresentação.
Se o seu modelo tem acumulados que ninguém confia mais, fale com a gente. A Fynx faz esse tipo de trabalho todo dia, do calendário à medida final.
Quer aplicar isso na sua empresa?
A Fynx implementa BI, Power BI e Power Platform de ponta a ponta. Conte seu cenário e devolvemos um diagnóstico direto ao ponto.
Falar com um especialista