CONTEXT TRANSITION – ENTENDENDO A TRANSIÇÃO DE CONTEXTO
Sabemos que a linguagem DAX trabalha com duas formas de contexto: Filtro e Linha. Ambos, dentro das suas especificidades, trabalham em conjunto para entregar o resultado desejado quando criamos uma expressão qualquer.
Nestes posts (aqui e aqui) eu falo a respeito de como os dois contextos funcionam e interagem. Mas o que fazer quando essa interação entre eles acaba por transformar um contexto de linha em filtro?
No assunto de hoje, quero apresentar como e o que fazer para evitar uma transição de contexto quando estamos trabalhando com DAX, em particular, com a função CALCULATE.
ÍNDICE:
INTRODUÇÃO
A transição de contexto ocorre quando um contexto que deveria ser de linha passa a ser de filtro e só é possível quando utilizamos a função calculate.
Sabemos que existem duas formas de se trabalhar com um row context: funções iteradoras e colunas calculadas (automática).
Essa simples medida por exemplo, tem uma função iteradora que cria um row context (SUMX).
Essa mesma expressão dentro da medida poderia ser criada em uma coluna calculada e o resultado seria o mesmo. A diferença é que veríamos o resultado da multiplicação linha por linha, como o row context deve ser.
Desde que seja bem estruturada, podemos utilizar qualquer expressão dentro de uma função iteradora.
Quando adentramos no ‘mundo’ da calculate, notamos que ela invalida qualquer que seja o row context existente. O que ela faz é adicionar as colunas que estão no row context em argumentos de filtro, filtrando seus valores.
Veja por exemplo, essas duas colunas calculadas.
A primeira, é um uso simples da função sum aplicada em um row context e a segunda, a mesma expressão dentro de uma calculate.
O resultado obtido com as funções.
Sabendo que a função SUM é um agregação, quando ela é aplicada em um row context, ela vai de linha em linha somando os valores, obtém o total e aplica esse valor para cada linha que a tabela possui.
Mas o que aconteceu na segunda coluna, quando aplicamos ela dentro de uma calculate!?
Cada linha desta coluna calculada, que opera com o row context, se tornou um filter context. Como não passamos nenhum filtro para a calculate, cada linha se tornou seu próprio filtro e seu próprio valor.
É como se cada linha fosse única e isso é repassado ao seu valor somado, logo, o valor $8.99 é um filtro, e como na linha só há um único filtro, bem, ele é retornado.
Veja que é totalmente plausível aplicar um filtro nesta expressão. Mas note que o contexto continua alterado e mais, cada linha continua sendo um filtro único.
Embora seja possível transitar entre os contextos, não é possível abrir um row context dentro da calculate sem uma função iteradora explícita.
Por exemplo: se quisermos calcular a venda atual de um determinado produto utilizando uma função iteradora, podemos:
NOTA: a chave é transformada em filtro pois a linha dela como um todo é. Como estou criando uma coluna calculada que consulta a tabela Sales e a FK é a ProductKey, daí o resultado.
É de extrema importância destacar que quando trabalhamos com context transition, não estamos trabalhando apenas com as colunas visíveis. O engine do PBI quando identifica essa situação, adiciona de forma implícita todas as colunas e linhas de uma tabela transformando-as em filtros.
Uma prova de que a transição de contexto acaba adicionando todas as colunas como filtro é que se existir numa determinada tabela uma coluna calculada com context transition e outra for criada, a execução terminará em falha.
Perceba abaixo nos respectivos resultados que uma delas retorna o resultado enquanto a outra não.
Esta falha é causada pela dependência circular(estou estudando sobre, trarei futuramente) que o PBI acusa quando encontra esse cenário. Então, cuidado quando for criar uma coluna calculada na tabela.
Observe que as duas colunas calculadas basicamente possuem a mesma fórmula, mudando apenas a coluna de preços. Se houver uma tabela com 20 colunas e 1bi de linhas.. o PBI irá trabalhar bastante.
Uma das melhores formas de se trabalhar com context transition é utilizando tabelas menores, como por exemplo, a mesma expressão na tabela de produtos.
Veja que posso utilizar uma expressão que retorna a performance dos produtos de acordo com suas vendas com este recurso.
Neste contexto específico, temos uma excelente aplicação do uso de transição de contexto no PBI com um número bem menor de colunas e linhas, o que resulta numa performance melhor.
De qualquer forma, não se esqueçam que não existe uma forma de evitar essa situação, cuidado com o uso. Se houver uma função iteradora dentro de uma calculate ou esta em um row context, obrigatoriamente teremos um context transition.
Algumas considerações sobre transição de contexto.
- São custosos – se houver 10 colunas com 1 milhão de linhas em uma tabela, a calculate aplicará os 10 filtros 1 milhão de vezes; o que será lento.
- Não filtra uma única linha – o row context quando fora de uma calculate aplica uma iteração linha – por – linha. Quando há uma transição do contexto, o novo filter context atua em todas as linhas com o mesmo conjunto de valores.
- Utiliza colunas que estão fora da fórmula – mesmo de fora e pensando que estão excluídas, em um context transition, todas as colunas da tabela estão participando do filtro de forma implícita e oculta; acaba gerando dependências entre tabelas e colunas inesperadas.
- Cria um filtro fora do row context – uma vez que ocorre a transição, um novo filtro é criado com uma natureza diferente do usual. Agora, o engine do DAX passa a filtrar todo o modelo, propagando o filtro e afetando os relacionamentos.
- Não se importa com o tipo de row context – utilizando uma função iteradora ou não, o row context é criado sempre que houver o cenário. Caso utilize uma coluna calculada em uma calculate, haverá o context transition com todas as colunas participantes.
- Transforma todos os contextos de linha – sendo aninhado ou não, todas as iterações que forem baseadas em contexto de linha e sofreram com o transition, terão suas colunas adicionadas no filter contexts; mesmo que seja arbitrário.
NOTA: algumas tabelas podem não ter uma coluna que garanta uma linha única e caso tenha esse cenário, evite a todo custo o uso de context transition. Nesta situação, teremos um cálculo errado com valores duplicados que afetarão o resultado final.
TRANSIÇÃO DE CONTEXTO E MEDIDAS – ENTENDENDO A CALCULATE IMPLÍCITA
Sabemos que uma medida só passa a ter um ‘sentido’ quando aplicada em um contexto de filtro. Assim sendo, quando criamos uma medida qualquer, temos ali uma calculate implícita, executada pelo próprio engine do PBI.
Essa execução implícita nos obriga a tomar muito cuidado quando vamos criar e executar uma expressão em DAX pois caso não haja uma atenção prévia, podemos cair no context transition e comprometer o resultado.
Mas nem sempre uma transição no contexto de uma expressão criada será algo negativo ou deverá ser evitado. Na situação abaixo, criei uma medida comum que calcula a venda total dos produtos e utilizei a mesma expressão criando uma coluna calculada, ambas na tabela Product.
A medida e a coluna possuem a mesma expressão. Mas reparem após, no resultado, a discrepância de valores.
O que aconteceu aqui?? Como a minha coluna calculada está na tabela Product e possui uma relação de 1-N com a Sales, cada marca realizou sua própria iteração, já que estamos utilizando SUMX.
Deste modo, podemos dizer que o produto da marca A.Datum, realizou 100.230 multiplicações; quantidade de linhas na tabela Sales.
Uma das formas de solucionar este caso utilizando coluna calculada, é aplicando a transição de contexto, veja.
Nota: é possível resolver utilizando RELATEDTABLE dentro da SUMX sem a CALCULATE. Mas como não é este o assunto, não o abordarei. Convido-os a fazer o teste.
A presença da calculate somado a transição de contexto fez com que o resultado fosse corrigido. Uma vez que há a transição, cada linha é seu próprio filtro e cada produto com seu próprio cálculo.
Como aplicamos no contexto de filtro por marca, o resultado foi devidamente agrupado.
Por fim e não menos importante, existem situações onde pode parecer que seja proveitoso substituir uma medida pela forma como ela é executada.
Mas o que você quer dizer com isso?!
Irei exemplificar com duas expressões que calculam as maiores vendas em um determinado dia. A primeira expressão está encapsulando uma medida dentro de uma função maxx.
Até aqui, tudo correto, agora perceba que posso escrever essa mesma expressão com o mesmo cálculo e resultado de outra forma. Irei mostrar a medida criada e sua aplicação no gráfico.
Aplicando as duas medidas ao contexto, chegamos ao mesmo resultado.
Essa situação foi possível devido a forma como o PBI processa a primeira expressão, que é exatamente como a segunda. Mas esse movimento feito pelo usuário não deve ser adotado de forma irrestrita.
Embora o código acima possa ser substituído pela seguinte expressão, isso não é aconselhado em nenhuma hipótese. Essa substituição funcionou por não ter nenhum row context, porém, se houvesse, teríamos um resultado completamente diferente.
Sabemos que para calcular a performance de um produto, criamos uma transição de contexto filtrando apenas os produtos atuais (ação proveniente do context transition), jogando-os em um contexto de filtro.
Criamos a seguinte expressão dentro de uma coluna calculada e aplicamos ao gráfico, como na imagem abaixo.
Agora, veja o que aconteceria se jogássemos uma medida aninhada dentro de uma calculate junto com um row context (coluna calculada).
Antes de vermos o resultado no gráfico, lembre-se que toda medida possui uma calculate implícita.
Embora o cálculo esteja correto, a avaliação no geral não. Como substituímos o código dentro da coluna calculada, todo o contexto e avaliação foram alterados.
Como as duas medidas sofreram alteração da sua forma de execução por terem uma calculate implícita, sendo a segunda, com redundância, ela processou apenas os resultados “correntes”, ou seja, a variável CURRENTSALES.
Deste modo, cuidado quando for realizar ‘substituições’ nos códigos por entender que ‘seria a mesma coisa’, não é. O contexto sempre muda a forma que uma expressão é analisada pelo engine e, por mais que suas intenções sejam boas, a execução pode não ser!
CONCLUSÃO
Transição de contexto pode ser uma excelente ferramenta para criar avaliações dentro do seu modelo de dados. É uma ferramenta que pode agregar bastante, mas que precisa de conhecimento.
Tenha sempre em mente que:
- São custosas;
- Devem ser utilizadas em tabelas pequenas, em linhas e colunas;
- Precisam de um certo domínio e devem ser aplicadas no contexto correto;
- De forma implícita, envolve todas as linhas e colunas da tabela;
- Altera um contexto de linha para filtro e cada linha passa a ser seu próprio filtro;
- Não substitua a forma de execução de um código; Medidas possuem calculates implícitas.
Usem com parcimônia!
Espero que gostem do post, saúde!