Cada caso traz o desafio, o prompt usado com a IA, as medidas corrigidas e
o impacto relatado (nos casos 11 a 15, o resultado num modelo real de
teste). O bloco de código vai inteiro para a DAX Query
View: o EVALUATE do fim mostra o resultado, e Update model with
changes grava as medidas no modelo.
Modelo usado nos exemplos
| Tabela | Colunas usadas | Relacionamento |
|---|---|---|
TabDimCalendario | gerada por DataHora.Calendario, marcada como Tabela de Datas | 1 → * com TabFatVenda[Data], TabFatMeta[Data] e TabFatConsumo[Data] |
TabFatVenda | Data, CodigoPedido, CodigoCliente, CodigoProduto, CodigoLoja, ValorVenda, CustoVenda | * → 1 com TabDimCliente, TabDimProduto e TabDimLoja |
TabDimCliente · TabDimProduto · TabDimLoja | CodigoCliente, NomeCliente · CodigoProduto, NomeProduto · CodigoLoja, NomeLoja | lado 1 |
TabFatMeta | Data (1º dia do mês), ValorMeta | * → 1 com o calendário |
TabFatEstoque · TabFatConsumo | CodigoProduto, QuantidadeAtual · CodigoProduto, Data, QuantidadeConsumida | * → 1 com TabDimProduto |
TabFatAssinatura | CodigoCliente, DataInicio, DataFim (em branco se ativo) | sem relacionamento com o calendário |
TabFatFunil · TabDimEtapa | uma linha por oportunidade e etapa alcançada · CodigoEtapa, Ordem, NomeEtapa | * → 1 |
TabIntAtualizacao | Data da Atualização | sem relacionamento (ver Padrão: fuso horário) |
TabFatTransacao | Data da Emissão, Data do Vencimento, Data da Transação, Transação (Recebimento/Pagamento), Valor da Transação (recebimento positivo, pagamento negativo) | * → 1 com o calendário por Data da Transação (ativo); Data da Emissão e Data do Vencimento inativos |
TabDimFeriado | Data (uma linha por feriado) | sem relacionamento |
TabFatColaborador | Matrícula, Entrada, Saída (em branco se ativo) | * → 1 com o calendário por Entrada (ativo) e por Saída (inativo) |
TabFatNPS | Data, CodigoCliente, Nota (0 a 10), uma linha por resposta | * → 1 com o calendário e com TabDimCliente |
1. Vendas acumuladas no ano com comparativo AA
Setor: Varejo · Complexidade: Intermediária
Desafio: comparar as vendas acumuladas no ano com o mesmo período do ano anterior, com variação percentual e farol.
Prompt:
"Tenho TabFatVenda (Data, ValorVenda) e TabDimCalendario[Data Referência] marcada como Tabela de Datas. Crie as medidas no padrão AV/AA/Δ%/Δi: vendas acumuladas no ano, mesmo período do ano anterior, variação percentual e farol. Use VAR e DIVIDE e não mostre acumulado em meses sem venda."
DEFINE
MEASURE TabFatVenda[Total de Vendas] =
SUM ( TabFatVenda[ValorVenda] )
MEASURE TabFatVenda[Total de Vendas (ytd) AV] =
VAR vUltimaVenda = CALCULATE ( MAX ( TabFatVenda[Data] ), REMOVEFILTERS () )
RETURN
-- Meses depois da última venda ficam em branco, em vez de repetir o acumulado
IF (
MIN ( TabDimCalendario[Data Referência] ) <= vUltimaVenda,
TOTALYTD ( [Total de Vendas], TabDimCalendario[Data Referência] )
)
MEASURE TabFatVenda[Total de Vendas (ytd) AA] =
CALCULATE (
TOTALYTD ( [Total de Vendas], TabDimCalendario[Data Referência] ),
SAMEPERIODLASTYEAR ( TabDimCalendario[Data Referência] )
)
MEASURE TabFatVenda[Δ% Total de Vendas (ytd)] =
VAR vAV = [Total de Vendas (ytd) AV]
VAR vAA = [Total de Vendas (ytd) AA]
RETURN
IF ( NOT ISBLANK ( vAV ), DIVIDE ( vAV - vAA, vAA ) )
MEASURE TabFatVenda[Δi Total de Vendas (ytd)] =
VAR vVariacao = [Δ% Total de Vendas (ytd)]
RETURN
SWITCH (
TRUE (),
ISBLANK ( vVariacao ), BLANK (),
vVariacao > 0, "✔️",
vVariacao = 0, "⚠️",
"❗"
)
EVALUATE
SUMMARIZECOLUMNS (
TabDimCalendario[Ano],
TabDimCalendario[Mês #],
"AV", [Total de Vendas (ytd) AV],
"AA", [Total de Vendas (ytd) AA],
"Δ%", [Δ% Total de Vendas (ytd)],
"Δi", [Δi Total de Vendas (ytd)]
)
ORDER BY TabDimCalendario[Ano], TabDimCalendario[Mês #]- 💡 Formate
Δ%como percentual na medida. Um número continua ordenável e serve de base para formatação condicional; o texto"12,0% ▲"do prompt original não. - 💡 As UDFs
Delta.PercentualeDelta.Infoda Code Library fazem o mesmo cálculo e o mesmo símbolo. - ⚠️ No mês e no ano correntes, o AV vai até a última venda e o AA
cobre o período inteiro do ano anterior. Para comparar dias iguais,
limite o AA a
EDATE ( última venda, -12 ).
Impacto relatado: redução de 3h/semana em análises manuais; identificação de queda de 12% nas vendas de março com ação corretiva em 48h; reuniões 40% mais rápidas com os indicadores visuais.
2. Análise RFM de clientes
Setor: E-commerce · Complexidade: Avançada
Desafio: classificar clientes por Recência, Frequência e Monetário para segmentar campanhas.
Prompt:
"Tenho TabFatVenda (Data, CodigoPedido, CodigoCliente, ValorVenda) e TabDimCliente. Crie medidas RFM: recência em dias até a última venda do modelo, número de pedidos e total gasto. Classifique cada cliente em Campeão (recência ≤ 30, 3 ou mais pedidos e total acima da mediana dos clientes), Em Risco (recência ≥ 60), Novo (1 pedido) ou Regular."
DEFINE
MEASURE TabFatVenda[Total de Vendas] =
SUM ( TabFatVenda[ValorVenda] )
MEASURE TabFatVenda[# Pedidos] =
DISTINCTCOUNT ( TabFatVenda[CodigoPedido] )
MEASURE TabFatVenda[Recência (dias)] =
VAR vUltimaCompra = MAX ( TabFatVenda[Data] )
-- Data de referência: última venda do modelo, não TODAY (ver Padrão: fuso horário)
VAR vDataReferencia = CALCULATE ( MAX ( TabFatVenda[Data] ), REMOVEFILTERS () )
RETURN
IF ( NOT ISBLANK ( vUltimaCompra ), DATEDIFF ( vUltimaCompra, vDataReferencia, DAY ) )
MEASURE TabFatVenda[Segmento RFM] =
VAR vRecencia = [Recência (dias)]
VAR vFrequencia = [# Pedidos]
VAR vMonetario = [Total de Vendas]
-- Mediana do total gasto POR CLIENTE, entre todos os clientes com compra
VAR vMediana =
CALCULATE (
MEDIANX (
FILTER ( VALUES ( TabDimCliente[CodigoCliente] ), NOT ISBLANK ( [Total de Vendas] ) ),
[Total de Vendas]
),
REMOVEFILTERS ( TabDimCliente )
)
RETURN
IF (
HASONEVALUE ( TabDimCliente[CodigoCliente] ) && NOT ISBLANK ( vMonetario ),
SWITCH (
TRUE (),
vRecencia <= 30 && vFrequencia >= 3 && vMonetario > vMediana, "Campeão",
vRecencia >= 60, "Em Risco",
vFrequencia = 1, "Novo",
"Regular"
)
)
EVALUATE
TOPN (
20,
SUMMARIZECOLUMNS (
TabDimCliente[CodigoCliente],
TabDimCliente[NomeCliente],
"Recência", [Recência (dias)],
"Frequência", [# Pedidos],
"Monetário", [Total de Vendas],
"Segmento", [Segmento RFM]
),
[Monetário], DESC
)
ORDER BY [Monetário] DESC- 💡 Frequência e monetário respeitam o período filtrado; a recência sempre mede até a última venda do modelo.
- ⚠️ A mediana é recalculada para cada cliente do visual. Com dezenas de milhares de clientes, grave mediana e segmento na origem.
- ⚠️ Medida não vira eixo nem segmentação. Para filtrar por segmento,
grave a classificação como coluna de
TabDimClientena origem (SQL ou Power Query). - ⚠️ A versão anterior usava uma coluna calculada na tabela de vendas:
MAX(TabVendas[Data])semCALCULATEdevolvia a última data da tabela inteira, e oCALCULATEcomEARLIERmantinha o filtro da linha atual, por isso a frequência dava 1.
Impacto relatado: segmentação de 45.000 clientes automatizada (antes: 2 semanas no Excel); campanha para "Em Risco" recuperou R$ 280.000 em 30 dias; recompra dos "Campeões" subiu de 23% para 41%.
3. Meta × realizado com farol
Setor: Saúde e Bem-Estar · Complexidade: Básica
Desafio: exibir o atingimento da meta em verde (≥ 90%), amarelo (75% a 89%) ou vermelho (< 75%).
Prompt:
"Tenho TabFatVenda (Data, ValorVenda) e TabFatMeta (Data, ValorMeta), ambas ligadas ao calendário. Crie o percentual de atingimento da meta e um farol 🟢 🟡 🔴 com o percentual ao lado. Sem meta, não mostre nada."
DEFINE
MEASURE TabFatVenda[Total de Vendas] =
SUM ( TabFatVenda[ValorVenda] )
MEASURE TabFatMeta[Total da Meta] =
SUM ( TabFatMeta[ValorMeta] )
MEASURE TabFatMeta[Atingimento da Meta (%)] =
VAR vMeta = [Total da Meta]
VAR vUltimaVenda = CALCULATE ( MAX ( TabFatVenda[Data] ), REMOVEFILTERS () )
RETURN
-- Sem meta ou período ainda sem vendas: BLANK. Com meta e sem venda: 0%
IF (
NOT ISBLANK ( vMeta ) && MIN ( TabDimCalendario[Data Referência] ) <= vUltimaVenda,
DIVIDE ( [Total de Vendas] + 0, vMeta )
)
MEASURE TabFatMeta[Farol da Meta] =
VAR vAtingimento = [Atingimento da Meta (%)]
RETURN
IF (
NOT ISBLANK ( vAtingimento ),
SWITCH (
TRUE (),
vAtingimento >= 0.90, "🟢",
vAtingimento >= 0.75, "🟡",
"🔴"
) & " " & FORMAT ( vAtingimento, "0.0%" )
)
EVALUATE
SUMMARIZECOLUMNS (
TabDimCalendario[Ano/Mês],
"Vendas", [Total de Vendas],
"Meta", [Total da Meta],
"Atingimento", [Atingimento da Meta (%)],
"Farol", [Farol da Meta]
)
ORDER BY TabDimCalendario[Ano/Mês]- ⚠️ A meta é mensal: num visual por dia, ela aparece só no dia 1. Use o farol em mês, trimestre ou ano.
- 💡 Para colorir o número em vez de concatenar o emoji, use
Atingimento da Meta (%)com formatação condicional por regras.
Impacto relatado: reuniões de status caíram de 45 para 15 minutos; a visibilidade imediata acelerou a ação nas regiões no vermelho.
4. Previsão de ruptura de estoque
Setor: Manufatura · Complexidade: Avançada
Desafio: apontar produtos com risco de ruptura nos próximos 30 dias, pelo consumo médio dos últimos 90 dias.
Prompt:
"Tenho TabFatEstoque (CodigoProduto, QuantidadeAtual) e TabFatConsumo (CodigoProduto, Data, QuantidadeConsumida). Crie: consumo médio diário dos 90 dias até o último consumo registrado, dias de cobertura e alerta de ruptura quando a cobertura for menor que 30 dias. Use DATESINPERIOD."
DEFINE
MEASURE TabFatConsumo[Consumo Médio Diário (90d)] =
-- Janela de 90 dias terminando no último consumo do modelo (de qualquer produto),
-- e não no fim do calendário, que pode estar no futuro
VAR vFim = CALCULATE ( MAX ( TabFatConsumo[Data] ), REMOVEFILTERS () )
VAR vConsumo =
CALCULATE (
SUM ( TabFatConsumo[QuantidadeConsumida] ),
DATESINPERIOD ( TabDimCalendario[Data Referência], vFim, -90, DAY )
)
RETURN
DIVIDE ( vConsumo, 90 )
MEASURE TabFatEstoque[Estoque Atual] =
SUM ( TabFatEstoque[QuantidadeAtual] )
MEASURE TabFatEstoque[Dias de Cobertura] =
-- BLANK sem consumo, em vez de um 9999 artificial
DIVIDE ( [Estoque Atual], [Consumo Médio Diário (90d)] )
MEASURE TabFatEstoque[Alerta de Ruptura] =
SWITCH (
TRUE (),
ISBLANK ( [Consumo Médio Diário (90d)] ), "Sem consumo em 90 dias",
[Dias de Cobertura] < 30, "⚠️ Risco de ruptura",
"✔️ Estoque OK"
)
EVALUATE
FILTER (
SUMMARIZECOLUMNS (
TabDimProduto[NomeProduto],
"Estoque", [Estoque Atual],
"Consumo por Dia", [Consumo Médio Diário (90d)],
"Cobertura (dias)", [Dias de Cobertura],
"Alerta", [Alerta de Ruptura]
),
[Alerta] = "⚠️ Risco de ruptura"
)
ORDER BY [Cobertura (dias)]- 💡 Use o alerta por produto. No total, a cobertura soma estoques de itens diferentes e perde o sentido.
- 💡 Estoque zerado com consumo dá cobertura 0 e entra como risco.
Impacto relatado: identificação proativa de 47 SKUs em risco; redução de 35% nas rupturas no trimestre seguinte.
5. Retenção por coorte de aquisição
Setor: SaaS / Assinaturas · Complexidade: Avançada
Desafio: medir quantos clientes de cada coorte de aquisição — o grupo de clientes que assinou no mesmo mês — continuam ativos 1, 2, 3, 6 e 12 meses depois.
💡 O que é coorte? É um grupo de pessoas que viveram o mesmo evento inicial no mesmo período: os clientes que assinaram em janeiro de 2025 formam uma coorte; os de fevereiro, outra. O termo vem da estatística e da epidemiologia ("estudo de coorte") e corresponde ao inglês cohort. Não confunda com corte, de um "o" só, que aparece no código como
vDataCorte: a data até onde a análise olha.Por que analisar por coorte? A retenção média mistura clientes antigos e novos e esconde mudanças. Separando por mês de entrada, dá para ver se quem entrou depois de uma campanha ou de uma mudança de preço fica mais ou menos tempo.
| Coorte (mês de aquisição) | Clientes | Ativos após 3 meses | Retenção em 3 meses |
|---|---|---|---|
| 2025-01 | 200 | 150 | 75% |
| 2025-02 | 180 | 117 | 65% |
Leia a tabela por linha: cada coorte é comparada com ela mesma ao longo do tempo, e as linhas entre si mostram se a retenção está melhorando.
Prompt:
"Tenho TabFatAssinatura (CodigoCliente, DataInicio, DataFim em branco para ativos). Crie a retenção por coorte: para cada mês de aquisição e cada N em 1, 2, 3, 6 e 12, o percentual de clientes ainda ativos N meses depois do próprio início. Só conte clientes que já completaram N meses."
Crie antes a coluna e a tabela de apoio no modelo:
-- Coluna calculada em TabFatAssinatura
Mês de Aquisição = FORMAT ( TabFatAssinatura[DataInicio], "YYYY-MM" )
-- Tabela calculada, sem relacionamento: meses de retenção analisados
TabIntMesRetencao = SELECTCOLUMNS ( { 1, 2, 3, 6, 12 }, "Meses", [Value] )DEFINE
MEASURE TabFatAssinatura[Retenção (%)] =
VAR vMeses = SELECTEDVALUE ( TabIntMesRetencao[Meses] )
VAR vDataCorte = TODAY ()
-- Clientes que já completaram N meses desde o próprio início
VAR vElegiveis =
FILTER ( TabFatAssinatura, EDATE ( TabFatAssinatura[DataInicio], vMeses ) <= vDataCorte )
-- Destes, os que continuavam ativos na data-alvo (DataFim em branco = ativo)
VAR vAtivos =
FILTER (
vElegiveis,
VAR vAlvo = EDATE ( TabFatAssinatura[DataInicio], vMeses )
RETURN
ISBLANK ( TabFatAssinatura[DataFim] ) || TabFatAssinatura[DataFim] >= vAlvo
)
RETURN
IF (
NOT ISBLANK ( vMeses ),
DIVIDE (
CALCULATE ( DISTINCTCOUNT ( TabFatAssinatura[CodigoCliente] ), vAtivos ) + 0, -- 0% em vez de branco
CALCULATE ( DISTINCTCOUNT ( TabFatAssinatura[CodigoCliente] ), vElegiveis )
)
)
EVALUATE
SUMMARIZECOLUMNS (
TabFatAssinatura[Mês de Aquisição],
TabIntMesRetencao[Meses],
"Retenção", [Retenção (%)]
)
ORDER BY TabFatAssinatura[Mês de Aquisição], TabIntMesRetencao[Meses]- ⚠️ A versão anterior filtrava
DataFim >= DataAlvo: comDataFimem branco (cliente ativo), a comparação trata BLANK como 0 e o cliente ativo contava como perdido. - 💡 Troque
TODAY ()pela data da última atualização (ver Padrão: fuso horário) para o corte não mudar com o fuso do Service. - 💡 Numa matriz, coloque
Mês de Aquisiçãonas linhas eMesesnas colunas.
Impacto relatado: clientes vindos de indicação retêm 2,3× mais; a realocação do orçamento de marketing elevou o ROI em 180%.
6. Curva ABC (Pareto 80/20) dinâmica
Setor: Universal · Complexidade: Intermediária
Desafio: identificar os produtos que somam 80% das vendas, respeitando os filtros de período e região.
Prompt:
"Tenho TabFatVenda (ValorVenda) e TabDimProduto (NomeProduto). Crie o ranking do produto, o percentual acumulado de vendas do maior para o menor e a classe A (até 80%), B (até 95%) ou C, respeitando as segmentações."
DEFINE
MEASURE TabFatVenda[Total de Vendas] =
SUM ( TabFatVenda[ValorVenda] )
MEASURE TabFatVenda[Ranking do Produto] =
-- A medida dentro do RANKX faz a transição de contexto; SUM direto daria o mesmo valor para todos
IF (
HASONEVALUE ( TabDimProduto[NomeProduto] ),
RANKX ( ALLSELECTED ( TabDimProduto[NomeProduto] ), [Total de Vendas], , DESC, DENSE )
)
MEASURE TabFatVenda[Vendas Acumuladas (%)] =
VAR vVendaProduto = [Total de Vendas]
VAR vProdutos =
ADDCOLUMNS ( ALLSELECTED ( TabDimProduto[NomeProduto] ), "@Vendas", [Total de Vendas] )
VAR vAcumulado = SUMX ( FILTER ( vProdutos, [@Vendas] >= vVendaProduto ), [@Vendas] )
VAR vTotal = SUMX ( vProdutos, [@Vendas] )
RETURN
IF (
HASONEVALUE ( TabDimProduto[NomeProduto] ) && NOT ISBLANK ( vVendaProduto ),
DIVIDE ( vAcumulado, vTotal )
)
MEASURE TabFatVenda[Classe ABC] =
VAR vAcumulado = [Vendas Acumuladas (%)]
RETURN
SWITCH (
TRUE (),
ISBLANK ( vAcumulado ), BLANK (),
vAcumulado <= 0.80, "A",
vAcumulado <= 0.95, "B",
"C"
)
EVALUATE
SUMMARIZECOLUMNS (
TabDimProduto[NomeProduto],
"Vendas", [Total de Vendas],
"Ranking", [Ranking do Produto],
"Acumulado", [Vendas Acumuladas (%)],
"Classe", [Classe ABC]
)
ORDER BY [Vendas] DESC- ⚠️ A versão anterior usava
RANKX(ALL(Produto), SUM(Valor)): sem medida nemCALCULATE, a soma não muda por produto e todos ficavam em 1º lugar. - 💡 Produtos com vendas iguais entram juntos no acumulado.
- 💡 Se houver produtos com o mesmo nome, use
TabDimProduto[CodigoProduto]no ranking e no acumulado. - ⚠️ No
RANKX, oALLSELECTEDprecisa cobrir todas as colunas da entidade que podem estar no visual. Ranquear porTabDimProduto[CodigoProduto]comTabDimProduto[NomeProduto]também nas linhas deixa o filtro do nome ativo durante a iteração: os outros códigos ficam sem venda e todos aparecem em 1º. UseALLSELECTED ( TabDimProduto[CodigoProduto], TabDimProduto[NomeProduto] )ouALLSELECTED ( TabDimProduto ). Num modelo real com 3.689 códigos e 3.287 nomes de cliente, o ranking por nome somava homônimos, e o ranking por código com o nome no visual dava 1º para todos.
Impacto relatado: 23 de 847 SKUs respondem por 80% do faturamento; o foco das promoções nesses 23 gerou +15% de margem.
7. Ticket médio da loja × rede
Setor: Restaurante / Food Service · Complexidade: Básica
Desafio: comparar o ticket médio de cada loja com o da rede.
Prompt:
"Tenho TabFatVenda (CodigoPedido, CodigoLoja, ValorVenda) e TabDimLoja (NomeLoja). Crie o ticket médio, o ticket médio da rede ignorando o filtro de loja, a diferença percentual e um status: acima (≥ +10%), na média ou abaixo (≤ −10%)."
DEFINE
MEASURE TabFatVenda[Total de Vendas] =
SUM ( TabFatVenda[ValorVenda] )
MEASURE TabFatVenda[# Pedidos] =
DISTINCTCOUNT ( TabFatVenda[CodigoPedido] )
MEASURE TabFatVenda[Ticket Médio] =
DIVIDE ( [Total de Vendas], [# Pedidos] )
MEASURE TabFatVenda[Ticket Médio da Rede] =
CALCULATE ( [Ticket Médio], REMOVEFILTERS ( TabDimLoja ) )
MEASURE TabFatVenda[Ticket Médio vs Rede (%)] =
VAR vLoja = [Ticket Médio]
VAR vRede = [Ticket Médio da Rede]
RETURN
-- Loja sem venda fica em branco, e não em −100%
IF ( NOT ISBLANK ( vLoja ), DIVIDE ( vLoja - vRede, vRede ) )
MEASURE TabFatVenda[Status vs Rede] =
VAR vDiferenca = [Ticket Médio vs Rede (%)]
RETURN
SWITCH (
TRUE (),
ISBLANK ( vDiferenca ), BLANK (),
vDiferenca >= 0.10, "✔️ Acima da rede",
vDiferenca > -0.10, "⚠️ Na média",
"❗ Abaixo da rede"
)
EVALUATE
SUMMARIZECOLUMNS (
TabDimLoja[NomeLoja],
"Ticket Médio", [Ticket Médio],
"Rede", [Ticket Médio da Rede],
"Diferença", [Ticket Médio vs Rede (%)],
"Status", [Status vs Rede]
)
ORDER BY [Diferença] DESC- 💡
REMOVEFILTERS ( TabDimLoja )tira só o filtro de loja: período e produto continuam valendo para a rede.
8. Conversão do funil de vendas
Setor: Vendas B2B / CRM · Complexidade: Intermediária
Desafio: calcular a conversão entre etapas do funil e apontar o gargalo.
Prompt:
"Tenho TabFatFunil (CodigoOportunidade, CodigoEtapa), com uma linha por etapa alcançada, e TabDimEtapa (Ordem, NomeEtapa). Crie o número de oportunidades por etapa, a conversão em relação à etapa anterior e o nome da etapa com a menor conversão."
DEFINE
MEASURE TabFatFunil[# Oportunidades] =
DISTINCTCOUNT ( TabFatFunil[CodigoOportunidade] )
MEASURE TabFatFunil[Conversão da Etapa (%)] =
VAR vOrdem = SELECTEDVALUE ( TabDimEtapa[Ordem] )
-- REMOVEFILTERS tira o filtro da etapa atual antes de filtrar a anterior
VAR vAnterior =
CALCULATE (
[# Oportunidades],
REMOVEFILTERS ( TabDimEtapa ),
TabDimEtapa[Ordem] = vOrdem - 1
)
RETURN
IF ( vOrdem > 1, DIVIDE ( [# Oportunidades], vAnterior ) )
MEASURE TabFatFunil[Etapa Gargalo] =
VAR vEtapas =
FILTER (
ADDCOLUMNS (
ALLSELECTED ( TabDimEtapa[NomeEtapa], TabDimEtapa[Ordem] ),
"@Conversao", [Conversão da Etapa (%)]
),
NOT ISBLANK ( [@Conversao] )
)
RETURN
MAXX ( TOPN ( 1, vEtapas, [@Conversao], ASC ), TabDimEtapa[NomeEtapa] )
EVALUATE
SUMMARIZECOLUMNS (
TabDimEtapa[Ordem],
TabDimEtapa[NomeEtapa],
"Oportunidades", [# Oportunidades],
"Conversão", [Conversão da Etapa (%)]
)
ORDER BY TabDimEtapa[Ordem]
EVALUATE
{ [Etapa Gargalo] }- ⚠️ A versão anterior tinha dois erros:
FILTER(Etapas, Ordem = anterior)percorria só a etapa já filtrada e devolvia vazio.FIRSTNONBLANK(TOPN(...), 1)recebia uma tabela de várias colunas no lugar de uma coluna e dava erro.
- 💡 A primeira etapa não tem conversão e fica fora do gargalo.
9. Churn mensal
Setor: Telecom / SaaS · Complexidade: Avançada
Desafio: calcular o churn do mês e projetá-lo para 12 meses.
Prompt:
"Tenho TabFatAssinatura (CodigoCliente, DataInicio, DataFim em branco para ativos), sem relacionamento com TabDimCalendario. Para cada mês: clientes ativos no primeiro dia, cancelamentos no mês entre esses clientes, churn mensal e churn anualizado. Deixe em branco os meses depois da última atualização (TabIntAtualizacao)."
DEFINE
MEASURE TabFatAssinatura[# Clientes Ativos no Início] =
VAR vInicio = MIN ( TabDimCalendario[Data Referência] )
VAR vHoje = MAX ( TabIntAtualizacao[Data da Atualização] )
RETURN
-- Meses depois da última atualização ficam em branco
IF (
vInicio <= vHoje,
CALCULATE (
DISTINCTCOUNT ( TabFatAssinatura[CodigoCliente] ),
REMOVEFILTERS ( TabDimCalendario ),
TabFatAssinatura[DataInicio] < vInicio,
ISBLANK ( TabFatAssinatura[DataFim] ) || TabFatAssinatura[DataFim] >= vInicio
)
)
MEASURE TabFatAssinatura[# Cancelamentos] =
VAR vInicio = MIN ( TabDimCalendario[Data Referência] )
VAR vFim = MAX ( TabDimCalendario[Data Referência] )
RETURN
CALCULATE (
DISTINCTCOUNT ( TabFatAssinatura[CodigoCliente] ),
REMOVEFILTERS ( TabDimCalendario ),
TabFatAssinatura[DataInicio] < vInicio, -- só quem já estava ativo no início
TabFatAssinatura[DataFim] >= vInicio,
TabFatAssinatura[DataFim] <= vFim
)
MEASURE TabFatAssinatura[Churn Mensal (%)] =
DIVIDE ( [# Cancelamentos] + 0, [# Clientes Ativos no Início] ) -- mês sem cancelamento = 0%
MEASURE TabFatAssinatura[Churn Anualizado (%)] =
VAR vChurn = [Churn Mensal (%)]
RETURN
IF ( NOT ISBLANK ( vChurn ), 1 - POWER ( 1 - vChurn, 12 ) )
EVALUATE
SUMMARIZECOLUMNS (
TabDimCalendario[Ano/Mês],
"Ativos no Início", [# Clientes Ativos no Início],
"Cancelamentos", [# Cancelamentos],
"Churn Mensal", [Churn Mensal (%)],
"Churn Anualizado", [Churn Anualizado (%)]
)
ORDER BY TabDimCalendario[Ano/Mês]- ⚠️ A versão anterior contava quem começou no mês anterior, e não
quem estava ativo. Também aplicava
DATESMTDsobre a coluna da própria tabela de fatos, fora do filtro do calendário. - 💡 Use as medidas no nível de mês; em trimestre ou ano, o "início" é o primeiro dia do período inteiro.
10. Painel executivo em 10 medidas
Setor: Universal · Complexidade: Todas
Desafio: reunir os indicadores executivos de acompanhamento diário.
Prompt:
"Tenho TabFatVenda (Data, CodigoPedido, CodigoCliente, CodigoProduto, ValorVenda, CustoVenda), TabDimCalendario, TabFatMeta (Data, ValorMeta) e TabIntAtualizacao (Data da Atualização). Crie as 10 medidas de um painel executivo: faturamento, margem, ticket médio, atingimento da meta, variação sobre o mês anterior, variação sobre o ano anterior, novos clientes, produto mais vendido, dias sem venda e projeção do mês."
DEFINE
-- 1. Faturamento
MEASURE TabFatVenda[Total de Vendas] =
SUM ( TabFatVenda[ValorVenda] )
-- 2. Margem bruta
MEASURE TabFatVenda[Margem Bruta (%)] =
VAR vReceita = [Total de Vendas]
VAR vCusto = SUM ( TabFatVenda[CustoVenda] )
RETURN
DIVIDE ( vReceita - vCusto, vReceita )
-- 3. Ticket médio
MEASURE TabFatVenda[# Pedidos] =
DISTINCTCOUNT ( TabFatVenda[CodigoPedido] )
MEASURE TabFatVenda[Ticket Médio] =
DIVIDE ( [Total de Vendas], [# Pedidos] )
-- 4. Atingimento da meta (mesmas medidas do caso 3)
MEASURE TabFatMeta[Total da Meta] =
SUM ( TabFatMeta[ValorMeta] )
MEASURE TabFatMeta[Atingimento da Meta (%)] =
VAR vMeta = [Total da Meta]
VAR vUltimaVenda = CALCULATE ( MAX ( TabFatVenda[Data] ), REMOVEFILTERS () )
RETURN
IF (
NOT ISBLANK ( vMeta ) && MIN ( TabDimCalendario[Data Referência] ) <= vUltimaVenda,
DIVIDE ( [Total de Vendas] + 0, vMeta )
)
-- 5. Variação sobre o mês anterior
MEASURE TabFatVenda[Δ% Total de Vendas vs Mês Anterior] =
VAR vMesAnterior =
CALCULATE ( [Total de Vendas], DATEADD ( TabDimCalendario[Data Referência], -1, MONTH ) )
VAR vAtual = [Total de Vendas]
RETURN
-- Mês sem venda (inclusive futuro) fica em branco, e não em −100%
IF ( NOT ISBLANK ( vAtual ), DIVIDE ( vAtual - vMesAnterior, vMesAnterior ) )
-- 6. Variação sobre o ano anterior (AA)
MEASURE TabFatVenda[Δ% Total de Vendas] =
VAR vAA =
CALCULATE ( [Total de Vendas], SAMEPERIODLASTYEAR ( TabDimCalendario[Data Referência] ) )
VAR vAV = [Total de Vendas]
RETURN
IF ( NOT ISBLANK ( vAV ), DIVIDE ( vAV - vAA, vAA ) )
-- 7. Novos clientes: primeira compra da história dentro do período
MEASURE TabFatVenda[# Novos Clientes] =
VAR vInicio = MIN ( TabDimCalendario[Data Referência] )
RETURN
COUNTROWS (
FILTER (
VALUES ( TabFatVenda[CodigoCliente] ),
CALCULATE ( MIN ( TabFatVenda[Data] ), REMOVEFILTERS ( TabDimCalendario ) ) >= vInicio
)
)
-- 8. Produto mais vendido
MEASURE TabFatVenda[Produto Mais Vendido] =
-- Sem vendas, todos empatam em BLANK e o TOPN devolveria todos
IF (
NOT ISBLANK ( [Total de Vendas] ),
MAXX (
TOPN ( 1, VALUES ( TabDimProduto[NomeProduto] ), [Total de Vendas], DESC ),
TabDimProduto[NomeProduto]
)
)
-- 9. Dias desde a última venda, contados até a última atualização
MEASURE TabFatVenda[Dias sem Venda] =
VAR vUltimaVenda = CALCULATE ( MAX ( TabFatVenda[Data] ), REMOVEFILTERS ( TabDimCalendario ) )
VAR vHoje = MAX ( TabIntAtualizacao[Data da Atualização] )
RETURN
IF ( NOT ISBLANK ( vUltimaVenda ), DATEDIFF ( vUltimaVenda, vHoje, DAY ) )
-- 10. Projeção linear do mês selecionado
MEASURE TabFatVenda[Projeção do Mês] =
VAR vInicioMes = MIN ( TabDimCalendario[Data Referência] )
VAR vFimMes = EOMONTH ( vInicioMes, 0 )
VAR vHoje = MAX ( TabIntAtualizacao[Data da Atualização] )
-- Mês encerrado: corta no fim do mês e a projeção é o próprio realizado
VAR vCorte = MIN ( vFimMes, vHoje )
RETURN
IF (
HASONEVALUE ( TabDimCalendario[Ano/Mês] ) && vCorte >= vInicioMes,
DIVIDE ( [Total de Vendas], DAY ( vCorte ) ) * DAY ( vFimMes )
)
EVALUATE
SUMMARIZECOLUMNS (
TabDimCalendario[Ano/Mês],
"Faturamento", [Total de Vendas],
"Margem", [Margem Bruta (%)],
"Ticket", [Ticket Médio],
"Meta", [Atingimento da Meta (%)],
"Δ% Mês", [Δ% Total de Vendas vs Mês Anterior],
"Δ% AA", [Δ% Total de Vendas],
"Novos Clientes", [# Novos Clientes],
"Top Produto", [Produto Mais Vendido],
"Projeção", [Projeção do Mês]
)
ORDER BY TabDimCalendario[Ano/Mês]
EVALUATE
{ [Dias sem Venda] }- ⚠️ A versão anterior de
Novos ClientesfaziaCALCULATE(MIN(Data))sem tirar o filtro do período: todo cliente com compra no período contava como novo. - ⚠️ A projeção anterior usava
TODAY(): num mês passado projetava com o dia de hoje, e no ServiceTODAY()segue o UTC. - 💡
# Novos Clientesrespeita os outros filtros: com um produto selecionado, conta a primeira compra daquele produto.
Impacto relatado: painel montado em 2 horas (antes, 2 dias); 10 indicadores cobrindo 90% das perguntas da diretoria.
11. Variação sobre base negativa (saldo de caixa)
Setor: Finanças / Tesouraria · Complexidade: Intermediária
Desafio: comparar o saldo de caixa com o ano anterior quando esse saldo pode ser negativo (uma base negativa: o AA ficou no vermelho), sem que o Δ% e o farol invertam o sentido.
Prompt:
"Tenho TabFatTransacao (Data da Transação, Valor da Transação), com recebimentos positivos e pagamentos negativos, e TabDimCalendario[Data Referência] marcada como Tabela de Datas. Crie a família AV/AA/Δ/Δ%/Δi do saldo de caixa. O saldo do ano anterior pode ser negativo: o Δ% e o farol precisam mostrar melhora quando o saldo sobe, mesmo saindo de uma base negativa."
DEFINE
MEASURE TabFatTransacao[Saldo de Caixa AV] =
-- Recebimentos positivos, pagamentos negativos: a soma é o saldo líquido
SUM ( TabFatTransacao[Valor da Transação] )
MEASURE TabFatTransacao[Saldo de Caixa AA] =
CALCULATE ( [Saldo de Caixa AV], SAMEPERIODLASTYEAR ( TabDimCalendario[Data Referência] ) )
MEASURE TabFatTransacao[Δ Saldo de Caixa] =
VAR vAV = [Saldo de Caixa AV]
VAR vAA = [Saldo de Caixa AA]
RETURN
-- Sem os dois períodos não há variação: evita −100% no início e no fim da série
IF ( NOT ISBLANK ( vAV ) && NOT ISBLANK ( vAA ), vAV - vAA )
MEASURE TabFatTransacao[Δ% Saldo de Caixa] =
-- ABS na base: o sinal do Δ% segue o Δ (melhorou = positivo) mesmo com AA negativo
DIVIDE ( [Δ Saldo de Caixa], ABS ( [Saldo de Caixa AA] ) )
MEASURE TabFatTransacao[Δi Saldo de Caixa] =
-- Farol pelo Δ em reais, que nunca inverte; o Δ% explode com base pequena
VAR vDelta = [Δ Saldo de Caixa]
RETURN
SWITCH (
TRUE (),
ISBLANK ( vDelta ), BLANK (),
vDelta > 0, "✔️",
vDelta = 0, "⚠️",
"❗"
)
EVALUATE
SUMMARIZECOLUMNS (
TabDimCalendario[Ano/Mês],
"AV", [Saldo de Caixa AV],
"AA", [Saldo de Caixa AA],
"Δ", [Δ Saldo de Caixa],
"Δ%", [Δ% Saldo de Caixa],
"Δi", [Δi Saldo de Caixa]
)
ORDER BY TabDimCalendario[Ano/Mês]- ⚠️ A fórmula dos casos 1 e 10,
DIVIDE ( vAV - vAA, vAA ), inverte o sinal quando o AA é negativo: o saldo que saiu de −R$ 1,13 mi para +R$ 2,74 mi aparece como −341,5%. ComABS(valor absoluto, sem sinal) no denominador, vira +341,5%. - 💡 Com AA pequeno ou de sinal oposto ao AV, o Δ% fica enorme. Por isso o farol lê o Δ em reais, e não o Δ%.
- 💡 Serve para qualquer medida que cruza o zero: lucro, resultado operacional, fluxo de caixa, variação de estoque.
- 💡 Se o calendário termina na data de hoje, o mês corrente só tem os dias
já passados, e o
SAMEPERIODLASTYEARcompara os mesmos dias do ano anterior. É uma alternativa aoEDATEdo ⚠️ do caso 1.
Resultado num modelo real: dos meses com saldo negativo no ano anterior, os dois que melhoraram apareciam como queda na fórmula anterior (−341,5% e −101,1%).
12. Prazos em dias úteis: PMR, PMP e atraso
Setor: Finanças / Contas a Receber e a Pagar · Complexidade: Avançada
Desafio: medir, em dias úteis (sem fins de semana e feriados), quanto tempo os títulos levam para virar caixa — o PMR, prazo médio de recebimento —, para sair do caixa — o PMP, prazo médio de pagamento — e quanto atrasam em relação ao vencimento. Os prazos são ponderados pelo valor: um título de R$ 500 mil pesa mais na média que um de R$ 50.
Prompt:
"Tenho TabFatTransacao (Data da Emissão, Data do Vencimento, Data da Transação, Transação, Valor da Transação) e TabDimFeriado (Data). Crie, em dias úteis e ponderados pelo valor: prazo médio de recebimento, prazo médio de pagamento, ciclo financeiro, atraso médio sobre o vencimento e o percentual do valor baixado até o vencimento. Mesma data conta 0 dia; baixa antes do vencimento conta negativo. Inclua o valor analisado pela data de vencimento."
DEFINE
MEASURE TabFatTransacao[Prazo Efetivo (dias úteis)] =
-- Dias úteis da emissão até a baixa, ponderados pelo valor de cada transação
VAR vLinhas =
FILTER (
TabFatTransacao,
NOT ISBLANK ( TabFatTransacao[Data da Emissão] )
&& NOT ISBLANK ( TabFatTransacao[Data da Transação] )
)
VAR vPonderado =
SUMX (
vLinhas,
VAR vIni = TabFatTransacao[Data da Emissão]
VAR vFim = TabFatTransacao[Data da Transação]
-- NETWORKDAYS conta os dois extremos; começar em vIni + 1 faz a mesma data valer 0
VAR vDias =
SWITCH (
TRUE (),
vFim > vIni, NETWORKDAYS ( vIni + 1, vFim, 1, TabDimFeriado ),
vFim < vIni, - NETWORKDAYS ( vFim + 1, vIni, 1, TabDimFeriado ),
0
)
RETURN
vDias * ABS ( TabFatTransacao[Valor da Transação] )
)
RETURN
DIVIDE ( vPonderado, SUMX ( vLinhas, ABS ( TabFatTransacao[Valor da Transação] ) ) )
MEASURE TabFatTransacao[PMR (dias úteis)] =
CALCULATE ( [Prazo Efetivo (dias úteis)], TabFatTransacao[Transação] = "Recebimento" )
MEASURE TabFatTransacao[PMP (dias úteis)] =
CALCULATE ( [Prazo Efetivo (dias úteis)], TabFatTransacao[Transação] = "Pagamento" )
MEASURE TabFatTransacao[Ciclo Financeiro (dias úteis)] =
-- Positivo: a empresa recebe depois de pagar e financia a diferença
[PMR (dias úteis)] - [PMP (dias úteis)]
MEASURE TabFatTransacao[Atraso Médio (dias úteis)] =
-- Positivo = baixa depois do vencimento; negativo = antecipação
VAR vLinhas =
FILTER (
TabFatTransacao,
NOT ISBLANK ( TabFatTransacao[Data do Vencimento] )
&& NOT ISBLANK ( TabFatTransacao[Data da Transação] )
)
VAR vPonderado =
SUMX (
vLinhas,
VAR vIni = TabFatTransacao[Data do Vencimento]
VAR vFim = TabFatTransacao[Data da Transação]
VAR vDias =
SWITCH (
TRUE (),
vFim > vIni, NETWORKDAYS ( vIni + 1, vFim, 1, TabDimFeriado ),
vFim < vIni, - NETWORKDAYS ( vFim + 1, vIni, 1, TabDimFeriado ),
0
)
RETURN
vDias * ABS ( TabFatTransacao[Valor da Transação] )
)
RETURN
DIVIDE ( vPonderado, SUMX ( vLinhas, ABS ( TabFatTransacao[Valor da Transação] ) ) )
MEASURE TabFatTransacao[Baixado até o Vencimento (%)] =
VAR vTotal = SUMX ( TabFatTransacao, ABS ( TabFatTransacao[Valor da Transação] ) )
VAR vEmDia =
SUMX (
FILTER ( TabFatTransacao, TabFatTransacao[Data da Transação] <= TabFatTransacao[Data do Vencimento] ),
ABS ( TabFatTransacao[Valor da Transação] )
)
RETURN
DIVIDE ( vEmDia + 0, vTotal ) -- 0% em vez de branco quando nada foi pago em dia
MEASURE TabFatTransacao[Valor por Vencimento] =
-- Troca o relacionamento ativo (baixa) pelo inativo (vencimento) só nesta medida
CALCULATE (
SUM ( TabFatTransacao[Valor da Transação] ),
USERELATIONSHIP ( TabFatTransacao[Data do Vencimento], TabDimCalendario[Data Referência] )
)
EVALUATE
SUMMARIZECOLUMNS (
TabDimCalendario[Ano],
"PMR", [PMR (dias úteis)],
"PMP", [PMP (dias úteis)],
"Ciclo", [Ciclo Financeiro (dias úteis)],
"Atraso", [Atraso Médio (dias úteis)],
"Em Dia", [Baixado até o Vencimento (%)],
"Por Vencimento", [Valor por Vencimento],
"Por Baixa", SUM ( TabFatTransacao[Valor da Transação] )
)
ORDER BY TabDimCalendario[Ano]- ⚠️ A média simples dá o mesmo peso a um título de R$ 50 e a um de R$ 500 mil. Num modelo real, o PMP simples era 14,1 dias úteis e o ponderado, 18,5.
- ⚠️
NETWORKDAYSlinha a linha roda no motor de fórmulas: 0,9 s no modelo de teste. Com volume alto, grave os dias úteis como coluna (Power Query ou coluna calculada) e troque oSWITCHpor essa coluna noSUMX. - ⚠️ Se o calendário termina hoje, vencimentos futuros ficam fora dele: no
Valor por Vencimento, eles aparecem numa linha em branco. Estenda o calendário até o maior vencimento se precisar dessa análise. - ⚠️ A tabela tem só títulos baixados. Aging (idade dos títulos em aberto) precisa de uma tabela de saldos em aberto.
- 💡 Ciclo financeiro = PMR − PMP: quantos dias a empresa financia entre pagar o fornecedor e receber do cliente.
- 💡 O 3º argumento
1deNETWORKDAYSdefine sábado e domingo como fim de semana.TabDimFeriadoprecisa ter uma única coluna de datas.
Resultado num modelo real: o ciclo financeiro caiu de +12,2 dias úteis em 2025 para −0,8 em 2026, porque o PMP subiu de 13,7 para 26,6 dias úteis.
13. Headcount e turnover de RH
Setor: Recursos Humanos · Complexidade: Avançada
Desafio: medir, por período, o headcount (quantos colaboradores estavam ativos) no início e no fim, quantos entraram e saíram, a taxa de desligamento e o turnover (rotatividade: quanto o quadro se renovou), numa tabela com uma linha por colaborador e datas de entrada e saída.
Prompt:
"Tenho TabFatColaborador (Matrícula, Entrada, Saída em branco para ativos), com relacionamento ativo de Entrada e inativo de Saída com TabDimCalendario[Data Referência]. Para cada período, crie: ativos no início, admissões, desligamentos, ativos no fim, headcount médio, taxa de desligamento e turnover de RH ((admissões + desligamentos) ÷ 2 ÷ headcount médio). Início + admissões − desligamentos precisa bater com o fim."
DEFINE
MEASURE TabFatColaborador[# Colaboradores] =
COUNTROWS ( TabFatColaborador )
MEASURE TabFatColaborador[# Admissões] =
-- O relacionamento ativo já é por Entrada
[# Colaboradores]
MEASURE TabFatColaborador[# Desligamentos] =
CALCULATE (
[# Colaboradores],
USERELATIONSHIP ( TabFatColaborador[Saída], TabDimCalendario[Data Referência] ),
NOT ISBLANK ( TabFatColaborador[Saída] ) -- ativos fora do total
)
MEASURE TabFatColaborador[# Ativos no Início] =
VAR vInicio = MIN ( TabDimCalendario[Data Referência] )
RETURN
-- Linha (Em branco) do calendário, criada pelas Saídas em branco: sem período, sem valor
IF (
NOT ISBLANK ( vInicio ),
CALCULATE (
[# Colaboradores],
REMOVEFILTERS ( TabDimCalendario ), -- tira o filtro que chega por Entrada
TabFatColaborador[Entrada] < vInicio,
TabFatColaborador[Saída] >= vInicio || ISBLANK ( TabFatColaborador[Saída] )
) + 0 -- 0 no primeiro período, e não branco
)
MEASURE TabFatColaborador[# Ativos no Fim] =
VAR vFim = MAX ( TabDimCalendario[Data Referência] )
RETURN
IF (
NOT ISBLANK ( vFim ),
CALCULATE (
[# Colaboradores],
REMOVEFILTERS ( TabDimCalendario ),
TabFatColaborador[Entrada] <= vFim,
TabFatColaborador[Saída] > vFim || ISBLANK ( TabFatColaborador[Saída] )
)
)
MEASURE TabFatColaborador[Headcount Médio] =
DIVIDE ( [# Ativos no Início] + [# Ativos no Fim], 2 )
MEASURE TabFatColaborador[Taxa de Desligamento (%)] =
-- Desligados sobre todos que passaram pela empresa no período
DIVIDE ( [# Desligamentos] + 0, [# Ativos no Início] + [# Admissões] )
MEASURE TabFatColaborador[Turnover (%)] =
DIVIDE ( ( [# Admissões] + [# Desligamentos] ) / 2, [Headcount Médio] )
EVALUATE
SUMMARIZECOLUMNS (
TabDimCalendario[Ano],
"Início", [# Ativos no Início],
"Admissões", [# Admissões],
"Desligamentos", [# Desligamentos],
"Fim", [# Ativos no Fim],
"Confere", IF (
NOT ISBLANK ( [# Ativos no Fim] ),
[# Ativos no Início] + [# Admissões] - [# Desligamentos] = [# Ativos no Fim]
),
"Taxa de Desligamento", [Taxa de Desligamento (%)],
"Turnover", [Turnover (%)]
)
ORDER BY TabDimCalendario[Ano]- ⚠️ Com o relacionamento ativo em
Entrada, umCALCULATEsó com filtros deEntradaeSaídacontinua filtrado pelo período do visual e conta apenas quem foi admitido nele. Num modelo real, a versão semREMOVEFILTERSdava turnover de 2,5% onde o correto era 9,5%. No caso 9 isso não acontece porque a tabela de assinaturas não tem relacionamento com o calendário. - ⚠️
USERELATIONSHIPemSaídasemNOT ISBLANKjoga todos os ativos na linha (Em branco) do calendário, e o total geral de desligamentos vira o total de colaboradores (9.999 em vez de 6.296 no modelo de teste). - ⚠️ As
Saídasem branco criam uma linha (Em branco) no calendário. OIF ( NOT ISBLANK ( vFim ) … )evita que as medidas comREMOVEFILTERSmostrem valor nela. - 💡 A coluna
Conferefecha a conta: início + admissões − desligamentos = fim. Se derFALSE, a regra de quem está ativo no dia da saída não está igual nas medidas. - 💡 No primeiro período da base, o início é 0 e o turnover passa de 100%. Comece a série no segundo período.
- 💡
Headcount Médiousa a média de início e fim. Para uma média mais fiel em períodos longos, faça a média dos# Ativos no Fimde cada mês comAVERAGEX ( VALUES ( TabDimCalendario[Ano/Mês] ), [# Ativos no Fim] ). - 💡
Taxa de DesligamentoeTurnoverrespondem perguntas diferentes: a primeira mede perda; o turnover mede renovação e sobe também quando a empresa só contrata.
Resultado num modelo real: em 2021, início 3.368 + 197 admissões − 403 desligamentos = 3.162 no fim; taxa de desligamento de 11,3% e turnover de 9,2%.
14. Participação %: qual é o total?
Setor: Universal · Complexidade: Intermediária
Desafio: mostrar a participação de cada produto no faturamento sem que o denominador (o "total" da divisão) ignore o período filtrado, e escolher conscientemente entre o total do visual, o total do período e o total geral.
Prompt:
"Tenho TabFatVenda (Data, ValorVenda), TabDimProduto (NomeProduto) e TabDimCalendario[Data Referência] marcada como Tabela de Datas. Crie a participação de cada produto no faturamento de três formas: sobre o total do visual (respeitando segmentações), sobre todos os produtos no período e sobre o total geral de todos os anos. Explique quando usar cada uma."
DEFINE
MEASURE TabFatVenda[Total de Vendas] =
SUM ( TabFatVenda[ValorVenda] )
MEASURE TabFatVenda[Participação no Visual (%)] =
-- Total do visual: tira os filtros das linhas e colunas, mantém segmentações e período
DIVIDE ( [Total de Vendas], CALCULATE ( [Total de Vendas], ALLSELECTED () ) )
MEASURE TabFatVenda[Participação no Período (%)] =
-- Todos os produtos, mas só no período e nos demais filtros (loja, cliente...)
DIVIDE ( [Total de Vendas], CALCULATE ( [Total de Vendas], REMOVEFILTERS ( TabDimProduto ) ) )
MEASURE TabFatVenda[Participação no Total Geral (%)] =
-- Todos os produtos, todas as lojas, todos os anos
DIVIDE ( [Total de Vendas], CALCULATE ( [Total de Vendas], REMOVEFILTERS () ) )
EVALUATE
SUMMARIZECOLUMNS (
TabDimProduto[NomeProduto],
TREATAS ( { 2025 }, TabDimCalendario[Ano] ),
"Vendas", [Total de Vendas],
"No Visual", [Participação no Visual (%)],
"No Período", [Participação no Período (%)],
"No Total Geral", [Participação no Total Geral (%)]
)
ORDER BY [Vendas] DESC| Denominador | Soma das fatias do visual | Quando usar |
|---|---|---|
ALLSELECTED () | 100%, sempre | Gráfico de rosca, barra 100%, "quanto cada um representa do que estou vendo" |
REMOVEFILTERS ( TabDimProduto ) | 100% se não houver segmentação de produto | Participação no mercado do período, mesmo com só alguns produtos selecionados |
REMOVEFILTERS () | Menos de 100% quando há filtro de período | Peso do item na história inteira da empresa |
- ⚠️
ALL ( TabFatVenda )não é "todos os produtos". A tabela de fatos "expandida" inclui as colunas das dimensões ligadas a ela, e oALLapaga também o filtro do calendário. Num modelo real, as regiões de 2025 somavam 13,7% em vez de 100%, porque cada uma era dividida pelo faturamento de todos os anos. Numa tabela monolítica (TabFlt), o efeito é o mesmo. - ⚠️
REMOVEFILTERS ( TabDimProduto )também ignora a segmentação de produto. Se o leitor selecionar três produtos, a soma fica abaixo de 100%. Isso é o esperado nessa medida: é participação no mercado, não no visual. - ⚠️ Use
ALLSELECTED ()sem argumento só em medidas exibidas diretamente no visual. Dentro de um iterador (SUMX,RANKX,ADDCOLUMNS), ele pode devolver um total diferente do esperado. - 💡 Formate as três medidas como percentual na própria medida, e não com
FORMAT: o valor continua numérico, ordenável e disponível para formatação condicional.
Resultado num modelo real: Sudeste 62,1%, Nordeste 14,2%, Sul 10,0%,
Centro-Oeste 7,9% e Norte 5,9% de 2025. Com ALLSELECTED () somam 100%;
com ALL na tabela, somavam 13,7%.
15. NPS com zona, margem de erro e ranking confiável
Setor: Universal (Experiência do Cliente) · Complexidade: Intermediária
Desafio: calcular o NPS (Net Promoter Score: % de promotores, que dão nota 9 ou 10, menos % de detratores, que dão de 0 a 6), classificá-lo na zona certa, mostrar a margem de erro (quanto o número pode oscilar só pelo tamanho da amostra) e ranquear clientes sem premiar quem respondeu uma vez só.
Prompt:
"Tenho TabFatNPS (Data, CodigoCliente, Nota de 0 a 10) e TabDimCliente. Crie: número de respostas, promotores (9 e 10), detratores (0 a 6), NPS de −100 a +100, margem de erro de 95%, zona (Crítica até 0, Aperfeiçoamento até 50, Qualidade até 75, Excelência acima), cor da zona e ranking de clientes por NPS só para quem tem pelo menos 10 respostas. Sem respostas, nada deve aparecer, nem a cor."
DEFINE
MEASURE TabFatNPS[# Respostas] =
COUNTROWS ( TabFatNPS )
MEASURE TabFatNPS[# Promotores] =
CALCULATE ( [# Respostas], TabFatNPS[Nota] >= 9 ) -- nota 9 ou 10
MEASURE TabFatNPS[# Detratores] =
CALCULATE ( [# Respostas], TabFatNPS[Nota] <= 6 ) -- nota 0 a 6
MEASURE TabFatNPS[NPS] =
-- % promotores − % detratores, de −100 a +100; não é a média das notas
VAR vN = [# Respostas]
RETURN
IF ( vN > 0, DIVIDE ( [# Promotores] - [# Detratores], vN ) * 100 )
MEASURE TabFatNPS[Margem de Erro do NPS] =
-- Intervalo de 95%: ±1,96 × erro-padrão da diferença de duas proporções da mesma amostra
VAR vN = [# Respostas]
VAR vP = DIVIDE ( [# Promotores], vN )
VAR vD = DIVIDE ( [# Detratores], vN )
VAR vVariancia = ( vP + vD ) - POWER ( vP - vD, 2 )
RETURN
IF ( vN > 1, 1.96 * SQRT ( vVariancia / vN ) * 100 )
MEASURE TabFatNPS[Zona do NPS] =
-- Arredonda antes de classificar: a zona segue o número exibido no card
VAR vNPS = [NPS]
VAR vInteiro = ROUND ( vNPS, 0 )
RETURN
SWITCH (
TRUE (),
ISBLANK ( vNPS ), BLANK (), -- sem resposta, sem zona
vInteiro <= 0, "Crítica",
vInteiro <= 50, "Aperfeiçoamento",
vInteiro <= 75, "Qualidade",
"Excelência"
)
MEASURE TabFatNPS[Cor do NPS] =
-- Formatação condicional (Formatar por: Valor do campo); herda o BLANK da zona
SWITCH (
[Zona do NPS],
"Crítica", "#EE3E2A",
"Aperfeiçoamento", "#F69322",
"Qualidade", "#79A607",
"Excelência", "#2B579A"
)
MEASURE TabFatNPS[Ranking de Clientes por NPS] =
VAR vMinimo = 10 -- respostas mínimas para entrar no ranking
RETURN
IF (
HASONEVALUE ( TabDimCliente[CodigoCliente] ) && [# Respostas] >= vMinimo,
RANKX (
FILTER ( ALLSELECTED ( TabDimCliente[CodigoCliente] ), [# Respostas] >= vMinimo ),
[NPS], , DESC, DENSE
)
)
EVALUATE
SUMMARIZECOLUMNS (
TabDimCalendario[Ano],
"Respostas", [# Respostas],
"NPS", [NPS],
"± Margem", [Margem de Erro do NPS],
"Zona", [Zona do NPS],
"Cor", [Cor do NPS]
)
ORDER BY TabDimCalendario[Ano]- ⚠️ Uma cor por faixa sem tratar o BLANK pinta contextos vazios:
BLANK () <= 49é verdadeiro, porque o BLANK vale 0 na comparação. Num modelo real, filtros sem nenhuma resposta ficavam amarelos. A zona começa comISBLANK, e a cor herda o vazio. - ⚠️ Classificar o NPS com decimais contra limites inteiros separa o card
da cor: 50,4 aparece como "50" e cai em Qualidade. Arredonde antes, como
em
vInteiro. - ⚠️ Ranquear por NPS sem mínimo de respostas coloca no topo quem respondeu uma vez com nota 10. Num modelo real, a mediana era de 5 respostas por cliente e 1.983 clientes empatavam em 1º. Com o mínimo de 10, entram 179 clientes e 36 empatam em 1º.
- ⚠️ A margem de erro usa a aproximação normal e fica pouco confiável com menos de ~30 respostas. É justamente aí que ela mais importa como alerta: com 5 respostas, ±35 pontos.
- 💡 NPS não se soma nem se tira média: o NPS da empresa não é a média dos NPS das lojas. Sempre recalcule a partir das contagens, como a medida faz em qualquer nível do visual.
- 💡 Média das notas não é NPS. Uma média 8 pode vir de muitos neutros (NPS perto de 0) ou de promotores e detratores equilibrados (NPS também perto de 0, com clientes muito mais insatisfeitos).
- 💡 As faixas das zonas são as mais usadas no Brasil. Confirme com a área
de negócio, e mantenha os limites só na medida
Zona do NPS: a cor, os ícones e os textos derivam dela. - 💡 Para escolher o mínimo no relatório, use um parâmetro numérico
(segmentação de 1 a 30) no lugar de
vMinimo. - 💡 Mostre a margem ao lado do NPS ("86 ± 2"). Se os intervalos de dois períodos se sobrepõem, trate a variação com cautela: pode ser só oscilação da amostra.
Resultado num modelo real: 20.000 respostas, NPS 85,7 ± 0,6 no geral; por ano, entre 83,6 e 87,1, com margem de ±1,4 a ±1,7, todos na zona de Excelência.