Cuidado com a leitura de múltiplos arquivos em união de tabelas (JOIN)

Fala pessoal, tudo bom?

Mais um post sobre os CUIDADOS que devemos ter em nosso script para evitar erros!

Dessa vez quero falar sobre a leitura de múltiplos arquivos através dos caracteres curinga* (ahn? ajuda aí!)

Wikipedia diz: Em computação os caracteres-curinga são utilizados em casamento de padrões para substituir algum outro carácter desconhecido em uma sequência de caracteres.

É muito utilizado ao realizar um glob para procurar arquivos cujo nome ou caminho completo são desconhecidos. Nos interpretadores de comandos, o caractere asterisco (*) é reconhecido como um caractere-curinga que casa com qualquer número de caracteres desconhecidos e o caractere interrogação (?) é um curinga que casa com um único caractere desconhecido.

(entendido? rsrs! retornando ao texto) quando utilizamos em conjunto de união de tabelas (JOIN! – Neste exemplo o CONCATENATE não é um erro, mas falo disso no final).

É um erro bobo, mas fácil de ser cometido. Sabe por quê? Porque conhecemos os conceitos e comportamentos do QlikView.

Conceitos e comportamentos

Sabemos que o QlikView possui o comportamento de unir (transformar em apenas uma) tabelas que possuem os mesmos campos (com a mesma nomenclatura), por esta razão quando utilizamos o caractere curinga para fazer uma leitura múltipla de arquivo, o QlikView “faz o favor” de unir todas essas tabelas (desde que respeite a regra anterior que falamos) em apenas uma.

Exemplo

Imagine um cenário com em que precisamos carregar planilhas com notas fiscais. Cada uma das planilhas possui informação de apenas um mês/ano.

Lista de arquivos: NF_jan12.xls, NF_fev12.xls, NF_mar12.xls e NF_abr12.xls.

Esses arquivos estão perfeitos para serem carregados de uma única vez através do caractere curinga “*”.

Ah, vale lembrar que esses arquivos estão todos do mesmo padrão, isso inclui: nome das colunas e nome da aba a serem carregadas.

Em nosso script do qlikview:

01

Resultado:

02

A leitura dos quatros arquivos se resumiram em apenas uma tabela devido ao comportamento do QlikView.

Agora vamos para o exemplo em que podemos se esquecer desse comportamento e cometer um erro grave.

O problema

Agora temos uma tabela principal com o nome das empresas de um grupo, conforme imagem:

03

Queremos exibir os dados de faturamento, mas mostrando todas as empresas, ou seja, mesmo que a empresa não tenha faturado nada, esta deve exibir no relatório.

Tecnicamente, iremos carregar primeira a tabela com a descrição das empresas e depois fazer o LEFT JOIN utilizando a tabela de nota fiscal. Nosso script seria:

04

Executando o script, veja o resultado:

05 06

O resultado ficou como esperado! As empresas 4 e 5 estão sendo exibidas sem nenhuma nota fiscal e o restante das informações fez a ligação com a descrição da empresa.

Opa, mas peraí! Carreguei informações de Jan/2013, Fev/2013, Mar/2013 e Abr/2013… Por quê esta aparecendo somente os dados de Abr/2013?

O erro cometido

Aparentemente fizemos tudo certo, mas um grande detalhe de comportamento nos atrapalhou: leitura múltipla de arquivos em conjunto com união de tabelas.

Vamos pensar…

Seguindo o comportamento de execução do Qlikview, em primeiro lugar carregamos a tabela Empresas e passamos a chama-la de Fato, tendo essa tabela carregada (em memória), realizamos a leitura múltipla das tabelas de notas fiscais e fazemos a união desta tabela com a data Fato através da coluna COD_EMPRESA.

Durante a execução, após ter a tabela fato em memória, da leitura múltipla, o QlikView, faz a leitura do primeiro arquivo encontrado que é “NF_abr13.xls” (Por quê esse? Ordem Alfabética!) e realiza o LEFT JOIN com a tabela fato, depois disso o QlikView faz a leitura do próximo arquivo “NF_fev12.xls” e realiza o LEFT JOIN com a tabela fato. OPA !!!! neste momento a tabela Fato já possui as colunas do arquivo de Empresas: COD_EMPRESA e DES_EMPRESA, e as colunas do arquivo de Nota Fiscal: NF_NUMERO, NF_DT_PAGAM, NF_VL_PAGAM e COD_EMPRESA, ou seja, ao tentar fazer o LEFT JOIN do segundo arquivo “NF_fev12.xls”, o QlikView não vai encontrar (match) a combinação de registros das colunas (NF_NUMERO, NF_DT_PAGAM, NF_VL_PAGAM e COD_EMPRESA).

Não entendi!

No primeiro arquivo, a chave de ligação do LEFT JOIN é apenas a coluna COD_EMPRESA (essa é a chave correta), porém a partir do segundo arquivo, a chave de ligação passa a ser: COD_EMPESA, NF_NUMERO, NF_DT_PAGAM, e NF_VL_PAGAM porque essas colunas já foram carregadas no primeiro arquivo carregado.

Logicamente, essa nova chave não fará mais nenhuma ligação por não termos essa combinação de registros.

Desenhando (esse momento foi muito difícil pra mim rsrs!!!)

07

08

O que fazer?

Bom, se o comportamento do QlikView é esse, então nestes casos é sempre importante termos um passo antes para carregar todos esses arquivos e guardar em uma tabela temporária, para depois fazer o JOIN. Dessa forma:

09

E o concatenate?

Para concatenate não tem problema, pois o processo é de forçar a concatenação, independente de quais colunas estejam nas tabelas.

Conclusão

É muito importante conhecermos a fundo os conceitos e comportamentos do QlikView. Neste caso, nossa cabeça pensa que o QlikView vai carregar todos os arquivos (concatenando-os) para depois fazer o JOIN, porém aprendemos que o QlikView não se comporta dessa forma.

Para quem deseja, segue a aplicação de exemplo para download.

Até a próxima semana pessoal!

Melhores Práticas – ApplyMap

Fala pessoal, tudo bom?

 

Quero continuar comentando sobre melhores práticas para QlikView e neste capítulo vou falar um pouco sobre a função Applymap.

 

A função Applymap é nada mais, nada menos do que um De-Para, ou seja, substitui um valor por outro através de uma tabela de mapeamento.

Por que um de-para pode ser considerado uma melhor prática de desenvolvimento? Pois essa função, na maioria dos casos, pode ser utilizada para substituir a função JOIN com uma performance MUITO MAIOR. Lembrando que estamos falando no desempenho da execução de carga.
 
 
 

Explicação para utilização

  • Deve-se declarar uma tabela de mapeamento através da função Mapping;
  • A tabela de mapeamento deve conter apenas duas colunas;
  • A primeira coluna é sempre o De e segunda coluna sempre o Para;
  • A utilização deve ocorrer somente no método 1:1 (um para um);
  • Os nomes das colunas na tabela de mapeamento não são relevantes;
  • Por se tratar de uma tabela de mapeamento, ela existirá somente durante o carregamento (load);
  • Nenhuma associação (ligação) será feita nesta tabela;
  • Nenhum erro é gerado se você usar um nome de tabela de mapeamento que não existe

 
 
 

A função

A função ApplyMap possui três parâmetros, sendo:

  • Nome da tabela de mapeamento (deve ser usado entre apóstrofes) – Obrigatório
  • Nome do campo que sofrerá o De-para – Obrigatório
  • Retorno caso não seja encontrado nenhuma valor na tabela de mapeamento – Opcional – Caso não seja preenchido, será retornado o próprio valor do campo que não foi encontrado.

 
 
 

Exemplo simples de utilização

//Criando a tabela de mapeamento

MapaEstado:

Mapping LOAD

UF,

DESC_UF

From Estado.qvd (qvd);

 

//Utilizando a função

ApplyMap(‘MapaEstado’, UF, ‘Não Encontrei’) as DESCRICAO_ESTADO

 
 
 

Orientações

É muito importante utilizarmos o terceiro parâmetro da função com uma string que identifique que nenhum valor foi encontrado, pois dessa forma é possível identificar casos em que a regra utilizada não obteve valor.

Sempre utilize o terceiro parâmetro da função, para retornar algum valor nos casos em que não foi encontrado nenhum mapeamento.

 
 
 

Benefícios

O principal benefício da função é o desempenho na realização do De/Para. É sempre recomendado a utilização do ApplyMap ao invés de JOIN quando se trata da ligação entre duas tabelas grandes. Não acredita? Existem diversos cenários testados que comprovam, procure na comunidade :D!

Outro benefício é que, como dito anteriormente, o ApplyMap funciona apenas em casos de 1:1 (um para um), ou seja, um determinado registro possui apenas um valor a ser mapeado. Caso a função seja utilizada em casos N:1, então será retornado o primeiro registro (De N) do mapeamento para aquele caso e não será gerado nenhum produto cartesiano dos dados.

No exemplo abaixo, o vendedor Yuri possui dois apelidos, ‘Nicolett’ e ‘Yure’. Se fosse realizado um JOIN para interligar essas tabelas, os valores de vendas do vendedor Yuri seria duplicado devido as duas possibilidades de apelido, porém com ApplyMap, somente o primeiro valor encontrado no mapeamento seria retornado (‘Nicolett’) e os valores de vendas permaneceriam inalterados.

 
 

MapaApelido:
Mapping LOAD * INLINE [
NOME, APELIDO
Yuri, Nicolett
Yuri, Yure
Paula, Paulinha
];

 
Fato:
LOAD
ID,
NOME_VEND,
VALOR,
ApplyMap(‘MapaApelido’, NOME_VEND, ‘Achei não!’) as APELIDO
INLINE [
ID, NOME_VEND, VALOR
1, Yuri, 2
2, Yuri, 4
3, Paula, 5
];

 
 

Por último, e não menos importante, é possível se utilizar desse mapeamento para outra tratativa sem a necessidade de um pré-load ou resident, pois ao fazer o mapeamento o novo valor fica disponível para utilização naquele momento.

No exemplo abaixo é calculada o valor de comissão do vendedor em apenas 1 passo:

 

MapaComissao:

Mapping LOAD

VENDEDOR,

PERC_COMISSAO

FROM Comissao.qvd (qvd);

 

Fato:

LOAD

QTD_VENDA * ApplyMap(‘MapaComissao’, VEND, 0) as VALOR_COMISSAO

RESIDENT ….

 
 
 

Casos extremos

Mas Yuri, e se precisar recuperar 4 campos de outra tabela? Valeria a pena criar 4 mapeamentos ou fazer um único join?

Para identificar qual a melhor opção nessa caso eu precisaria de mais um detalhe: O volume de registros das duas tabelas é grande? Passa de 1 milhão de linhas? Vai variar muito daqui N tempo? Se o volume de dados é grande, então não tenho receio algum de te recomentar a utilização de 4 tabelas de mapeamentos e 4 applymaps, caso contrário utilize JOIN.

 
 
 

E mais alguns detalhes

Com o ApplyMap

O terceiro parâmetro da função ApplyMap é muito poderoso, pois você pode utilizar funções ou combinações de campos, por exemplo:

 

1) Retorne um conjunto de campos

ApplyMap(‘Mapeamento’, CAMPO, CAMPO2& ‘-‘ &CAMPO3)

 

2) Caso um mapeamento não funcione, tente outro.

ApplyMap(‘Mapeamento’, CAMPO, ApplyMap(‘MapeamentoDois’, CAMPO2))

/* Sinceramente esse caso é muito interessante, já presenciaram modelagens que a ligação entre duas tabelas pode ser realizada através de duas chaves diferentes? Dependendo da situação através de uma chave ou através de outra? Esse caso pode salvar sua vida rsrs */

 

3) Funções – Verifico condição

ApplyMap(‘Mapeamento’, CAMPO, if( CAMPO2 > CAMPO3, ‘É Maior’, ‘É Menor’))

 
 
 

Conclusão

É sempre bom se utilizarmos desse manual das melhores práticas e o ApplyMap é uma das principais funções para conseguirmos obter o melhor desempenho (de carga) dentro do script das nossas aplicações. Sempre analise seu atual ambiente e em caso de dúvidas faça o teste entre JOIN e ApplyMap.

 

 

 

Até a próxima semana!

 

Melhores Práticas – Hideprefix ou Hidesuffix

Fala pessoal, tudo bom?
 
Quero resgatar um pouco sobre o tópico de melhores práticas para o Qlikview e isso inclui a utilização da função Hideprefix.

Mas afinal o que é e para que server esta função?
Sua função serve essencialmente para transformar um ou diversos campos em campos de sistema, ou seja, se tornarão campos que não poderão ser utilizados pelo usuário e somente pelo desenvolvedor.
Essa é uma boa prática para evitar que nossos usuários possam obter informações incoerentes através de um campo que não possa ser utilizado no modelo.
 
 

Hideprefix

Todos os nomes de campos iniciados por um caractere de texto (ou pelo conjunto de caracteres) serão ocultos, da mesma forma que os campos de sistema.

Essa configuração funciona apenas para campos, não para variáveis.
 
 

Hidesuffix

Todos os nomes de campos terminados por um caractere de texto (ou pelo conjunto de caracteres) serão ocultos, da mesma forma que os campos de sistema.

Essa configuração funciona apenas para campos, não para variáveis.

 

Como posso utilizar essas funções?

Para ativar essas funções é necessário declará-las como variáveis. Estas variáveis são tratadas como variáveis de configurações do sistema.

 

 

Exemplos de utilização

 

Hideprefix

SET HidePrefix = ‘%’;

Neste exemplo campos iniciados com % serão tratados como campos do sistema.

 

Posso também declarar um prefixo camposto por mais de um caractere, por exemplo:

SET HidePrefix = ‘%C’;

Neste exemplo campos iniciados com %C serão tratados como campos do sistema.

 

Hidesuffix

SET Hidesuffix = ‘%’;

Neste exemplo campos terminados com % serão tratados como campos do sistema.

 

Da mesma forma que o Hideprefix, também é possível declarar um sufixo composto por mais de um caractere, por exemplo:

SET Hidesuffix = ‘ABA’;

Neste exemplo campos terminados com ABA serão tratados como campos do sistema.

 

Observação: Os campos de sistema podem ser visualizados pelo usuário desenvolvedor habilitando o checkbox “Mostrar campos do sistema” nas telas de Dimensões, expressões e lista de campos. Abaixo mostro onde encontrar essas opções em cada uma das telas:

 

Criação de objeto - Dimensões

Criação de objeto – Dimensões

Criação de objeto - Expressões

Criação de objeto – Expressões

Propriedades da Pasta - Campos

Propriedades da Pasta – Campos

 

Na prática

É sempre uma boa prática transformar os campos que são utilizados como chave de ligação entre as tabelas como um campo de sistema.

Em meu caso, sempre crio os campos chaves com o ‘%’ na frente e faço a omissão utilizando o Hideprefix.

 

Outro caso de utilização: Campos de dimensões ou expressões de relatório Ad-Hoc (Aquele tipo de relatório em que selecionamos as dimensões e expressões do gráfico através de dois campos com os valores. Colocarei um post de como faze-lo mais para frente) não devem estar disponibilizados na aplicação, devem ser tratados como campos de sistema.

 
 

Conclusão

Essa melhor prática não esta ligada a desempenho, e sim para ORGANIZAÇÃO. É importante termos o cuidado com quais campos devem ou não ser disponibilizados para o usuário, pois a tendência para o mundo Qlik (Sim, estou falando do Qlik Sense) é que os próprios usuários criem seus dashboards, relatórios e gráficos; e é uma de nossas responsabilidades disponibilizar somente o que realmente possui sentido para o usuário utilizar.

 

 

Nos vemos na próxima semana!

Abraços