segunda-feira, 26 de outubro de 2009

Obtendo o primeiro dia do mês

Como no oracle existe a função last_day, e não existe uma 'first_day', muitas vezes nos deparamos com códigos para obter o primeiro dia do mês, quando existe o comando trunc que nos dá isso de barbada! Observe:

SQL> select trunc(sysdate,'mm') from dual
2 /

TRUNC(SY
--------
01/10/09

o mesmo vale para o primeiro dia do ano:

SQL> select trunc(sysdate,'rrrr') from dual
2 /

TRUNC(SY
--------
01/01/09

quinta-feira, 15 de outubro de 2009

Manipulação de tabelas de memória

Muitas vezes temos que trabalhar com tabelas de memória para manipular dados. Existem vários tipos de tabela, sendo a mais comumente utilizada a que indexa os registro por um inteiro binário (binary_integer).

Para o uso desta tabela, encontramos um pequeno problema quando temos que deletar um registro qualquer desta tabela, visto que ai, a mesma acaba perdendo a sequencia da indexação... veja o exemplo:

SQL> DECLARE
2 TYPE T_A IS TABLE OF VARCHAR2(10) INDEX BY BINARY_INTEGER;
3 V_A T_A;
4 BEGIN
5 FOR I IN 1..10
6 LOOP
7 V_A(I) := 'A - '||I;
8 END LOOP;
9 V_A.DELETE(3);
10 FOR I IN V_A.FIRST..V_A.LAST
11 LOOP
12 DBMS_OUTPUT.PUT_LINE( V_A(I));
13 END LOOP;
14 END;
15 /
A - 1
A - 2
DECLARE
*
ERRO na linha 1:
ORA-01403: no data found
ORA-06512: at line 12


Neste exemplo, eu criei uma tabela de memória, e inicializei com 10 registros. Logo após, deletei o terceiro registro, para simular, e mandei mostrar os registros constantes na tabela. Note que no momento que o programa chegou no registro faltante, ele deu um erro de no data found.
Sendo assim, mesmo usando a varredura da tabela de memória do first para o last, ele ainda varre todos os indices, inclusive aquele que já foi deletado!!!

Isto pode ser resolvido facilmente, usando a função exists da tabela de memória, como no exemplo abaixo:

SQL> DECLARE
2 TYPE T_A IS TABLE OF VARCHAR2(10) INDEX BY BINARY_INTEGER;
3 V_A T_A;
4 BEGIN
5 FOR I IN 1..10
6 LOOP
7 V_A(I) := 'A - '||I;
8 END LOOP;
9 V_A.DELETE(3);
10 FOR I IN V_A.FIRST..V_A.LAST
11 LOOP
12 IF V_A.EXISTS(I) THEN
13 DBMS_OUTPUT.PUT_LINE( V_A(I));
14 END IF;
15 END LOOP;
16 END;
17 /
A - 1
A - 2
A - 4
A - 5
A - 6
A - 7
A - 8
A - 9
A - 10

Procedimento PL/SQL concluído com sucesso.

quarta-feira, 14 de outubro de 2009

Cuidado com o lpad/rpad

Bom, todos que trabalham com oracle, já tiveram que alguma vez usar as funções de complementação de caracteres (lpad/rpad). Ela basicamente serve para preencher um variável ou coluna string com um caracter até obter o tamanho desejado, como no exemplo abaixo:

SQL> select lpad('alguma coisa',20,'x') from dual
2 /

LPAD('ALGUMACOISA',2
--------------------
xxxxxxxxalguma coisa


Mas o que pouca gente sabe, é que dependendo do parâmetro de entrada destas funções, ela pode retornar alguns resultados diferentes do desejado, como quando o primeiro parâmetro passado for nulo. Neste caso, a função retorna nula, como se pode diagnosticar no exemplo abaixo:

SQL> select lpad(null,20,'x') from dual
2 /

L
-


Ou seja, eu pedi para função preencher até 20 caracteres com x, mas ela me retornou nula!!!

Portanto, como geralmente o parâmetro de entrada é usado com alguma coluna do banco, sugiro usar a função nvl para se certificar que a função, mesmo no caso da coluna ser nula, retorne o campo formatado conforme desejado. Observe:

SQL> select lpad(nvl(null,'a'),20,'x') from dual
2 /

LPAD(NVL(NULL,'A'),2
--------------------
xxxxxxxxxxxxxxxxxxxa


o mesmo vale para o parâmetro de complemento, como no exemplo abaixo:

SQL> select lpad('a',20,null) from dual
2 /

L
-


Ou seja, se o parâmetro for nulo, também a função irá retornar nulo...

Há, muito importante, se tiver que colocar um número x de zeros ou qualquer outro caracter repetido, não repita o valor, por favor, use lpad/rpad, observe:

SQL> select lpad('0',20,'0') from dual
2 /

LPAD('0',20,'0')
--------------------
00000000000000000000

Teste Unitário em geração de Arquivos

Vejo hoje em dia muitos desenvolvedores que apenas partem do pressuposto, que se a saida está de acordo com o documento de análise, então o programa está concluido. Só que eles não se atentam para o fato de que o analista é humano, e portanto, também está sujeito a cometer falhas no seu processo. Por isso se torna cada vez mais necessário, que o desenvolvedor entenda o que está fazendo, e qual o resultado esperado para o procedimento que ele está concluido.
Tomemos como exemplo uma geração de arquivo texto posicional, onde, com base no layout passado, o desenvolvedor faz o programa de extração dos dados da base, e posterior formatação para o arquivo texto. Após finalizado o desenvolvimento, o programador dá uma rápida conferida, e ai, qual o próximo passo: entregar o procedimento?
Se ele tiver um arquivo texto de exemplo no layout que desenvolveu, pode fazer uma rápida comparação entre a saida do seu programa e o exemplo passado, e com isto, detectar inúmeros problemas que só seriam vistos mais pra frente, como deslocamento de campos, alinhamento de colunas, preenchimentos das demais posições, falta de informações que ocasionaram deslocamento de campos. Ou seja, um simples teste, detecta inúmeros bugs e faz com que o desenvolvedor possa atingir uma qualidade muito maior no produto (software) que ele desenvolveu.

quinta-feira, 8 de outubro de 2009

Utilização de funções analiticas

Bom, hoje irei falar sobre uma função analitica que a um bom tempo não usava, mas que ontem me foi bem util. Trata-se da função over partition do Oracle, a qual pode ser conferida no exemplo abaixo:

Ela 'reinicializa' a contagem do campo seq de acordo com o parametro passado no partition by.

Aguardo sugestões para os próximos posts...