Pular para o conteúdo
Fynx
Business Intelligence14 min de leitura

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.

F
Fynx

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 ALL sobre a tabela de datas.
  • Reaplicar um teto móvel, geralmente com MAX da 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.

AbordagemQuando usarVantagemLimitação
CALCULATE + FILTER(ALL)Acumulado total, sem reinício, ou lógica personalizadaControle absoluto do teto e do que preservarMais verboso e fácil de errar o filtro
TOTALYTDAcumulado no ano civil ou fiscalUma linha, legível, suporta ano fiscalReinicia sempre no ano, não serve para acumulado infinito
TOTALMTD / TOTALQTDAcumulado no mês ou no trimestre correnteRápido para painéis de fechamentoEscopo fixo no período
ALLEXCEPT + FILTERAcumulado que reinicia por ano mantendo granularidade finaControle do reinício com performance boaExige 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:

  1. 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.
  2. A tabela está marcada como tabela de datas. No Power BI, isso ativa a inteligência temporal e garante que TOTALYTD interprete o contexto corretamente. Sem marcar, você pode ver resultados silenciosamente errados.
  3. 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 acumuladoO que remover no filtroO que preservarFunção-chave
Running total por data, respeitando cada categoriaSó o filtro de dataCategoria, região, produtoALL('Calendario')
Running total que zera a cada anoData, exceto o anoAno e demais recortesALLEXCEPT('Calendario','Calendario'[Ano])
Acumulado global ignorando categoriaData e categoriaNada além do necessárioALL('Calendario') + ALL(Categoria)
Acumulado no ano padrãoTratado pela funçãoContexto fora da dataTOTALYTD

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.
  • ALL grande 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

Vamos transformar seus dados em decisão?

Conte seu cenário. Devolvemos um diagnóstico e uma proposta com faixa de investimento em poucos dias úteis, sem folheto, direto ao ponto.