Melhores Práticas – Include

Fala pessoal! Tudo bom?

Voltando ao assunto de Melhores Práticas, quero falar sobre o Include!

Quando estamos em um cliente, ou em nossa empresa, em que o ambiente de QlikView seja pequeno não percebemos a importância do Include (Mas o que é isso? Calma! rsrs). Houve uma mudança na estrutura de diretórios do ambiente? Entro no QVW do meu projeto e altero a variável de caminho (Espero que você tenha esse costume de colocar variáveis com os caminhos dos arquivos 😀 !)

Como citei anteriormente, se o meu ambiente é pequeno, com poucos qvws, entro em cada um deles e altero, mas e se meu ambiente esta grande? Vou perder algumas belas horas para alterar tudo!

Continuar lendo

Cálculo de Tempo Útil de Atendimento (SLA)

Fala pessoal! Tudo bom?

Em algum momento já se depararam com a possibilidade de cálculo do tempo de atendimento (SLA)? Considerando apenas o tempo útil da empresa?

Parece simples, mas não é!

Esse cálculo envolve muitas condicionais e tratativas. Confesso que a primeira vez que fui desenvolver, fiquei um bom tempo (MUIIITO TEMPO) pensando em todas as possibilidades do cálculo!

Vou citar algumas:

1 – Abre dentro do horário e fecha no mesmo dia dentro do horário
2 – Abre dentro do horário e fecha no mesmo dia fora do horário
3 – Abre fora do horário e fecha no mesmo dia dentro do horário
4 – Abre fora do horário e fecha no mesmo dia fora do horário
5 – Abre dentro do horário e fecha em outro dia dentro do horário
6 – Abre dentro do horário e fecha em outro dia fora do horário
7 – Abre fora do horário e fecha em outro dia dentro do horário
8 – Abre fora do horário e fecha em outro dia fora do horário
9 – Abre dentro do horário e fecha em outro dia dentro do horário
10 – Abre dentro do horário e fecha em outro dia fora do horário
11 – Abre fora do horário e fecha em outro dia dentro do horário
12 – Abre fora do horário e fecha em outro dia fora do horário
13 – Abre antes do final de semana dentro do horário e fecha no final de semana
14 – Abre antes do final de semana dentro do horário e fecha no próximo dia útil dentro do horário
15 – Abre antes do final de semana fora do horário e fecha no final de semana
16 – Abre antes do final de semana fora do horário e fecha no próximo dia útil fora do horário
17 – Abre antes do feriado dentro do horário e fecha no feriado
18 – Abre antes do feriado dentro do horário e no próximo dia útil após o feriado antes do horário
19 – Abre antes do feriado dentro do horário e no próximo dia útil após o feriado dentro do horário
20 – Abre antes do feriado fora do horário e no próximo dia útil após o feriado dentro do horário

E acredite, existem mais condicionais.

Vamos ao que interessa, a prática!

Funções

Para este exemplo, utilizaremos as seguintes funções:

  • FRAC
  • FLOOR
  • MakeTime
  • NetWorkDays
  • LastWorkDate
  • FABS
  • Interval

Manual!! Me ajude…

FRAC

Utilização: frac(x)

Descrição: Retorna a parte fracionária de um número.

Exemplos:
frac( 11,43 ) retorna 0,43

FLOOR

Utilização: floor(x [ , base [ , offset ]])

Descrição: Arredondamento de x para baixo até o múltiplo mais próximo de base com um deslocamento de offset. O
resultado é um número.

Exemplos:

floor( 2,4 ) retorna 2

floor(11,43) retorna 11

MakeTime

Utilização: MakeTime( hh [, mm [, ss [.fff ]]] )

Descrição: Retorna uma hora calculada a partir da hora hh, do minuto mm, do segundo ss com uma fração fff até um
valor em milissegundos.

Se nenhum minuto for indicado, 00 será assumido.
Se nenhum segundo for indicado, 00 será assumido.
Se nenhuma fração de segundo for indicada, 000 será assumido.
Exemplos:
maketime( 22 ) retorna 22:00:00
maketime( 22, 17 ) retorna 22:17:00
maketime( 22, 17, 52 ) retorna 22:17:52

NetWorkDays

Utilização: networkdays (data_inicial, data_final {, feriado})

Descrição: Retorna o número de dias úteis (segunda-sexta) entre e incluindo a data_inicial e a data_final,
levando em conta quaisquer feriados opcionais listados. Todos os parâmetros devem ser datas ou
datas/horas válidas.

Exemplos:
networkdays (‘2007-02-19’, ‘2007-03-01’) retorna 9
networkdays (‘2006-12-18’, ‘2006-12-31’, ‘2006-12-25’, ‘2006-12-26’) retorna 8

LastWorkDate

Utilização: lastworkdate(data_inicial, nº_de_dias_úteis {, feriado})

Descrição: Retorna a data final mais recente para obter o número_de_dias_úteis (segunda-sexta) se o início for
na data_inicial, considerando-se os feriados listados opcionalmente. Data_inicial e feriado devem
ser datas ou datas/horas válidas.

Exemplos:
lastworkdate (‘2007-02-19’, 9) retorna ‘2007-03-01’
lastworkdate (‘2006-12-18’, 8, ‘2006-12-25’, ‘2006-12-26’) retorna ‘2006-12-29’

FABS

Utilização: fabs(x)

Descrição: É o valor absoluto de x. O resultado é um número positivo.

Exemplos:
fabs( 2,4 ) retorna 2,4
fabs( -3.8 ) retorna 3.8

Interval

Utilização: interval( expressão [ , código de formato ])

Descrição: A função interval formata a expressão como um intervalo de tempo, de acordo com o caracter fornecido
como código de formato. Se o código de formato for omitido, será utilizado o formato de hora definido no
sistema operacional. Os intervalos podem ser formatados como hora, dia ou como uma combinação de dias,
horas, minutos, segundos e frações de segundos.

Exemplos:
Os exemplos abaixo supõem as seguintes configurações do sistema operacional:
Formato de data abreviada: YY-MM-DD
Formato de hora: hh:mm:ss
Separador de número decimal: .
interval( A ) em que A=0.375 retorna:
Caracter 09:00:00
Número 0.375
interval( A ) em que A=1.375 retorna:
Caracter 33:00:00
Número 1.375

Vamos ao código

Nosso exemplo vai calcular o tempo útil de SLA de uma empresa que possui como horário de trabalho das 08:00 até as 18:00 de Segunda à Sexta. Nessa empresa a hora do almoço é contabilizada!

Calcularemos o tempo de fechamento de chamados, ou seja, nossa tabela possui uma data de abertura e data de encerramento.

Vamos lá….

Em primeiro lugar crio uma variável chamada Feriado e cadastro os possíveis feriados, mas lembre-se de cadastrar esses feriados com os valores entre apóstrofes e delimitados por virgula “,”.

Segue exemplo:

SET Feriados = ’09/06/2015′,’10/06/2015′;

Em segundo, devo tratar a data de abertura e data de encerramento.

Para a coluna de data de abertura:

  • Se a abertura é antes das 08 horas, então passa a ser as 08 horas do mesmo dia
  • Se a abertura é após as 18 horas, então passa a ser as 08 horas do próximo dia útil (considerando feriados e finais de semana).

Para a coluna de data de encerramento:

  • Se o encerramento é antes das 08 horas, então passa a ser 18 horas do dia útil anterior
  • Se o encerramento é depois das 18 horas, então passa a ser 18 horas do mesmo dia

Em terceiro, faço os cálculos finais, com as condições:

  • Se a data de encerramento é igual ao dia de abertura, então faço uma coluna menos a outra e assim temos o tempo útil
  • Se a data de encerramento é diferente ao dia de abertura, então faço preciso fazer o seguinte cálculo:
    • A – uma coluna menos a outra para contabilizar a quantidade de horas no intervalo
    • B – Verifico a quantidade de dias que existe dentro desse intervalo e multiplico por 14 (14 horas não úteis dentro de um dia de 10 horas úteis). Lembre-se que das 08:00 até as 18:00 temos 10 horas, logo se o dia possui 24 horas, então neste dia temos 14 horas não úteis.
    • C – Verifico a quantidade de dias não uteis dentro do intervalo e multiplico por 24 (Esses dias não úteis são os Sáb, Dom e Feriados)
    • A conta final fica: A – B – C

Ufa!!

Bem chatinho né? Isso porque não estamos desconsiderando a hora de almoço!! Mas isso fica para um próximo post!

Desta vez não irei colar o código, mas peço que façam o download da aplicação de exemplo. Opa, Só clicar aqui para fazer o download!

Espero que gostem!

Até a próxima semana!

IntervalMatch na prática

Fala galera, tudo bom?

Já se depararam com uma situação em que era necessário fazer um JOIN entre tabelas, porém esse JOIN não é o valor exato da segunda tabela e sim um valor que esta dentro de um intervalo dela?

Por exemplo:

Tenho uma tabela com os limites das notas de uma escolha

  • de 0 até 34 é Nota “E”
  • de 35 até 40 é Nota “D”
  • de 41 até 60 é Nota “C”
  • de 61 até 80 é Nota “B”
  • de 81 até 100 é Nota “A”

A nossa segunda tabela possui as notas dos alunos.

Nem sempre teremos uma nota com o valor dos limites do intervalo e isso impossibilita de resolvermos essa situação utilizando um comando de JOIN.

Podemos criar os registros que estão faltando no intervalo e depois é só fazer Join? Sim, para esse exemplo pode até funcionar, pois no total teremos 100 registros, mas e se a nossa tabela de “Range” possuir intervalos de milhares? milhões? Acho que para esses casos não seria uma boa solução, concordam?
   

IntervalMatch

O prefixo IntervalMatch é usado para criar uma tabela comparando valores numéricos discretos com um ou mais intervalos numéricos.
Ele deve ser colocado antes do Load ou declaração de Select (SQL) que carrega os intervalos. O campo que contém os pontos de dados discretos (Notas no exemplo abaixo) já deve ter sido carregado no QlikView antes do comando com o prefixo IntervalMatch. O prefixo não lê esse campo a partir da tabela da base de dados. O prefixo transforma a tabela carregada de intervalos em uma tabela que contém uma coluna adicional: os pontos de dados numéricos discretos Ele também expande o número de registros de forma que a nova tabela tenha um registro por combinação possível de ponto de dados discreto e intervalo.
Os intervalos podem estar sobrepostos e os valores discretos estarão vinculados a todos os intervalos
correspondentes.
   

Prática

Em primeiro lugar crie a nossa tabela com os intervalos.

Grade_Notas:
LOAD * INLINE [
Min, Max, Grade
0, 34, E
35, 40, D
41, 60, C
61, 80, B
81, 100, A
];

Agora vamos criar a nossa tabela das notas por aluno

Notas:
LOAD * INLINE [
Nota, Aluno
30, Yuri
50, Edson
99, Alan
10, Geraldo
93, Henrique
];

Agora vamos para a utilização do IntervalMatch

A utilização do IntervalMatch deve-se partir da leitura da tabela de intervalos e o comando IntervalMatch deve-se relacionar com a coluna que deve ser checada dentro do intervalo:

Usando_IntervalMatch:
IntervalMatch(Nota) //Comando IntervalMatch chamando a coluna a ser checada dentro do Intervalo
LOAD
Min, //Primeiro o menor valor do intervalo
Max //depois o maior valor do intervalo
RESIDENT Grade; //Tabela de Intervalos

Como resultado agora teremos a tabela “Usando_IntervalMatch” que possui as colunas: Min, Max e Nota. Essa tabela já fez a ligação entre as tabelas Nota e Grade, veja:

IntervalMatch 01

Agora é só fazer o JOIN necessário para remover essa chave sintética. Remova a parte do IntervalMatch e faça a alteração abaixo:

LEFT JOIN (Notas)

IntervalMatch(Nota) //Comando IntervalMatch chamando a coluna a ser checada dentro do Intervalo
LOAD
Min, //Primeiro o menor valor do intervalo
Max //depois o maior valor do intervalo
RESIDENT Grade; //Tabela de Intervalos

LEFT JOIN (Notas)

LOAD
*
RESIDENT Grade;

DROP TABLE Grade;
DROP Fields Min, Max;
   

Resultado final

IntervalMatch 02
A aplicação de exemplo pode ser baixada aqui!
   

Conclusão

É uma boa prática conhecermos muitas das funções que o QlikView nos oferece, pois estas funções sempre irão nos poupar linhas e linhas de código. A utilização do IntervalMatch também é o melhor recurso para essa situação, garantindo o resultado esperando com uma ótima performance na recarga do script.

Na próxima semana irei demonstrar como utilizar o IntervalMatch Estendido 😀

Até a próxima semana!

Relatório AD-HOC

Fala pessoal, tudo beleza?

Na semana passada não foi possível fazer um post devido ao feriado de pascoa, mas voltamos agora com tudo 🙂

   

Afinal, o que é e como construir um relatório ad-hoc?

 

Em primeiro lugar precisamos entender o termo ad-hoc.
 
Sempre que utilizamos o termo ad-hoc, estamos se referindo a algo temporário e que se destina para um fim específico.
 
Neste tipo de relatório, geralmente, construímos um tabelão em que o usuário seleciona as dimensões e expressões que necessitam visualizar. Engraçado é a palavra temporário, pois se tratando de uma ferramenta super poderosa um simples tabelão não pode ser vinculado ao BI de uma grande corporação.
 
Opinião: Sabemos que isso não é verdade. O layout é importante? Muito, mas o objetivo principal é conseguir atender as necessidades do cliente da melhor maneira possível. Se para atender essa necessidade eu preciso desenvolver apenas tabelas, então bora :D!

  

Como falado anteriormente, podemos desenvolver o relatório ad-hoc apenas dentro de um tabelão ou então fazer o próprio usuário escolher o tipo de gráfico. Para esse primeiro post do assunto, irei demonstrar apenas a funcionalidade do usuário selecionar as dimensões e expressões que pretende visualizar dentro de um tabelão.

  

Em primeiro lugar precisamos criar um campo com as possíveis dimensões e outro com as expressões.

IMPORTANTE: A nomenclatura desse campo deve respeitar o prefixo utilizado na função HidePrefix, pois estes campos não deverão estar disponíveis para nossos usuários desenvolvedores (com licença named).

   

Vamos para o script!

  

1° Passo) Crie uma aba chamada AD-HOC e crie os campos (no meu exemplo vou utilizar INLINE, mas você pode ficar livre para colocar esses campos em planilha ou dentro de uma tabela em seu BD).

OBS: O conteúdo dentro do campo de dimensão e/ou expressão não necessariamente precisam possuir o mesmo nome da dimensão que será utilizada na tabela.

 

SET HidePrefix = ‘_’;

 

//Tabela com os campos de dimensões

Dimensões:
LOAD * INLINE [
_dimensao
Dimensão1
Dimensão2
Dimensão3
Dimensão4
Dimensão5
Dimensão6
Dimensão7
Dimensão8
];

 

//Tabela com expressões

Expressões:
LOAD * INLINE [
_expressao
Expressão1
Expressão2
];

  

2° Passo) Agora precisamos criar um gráfico do tipo Tabela Simples ou Tabela Dinâmica.

  

3° Passo) Após selecionar o tipo de gráfico, adicione a seguinte condição de cálculo, conforme imagem:

01 - Condição de Cálculo

Condição a ser preenchida: GetSelectedCount(_dimensao) > 0 and GetSelectedCount(_expressao) > 0

Traduzindo a expressão: Serão exibidos os dados do gráfico somente se houver, pelo menos, uma dimensão e uma expressão.

  

4° Passo) Agora precisamos adicionar as dimensões do gráfico. Note que essas dimensões não são as criadas na tabela Dimensões no primeiro passo.

Após adicionar cada uma dessas dimensões, devemos habilitar a condição “Permitir Condicional” e colocar a condição conforme a imagem abaixo:

 

02 - Condição da Dimensão

Condição a ser preenchida: SubStringCount(CONCAT(‘|’&_dimensao,’|’) & ‘|’, ‘|Dimensão1|’)

Traduzindo a expressão: Estou concatenando todos os possíveis valores do campo _dimensao e estou procurando pela string |Dimensão1|, ou seja, se o usuário selecionou o valor Dimensão1, então este campo será exibido em nosso relatório. Por que estou utilizando o pipe “|” para concatenar e para buscar a string? Pois se houver um campo Dimensão1 e um Dimensão11, então o delimitador pipe irá diferencia-lo na busca.
  

5° Passo) É necessário fazer essa ação para cada dimensão que o usuário pode selecionar.

  

6° Passo) Para as expressões deve-se habilitar o checkbox “Condicional” e fazer o mesmo, porém agora utilizando o campo _expressao na condicional, conforme imagem:

03 - Condição da Expressão

Condição a ser preenchida: SubStringCount(CONCAT(‘|’&_expressao,’|’) & ‘|’, ‘|Expressão1|’)

  

7° Passo) Disponibilizar para seleção os campos _dimensao e _expressao.

  

8° Passo) Perfumarias! 😀

  

Para fazer o download da aplicação de exemplo clique aqui.

   

Conclusão: Eu gosto muito de colocar um relatório desse tipo nos projetos, pois o usuário fica com a liberdade de criar um gráfico de acordo com o seu desejo (dentro das dimensões e expressões pré-estabelecidas).

O cuidado que precisamos ter é limitar a quantidade de dados a serem exibidos se possibilitarmos a visualização de informações de baixo nível.

  

É isso pessoal!

Na próxima semana tem mais 🙂

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!