quinta-feira, 28 de março de 2019

Dimensão com Hierarquia Auto-referenciada - Parent-Child

Uma dimensão do tipo parent-child implementa o conceito auto-referenciamento de uma árvore desbalanceada, ou seja, possui ramos com tamanhos diferentes dependendo da profundidade de cada encadeamento.

Este tipo de dimensão pode aparecer em projetos que trabalham com listas de materiais que possuem subcomponentes, em sistemas corporativos que organizam os recursos humanos em estrutura organizacionais, em contas contábeis, etc..

Os projetos criados com o modelo tradicional do analysis services multidimensional (MDX) já possuem uma configuração nativa para tratar este tipo de dimensão, basta configurar corretamente as propriedades para que funcione.

Configuração das propriedades da dimensão





























Visualizando a dimensão
















Porém, com modelos tabulares criados com DAX esse suporte nativo não existe. Para obter uma hierarquia navegável no modelo de dados, você precisa decompor os níveis até um determinado ponto para obter uma hierarquia pai-filho.

Para isso o DAX fornece funções específicas para trabalhar com uma hierarquia pai-filho usando colunas calculadas.

Hierarquias Pai-Filho em DAX


A hierarquia é implementada utilizando as funções do grupo PATH.

PATH

PATHCONTAINS

PATHITEM

PATHITEMREVERSE

PATHLENGTH


Dados de Exemplo: Este exemplo será feito com base nesta tabela, onde o código da coluna gerente é o código pai.
















1-Identificar o caminho completo: 

Utiliza a coluna Gerente para referenciar todas as relações e retornar uma string com o caminho completo com todos os níveis superiores.



















Neste exemplo, existe uma coluna com a hierarquia já criada, em algumas fontes de dados isso pode existir, se esse for o caso, o primeiro passo pode ser substituído por esta função.


















2-Descobrindo o tamanho do caminho: 

Utilizar a função PATHLENGTH para identificar o tamanho. Esse valor será útil para identificar quantos níveis devem ser criados.


















3-Criando os níveis:

Nível 1: Utilizar PARENTITEM para localizar o código do nivel 1 e fornecer o parâmetro para LOOKUPVALUE localizar o nome do profissional.

























Nível 2: A partir do nível 2 é necessário fazer uma verificação no tamanho.






























O processo se repete para quantos níveis forem necessários.

O resultado final pode ser observado em um filtro ou em uma matrix.





Fonte:
https://docs.microsoft.com/en-us/dax/parent-and-child-functions- dax
https://www.daxpatterns.com/parent-child-hierarchies/
https://docs.microsoft.com/en-us/dax/understanding-functions- for-parent-child- hierarchies-in-dax
https://www.youtube.com/watch?v=QFKTr8tAQXE&list=PLWfPHxJoa7zthSaAMlt0JkJpFeVtdHzq6&index=42



quarta-feira, 20 de março de 2019

Iterações em Conjuntos de Dados Com Cursores e Sem Cursores

Em SQL existem duas formas de fazer iterações sobre as linhas de uma tabela, acessando a cada ciclo um registro específico.


1-Acesso iterativo/procedural através do uso de CURSOR: Um objeto que carrega a tabela em memória e permite a iteração linha a linha através de variáveis.

2-Set-Based declarativo: Acessa os dados utilizando os princípios relacionais que aplicam as regras da teoria de conjuntos, que opera com tabelas como conjuntos de entradas

A linguagem de consulta SQL é diferente das linguagens de programação tradicionais, ela é essencialmente declarativa e não procedural, isso significa que você deve se preocupar em definir o que você quer e não como obter o que você quer.

Na teoria, é sempre indicado utilizar abordagem que utiliza operações Set-Based em tabelas e evitar operações com linhas individuais com cursores devido a problemas de performance.

Realizar operações com cursores acaba quebrando o princípio da teoria de conjuntos com o qual os bancos de dados relacionais foram criados, pois além de definir o quê será retornado é necessário definir como operar sobre esse dado.

Por outro lado, quando é usada a abordagem de conjuntos, não é possível garantir a ordenação precisa das linhas retornadas a menos que seja usado cuidadosamente a cláusula ORDER BY, para garantir o retorno de valores únicos para a ordenação. Já com iterações com cursores é possível garantir o controle total da ordenação.


Iteração com Cursores


Cursores são estruturas da linguagem T-SQL que permitem o processamento das linhas retornadas por uma consulta (SELECT), através de estruturas complexas de programação, como repetições ou  comandos condicionais.
O exemplo básico de cursor consiste em uma repetição (loop) onde um mesmo conjunto de comandos é executado para todas as linhas do retorno de uma consulta.

A sequência de declaração: DECLARE > OPEN > FETCH (loop) > CLOSE > DEALLOCATE

DECLARE @curcustid AS INT;

-- Cursor para percorrer os nomes dos objetos
DECLARE cust_cursor CURSOR FAST_FORWARD FOR 

  SELECT custid   FROM Sales.Customers;

  -- Abrindo Cursor para leitura
 OPEN cust_cursor;

-- Lendo a primeira linha e atribuindo às variáveis que serão atualizadas a cada iteração
FETCH NEXT FROM cust_cursor INTO @curcustid;

-- Percorrendo linhas do cursor (enquanto houver)
WHILE @@FETCH_STATUS = 0
BEGIN 

    EXEC Sales.ProcessCustomer @custid = @curcustid;

    FETCH NEXT FROM cust_cursor INTO @curcustid;
END;

-- Fechando Cursor para leitura
CLOSE cust_cursor;

-- Desalocando o cursor
DEALLOCATE cust_cursor;


Algumas considerações:
Um cursor deve estar sempre associado a uma consulta, especificada ao declarar o cursor.
O comando FETCH popula as variáveis recebidas como parâmetro com os valores da próxima linha da consulta a ser lida. O número de variáveis passadas como parâmetro deve ser igual ao número de colunas retornadas na consulta associada ao cursor.
A variável global @@FETCH_STATUS retorna o resultado da última operação FETCH executada por um cursor na conexão.
O status 0 significa que o comando FETCH retornou uma linha, qualquer outro resultado significa que não houve linha retornada.
Cursores são estruturas relativamente lentas se comparadas ao desempenho de consultas do banco. O uso descuidado desse método pode causar sérios problemas de performance.


Set-Based

Para obter o mesmo efeito e cumprir o mesmo tipo de tarefa usando somente instruções padrão sql sem usar cursores, você pode usar as cláusulas ORDER BY em conjunto com TOP(1) e um loop com WHILE.

Exemplo de iteração sem usar cursor:

DECLARE @curcustid AS INT;

--Seleciona o cliente com o menor número de código
SET @curcustid = (SELECT TOP (1) custid FROM Sales.Customers ORDER BY custid);

--Itera enquanto houver clientes com valores maiores que o atual
WHILE @curcustid IS NOT NULL
BEGIN
      EXEC Sales.ProcessCustomer @custid = @curcustid;
   
    --Atribui o valor de código do próximo cliente
     SET @curcustid = (SELECT TOP (1) custid FROM Sales.Customers WHERE custid > @curcustid ORDER BY custid);
END;
GO

Nestes casos é necessário criar indice para a coluna custid, caso contrário a tabela será toda escanada e a performance será pior do que um cursor.

Datas Relativas Usando Power BI Filter Visual, DAX e JavaScript

Um requisito frequente em painéis criados com Power BI é a definição de um filtro padrão para a data atual ou ano atual. Quando um painel é renderizado, ele apresenta os filtros que foram salvos por último e estes são sempre estáticos (não são alterados de forma relativa).

Para que não seja necessário re-filtrar o painel sempre que ele é carregado e para que a data seja atualizada sem intervenção manual, temos algumas alternativas para implementar um filtro padrão com datas relativas.

1-Utilizando a configuração do visual de filtros do Power BI

Quando um campo do tipo data é utilizado como filtro o Power BI, a configuração abaixo é apresentada como opção para definir datas relativas (relativas a data atual).

Clique no menu e selecione a opção relative.


















Será apresentada uma lista de opções para configuração do tipo de intervalo que será usado.

Para selecionar o ano atual escolha a opção "This". Irá por padrão manter o filtro do ano atual com base na data de hoje. No dia 01/01/2020 o filtro será alterado para o ano novo, sem que tenha que ser atualizado manualmente.
















Existe diferentes combinações de datas relativas, para períodos maiores é possível usar a opção "Last". Neste exemplo serão filtrados como padrão os últimos 5 anos com base na data de hoje 20/03/2019.






















2-Criando uma nova coluna no modelo de dados


Outra alternativa interessante é a de criar uma nova coluna utilizando DAX para identificar o ano atual. Este método é interessante pois permite que o filtro seja utilizando de forma implícita, sem que tenha que obrigatoriamente criar um visual do tipo filtro na tela do painel.

Último Ano = IF('Calendar'[Year]= MAX('Calendar'[Year]);"Último Ano";FORMAT('Calendar'[Date]; "YYYY"))

Na função acima, se o ano da linha atual for igual ao maior ano, ou seja, o ano mais recente, então defina como "Último Ano", senão, apresenta a data em um formato de ano.

Agora basta manter o filtro "Ultimo Ano" selecionado. Seja no próprio painel com um filtro ou seja internamente como filtro de pagina.



3-Usando JavaScript


Usando um custom visual chamado Power Slicer https://appsource.microsoft.com/en-us/product/power-bi-visuals/WA104382000 é possivel implementar também com funções javascript.

Este custom visual permite um maior controle sob as customizações do visual de um slice. Além disso possui recurso configuração valores padrão utilizando funções JavaScritp.

Crie um filtro com o Ano e informe na propriedade "Default Value" a função: (new Date().getFullYear()-5)

Essa função irá retornar o ano de 2019 e será decrementado por 5, resultando no ano de 2014.

Sempre que o painel for carregado, será filtrado de acordo como resultado da função javascript.













Definir filtro padrão baseado em uma data relativa, nos garante que os painéis não irão carregar dados desnecessários nos casos onde as análises são feitas predominantemente com base no ano atual. Estes métodos de se obter períodos relativos podem ser aplicadas dependendo da necessidade de cada cenário.


Vídeo:


terça-feira, 19 de março de 2019

Calcular Corretamente Idade em DAX

A princípio uma forma intuitiva para se fazer um cálculo de idade seria utilizar a função DateDiff para verificar a diferença entre a data atual e data de nascimento e fazer a divisão por 365.

Idade = DIVIDE(DATEDIFF(TODAY();Planilha1[Data];DAY);365)














Porém o resultado desta função é incorreto, neste exemplo a data de aniversário para completar 34 anos ainda não ocorreu e a função está marcando 34.

A forma correta de obter estes resultados, utiliza a função YEARFRAC.

 YEARFRAC  = Calcula a fração do ano representada pelo número de dias inteiros entre duas datas.

INT faz o arredondamento.









Funções para cálculo de idade que levam em consideração a fração do ano:

Idade Fracionada = YEARFRAC (Planilha1[Data]; Planilha1[Hoje] )

Idade Arredondada = INT(YEARFRAC (Planilha1[Data]; Planilha1[Hoje] ) )


Fonte:
https://www.sqlbi.com/blog/marco/2018/06/24/correct-calculate-of-age-in-dax-from-birthday/
https://docs.microsoft.com/en-us/dax/yearfrac-function-dax


Carga Incremental com SQL e Integration Services - SSIS/TSQL Incremental Load

O processo de carga e transformação em um modelo de data warehouse apresenta alguns desafios que podem ser abordados de diferentes formas, dependendo principalmente do volume de dados envolvido. Nos casos em que o volume é pequeno e o tempo de carga é baixo, uma das estratégias mais comum é a carga FULL. Nesta abordagem, o banco de dados do DW é truncado, todos seus registros são apagados e recarregados com informações atualizadas, mantendo assim a integridade entre a fonte e o DW. Apesar de ser uma técnica que mantem a integridade, ela acaba carregando dados que não foram alterados, sem necessidade, gerando um gargalo em bancos de dados maiores.

Neste artigo vamos abordar algumas alternativas para realizar uma carga incremental.

Basicamente, todos os exemplos seguem a mesma lógica que se baseia em comparar uma base de ORIGEM com uma base de DESTINO (DW) e:

1-Detectar novos registros. Registros que existem na origem mas não existem no destino
2-Detectar registros atualizados: Registros que existem em ambos bancos mas possui algum campo com valor diferente
3-Opcionalmente deletar registros no destino que foram deletados ou não existem mais na origem.


Para nossos exemplos, vamos criar algumas tabelas e inserir alguns dados:

--Criar bancos de origem e destino
CREATE DATABASE [SSISIncrementalLoad_Source]
CREATE DATABASE [SSISIncrementalLoad_Dest]

--Criar uma tabela no banco de origem
CREATE TABLE dbo.tblSource
(ColID int NOT NULL
,ColA varchar(10) NULL
,ColB datetime NULL constraint df_ColB default (getDate())
,ColC int NULL
,constraint PK_tblSource primary key clustered (ColID))

--Criar uma tabela no banco de destino
CREATE TABLE dbo.tblDest
(ColID int NOT NULL
,ColA varchar(10) NULL
,ColB datetime NULL
,ColC int NULL)

--Inserir alguns dados na origem
USE SSISIncrementalLoad_Source
GO
INSERT INTO dbo.tblSource (ColID,ColA,ColB,ColC) VALUES(0, 'A', '200180101', -1)
INSERT INTO dbo.tblSource (ColID,ColA,ColB,ColC) VALUES(1, 'B', '20180102', -2)
INSERT INTO dbo.tblSource (ColID,ColA,ColB,ColC) VALUES(2, 'N', '20180103', -3)


--Inserir alguns dados no destino
USE SSISIncrementalLoad_Dest
GO
--Inserir uma linha não alerada
INSERT INTO dbo.tblDest(ColID,ColA,ColB,ColC) VALUES(0, 'A', '200180101', -1)
--Inserir linha alterada
INSERT INTO dbo.tblDest (ColID,ColA,ColB,ColC) VALUES(1, 'C', '20190101', -2)


--Consultar novas linhas
SELECT s.ColID, s.ColA, s.ColB, s.ColC
FROM SSISIncrementalLoad_Source.dbo.tblSource s
LEFT JOIN SSISIncrementalLoad_Dest.dbo.tblDest d ON d.ColID = s.ColID
WHERE d.ColID IS NULL


Usando SQL Padrão para identificar linhas novas e alteradas:

Para iniciar, vamos aos exemplos básicos de como inserir, dados novos e atualizados:

Para inserir novas linhas, o segredo está na cláusula WHERE onde são filtradas linhas que não existem no destino, logo são linhas novas.

--Inserir no destino apenas as linhas novas
INSERT INTO SSISIncrementalLoad_Dest.dbo.tblDest (ColID, ColA, ColB, ColC)
SELECT s.ColID, s.ColA, s.ColB, s.ColC
FROM SSISIncrementalLoad_Source.dbo.tblSource s
     LEFT JOIN SSISIncrementalLoad_Dest.dbo.tblDest d ON d.ColID = s.ColID
WHERE d.ColID IS NULL

No processo de atualização, é feito um JOIN para identificar apenas as linhas que as colunas da origem e destino são diferentes e a atualização é feita apenas nestes casos.

--Atualizar dados no destino com as linhas alteradas.
UPDATE d
SET
   d.ColA = s.ColA
   ,d.ColB = s.ColB
   ,d.ColC = s.ColC
FROM SSISIncrementalLoad_Dest.dbo.tblDest d
     INNER JOIN SSISIncrementalLoad_Source.dbo.tblSource s ON s.ColID = d.ColID
WHERE (
(d.ColA != s.ColA)
OR (d.ColB != s.ColB)
OR (d.ColC != s.ColC)
)


Implementação do padrão utilizando o SSIS

Agora que vimos o padrão em sql, vamos criar um processo ETL utilizando o Integration Services.

Crie um pacote e inclua um objeto control flow do tipo data flow. Utilize as tarefas de Lookup, Condicional split e Row Command para realizar a a tarefa.





























1-Criar uma conexão Origem OLE DB: Conexão com tabela de origem

2-LEFT JOIN : Criar uma tarefa de Lookup apontando para a tabela de destino

































Fazer a junção entre origem e destino através da coluna ColID e incluir o sulfixo Dest_ nas novas colunas retornadas. Isso irá ajudar na diferenciação.


























Como por padrão a tarefa Lookup faz Inner Join e não Left Join, temos que marcar a opção de ignorar as falhas de saída. Isso vai fazer com que os casos que não houver correspondência exata entre os IDs de origem e destino, seja retornado NULL na coluna da tabela de destino. O valor NULL será usado para identificar a ausência do registro na tabela de destino.
















3-Divisão Condicional: Criar uma tarefa "Condicional Split" para separar o fluxo de novas linhas e linhas atualizadas.

New Rows: Quando a coluna ID do destino for null, isso significa que o lookup não achou, logo é novo
Changed Rows: Compara as colunas da origem e destino para identificar as diferenças, se houver alguma retorna.
Unchanged: Todos os demais casos.



4-Destino OLE DB: Crie uma tarefa de destination para enviar e inserir o fluxo de novas linhas diretamente para a tabela de destino.


























5-Update Destino Linha-por-linha: Inserir Linhas Alteradas com a tarefa OLE DB Command. Conecte o fluxo de linhas alteradas em uma tarega OLE DB Command.

Essa tarefa executa um comando sql para cada linha do fluxo de dados.

Configure as propriedades e informe o comando UPDATE com os parâmetros para serem mapeados na sequência.




































Mapeando os Parâmetros
Fazer o mapeamento do fluxo de entrada de dados com o comando sql.

As colunas da tabela destino serão atualizadas com o conteúdo da tabela de origem com base no ID (mapeado na clausula WHERE).

A sequencia que os parâmetros aparecem no comando é a sequência de numeração do nome do parâmetro.




















Esse padrão de carga incremental funciona bem, mas pode ser demorado para cargas muito volumosas, para isso existe uma variação para cargas maiores que realizam operações em conjuntos de dados e não em linha por linha, como no exemplo acima.


Padrão ETL Incremental Set-Based

Este exemplo utiliza uma tabela intermediaria stage para enviar as linhas alteradas e fazer a atualização em conjunto.
















1- Truncate Stage Update: O primeiro passo é garantir que a tabela stage esteja truncada, sem linha alguma. Para isso utilize um control flow para executar um comando sql

































2-Incremental Load Flow: O segundo passo, implementa o mesmo processo de carga padrão descrito anteriormente, mas ao invés de executar a atualização linha por linha, a tabela stage será populada com o fluxo de dados de linhas atualizadas.


























3- Set-Based Update: A terceira etapa executa um comando SQL para realizar a atualização a tabela destino com os dados da stage

































Pronto, desta maneira o processo de atualização é feito em conjunto, o processo desta forma é muito mais eficiente.

Carga Incremental utilizando o comando MERGE do T-SQL

Como alternativa ainda existe o comando MERGE disponível no T-SQL do SQL Server a partir de 2012.

Ele aplica a mesma lógica em um código bem enxuto e de fácil leitura. Ele define a origem e destino, identifica a coluna de junção nos casos de linhas alteradas, especifica as colunas que serão comparadas e ainda traz a possibilidade de fazer a exclusão.


MERGE INTO [dbo].[tblDest] AS TGT --Alvo
USING [dbo].[tblSource] AS SRC --Fonte

--Faz a comparação das linhas de SRC com TGT
ON SRC.ColID = TGT.ColID

--Quando a linha na fonte for igual a linha que já existe no alvo e tiver algum campo com valor diferente entre elas
WHEN MATCHED AND ( TGT.ColA <> SRC.ColA OR TGT.ColB <> SRC.ColB OR TGT.ColC <> SRC.ColC)
 THEN
   UPDATE
      SET TGT.ColA = SRC.ColA,
          TGT.ColB = SRC.ColB,
          TGT.ColC = SRC.ColC

--Quando a linha da fonte não existir no alvo, insere como nova linha
WHEN NOT MATCHED
    THEN
      INSERT
      VALUES(SRC.ColID, SRC.ColA, SRC.ColB, SRC.ColC)

--Quando existe alguma linha no alvo que não existe na fonte. DELETE (opcional)
WHEN NOT MATCHED BY SOURCE THEN
DELETE;

Estas são algumas técnicas relacionadas ao processo de carga ETL incremental, muito útil em situações onde é necessário manter a integridade entre a origem e o destino mas não é possível realizar uma carga full devido ao volume de dados.


Fonte:
https://andyleonard.blog/2007/07/ssis-design-pattern-incremental-loads/
http://www.sqlservercentral.com/articles/Integration+Services+(SSIS)/72492/
https://docs.microsoft.com/pt-br/sql/t-sql/statements/merge-transact-sql?view=sql-server-2017


quarta-feira, 13 de março de 2019

Curva ABC Segmentação Dinâmica com Power BI - DAX

A segmentação ABC apresentada no post anterior cria uma coluna calculada para representar o resultado da classe, o que permite a utilização dessa coluna como filtro ou como campo para apresentar em elementos gráficos. Esse é o lado bom, mas, uma coluna calculada precisa ser "calculada" em tempo de processamento do modelo e não em tempo de execução, a medida que o painel é filtrado. O problema relacionado a este padrão de implementação é o fato dele ser estático e não atualizar conforme os painéis são manipulados. Para desenvolver uma métrica dinâmica que respeite os filtros, necessariamente temos que criar medidas.

Vamos a um exemplo de como implementar a classificação ABC através de medidas.

1-Criar uma medida de RANK

Essa medida será usada para identificar os produtos que possuem valores maiores do que o produto atual.









2-Criar o acumulado das vendas até o Rank do produto atual

* para cada produto, a engine do dax cria uma tabela temporária em memória com todos os produtos com melhor rank e faz a agregação.


















3-Total geral dos produtos selecionados

Para identificar a proporção do percentual do total do valor agregado e obter a métrica para gerar a classificação ABC.










É importante dizer que a função ALLSELECT() garante que o filtro do contexto explicito do painel é considerado e a medida irá ser calculada considerando os filtros realizados pelo usuário.


4-Dividir o acumulado atual com o total geral de produtos para obter o percentual do total







5-Definir a classe com base no percentual




















Também é possível obter o mesmo resultado utilizando a função TOPN para obter o acumulado e extrair a classificação dele.












Essa medida cria uma tabela temporário com os produtos TOPN até o Rank atual do produto. Exemplo: se o rank do produto é 10 a função irá retornar uma tabela com os 10 primeiros que será usada pela CALCULATE para fazer a agregação.



Como o resultado das Classes é obtido por uma medida, podemos usar em uma matrix como no exemplo abaixo..



































Mas a princípio não é possível usar a medida em um gráfico. Uma alternativa interessante para apresentar um quantitativo agregado seria usar cards.

Para apresentar em cards temos que criar 3 novas medidas, uma para cada classe



Resultado Final






Vídeo:




Curva ABC Segmentação Estática com Power BI - DAX

Análise de curva ABC é um tipo de padrão de segmentação que leva em consideração a classificação de um item (produto, cliente, etc) por categorias (A, B, C) com base no acumulado dos totais dos maiores valores para os menores.

É uma forma de identificar o princípio de Pareto, que considera que os itens com maiores resultados são poucos, mas impactam significativamente no resultado.

Os itens da classe A teoricamente são os mais importantes para o negócio. O valor deles deve ser avaliado com mais frequência, enquanto os itens da classe C são menos importantes e os itens da classe B são opcionais.


Cenários de utilização



Segmentação de clientes: separar os clientes por categorias para auxiliar a alocação de recursos de marketing.

Gerenciamento de ativos: gerenciar os estoques, aumentar a disponibilidade de estoque e negociar preços melhores para produtos na classe A, reduzindo o tempo e os recursos para itens nas classes B.

Este padrão de implementação é estático, isso significa que a classificação ABC é definida para um item em tempo de processamento do modelo e não em tempo de execução, como ocorre com as medidas. Isso ocorre porque a implementação é feita com colunas calculadas, e colunas calculadas são previamente armazenadas no modelo.

Etapas para implementação.


1-Criar uma coluna calculada na tabela de produtos com as vendas acumuladas por produto.

Essa primeira coluna faz a agregação do acumulado dos totais de vendas de produto, levando em consideração apenas os valores superiores ao valor da linha atual.































As vendas são acumuladas do maior para o menor valor.




2-Criar uma coluna calculada na tabela produto, com a venda total geral











É necessário usar a função ALL() para garantir que não haverá a transição de contexto e o total retornado irá ignorar o filtro de produto.



3- Gerar o percentual do acumulado.



















Essa métrica será usada como parâmetro para definir a classe.




4-Definindo a classificação ABC

































Opções de visualização:

Os produtos estão ordenados pelos totais de vendas e as classes estão respeitando o acumulado de até 80% para classe A até 90% para classe B.






Outra forma de apresentar os resultados, pode ser o gráfico de linhas e colunas, que configurado de forma decrescente do acumulado evidencia a suavização da curva.





















Vídeo:



Fontes:
Power BI & DAX Avançado - Guia Completo: https://www.udemy.com/share/1002h2B0QfeF1QRw==/

https://www.daxpatterns.com/abc-classification/

https://exceleratorbi.com.au/cumulative-running-total-based-on- highest-value/

https://community.powerbi.com/t5/Quick-Measures- Gallery/Dynamic-ABC- Classification/m-p/479146#M180

Formatação condicional baseada em expressões DAX

Na época que o report server era a principal ferramenta de relatórios/painéis da Microsoft, uma das características que eu mais gostava era a possibilidade de criar expressões em quase tudo que era renderizado na tela. Isso dava uma grande flexibilidade e permitia usar a criatividade para os mais diversos requisitos dos clientes. Com o surgimento do Power BI alguns visuais passaram a ficar bem mais "engessados". Mas em releases recentes vem surgindo opções interessantes de formatações condicionais que trazem de volta certo grau de customização nos visuais. 

A partir da evolução dos recursos de formatação condicional em visuais de tabelas do Power BI tornou-se possível fazer formatações condicionais utilizando os resultados obtidos por expressões implementadas por medidas.

Vamos a um exemplo de aplicação destes recursos: Criar uma formatação condicional em colunas de uma tabela com base na lógica de uma medida. 

Formatar apenas os maiores e menores resultados. 

 1-Dados de exemplo: Lista de clientes e valores de receita. Bem simples para facilitar a visualização.




2-Criar uma medida de RANK para usar na lógica da formatação condicional. Essa será a medida que será avaliada.










3-Criar nova medida para definir a cor que será usada para a formatação condicional. 

A função SWITCH avalia as condições na sequência.










Neste momento você pode ser implementar a lógica da formatação condicional específica para sua necessidade de acordo com a medida que será monitorada. Neste exemplo é o rank, mas poderia ser qualquer outra medida. 

* o resultado precisa ser o nome de uma cor. 

Com a medida pronta, acesse as propriedades da tabela.














4-Configurar com base na medida. 

Selecionar a opção "Field Value" e a medida criada. 



















O resultado, 


































* Esse processo pode ser repetido para definir a formatação condicional também da fonte do texto da célula, de forma que contraste melhor com as cores do background. 


5-Formatar TOPN
Uma variação do nosso exemplo poderia ser destacar o TOP3 clientes.

Este exemplo está destacando apenas o 3 primeiros e os 3 últimos. 










































Mas ainda assim estamos fixando o valor dos últimos clientes. Caso a lista de cliente aumente essa medida não ira atender nosso requisito. 

Para resolver isso podemos usar uma variável e alterar a nossa medida.

















































Fonte: http://radacad.com/dax-and-conditional-formatting-better-together-find-the-biggest-and-smallest-numbers-in-the-column


Video