quinta-feira, 9 de julho de 2009

A peça que o between pregou!

Boas pessoal! Começarei meu primeiro post falando de SQL!
Antes, uma observação: O SGBD onde o problema ocorreu e as queries foram testadas é PostgreSQL 8.3.

Fui procurado para tirar uma dúvida sobre uma query que utilizava between. Como sempre faço, separo as ações da seguinte forma:

O problema: “O intervalo de datas não está funcionando quando coloco 12/06/2009 a 10/07/2009”.

A estrutura:
ID......Data
1 30/06/2009 00:00
2 02/07/2009 09:00
3 10/07/2009 11:00
A primeira suspeita:
O erro mais comum é quando o campo armazena data/hora e comparamos apenas com data, conforme exemplo abaixo:
select id from tmp_post
where dt_post between to_date( '12/06/2009', 'dd/mm/yyyy') and to_date( '10/07/2009', 'dd/mm/yyyy');
O resultado disto seria o retorno de apenas 2 registros (30/06 e 02/07). Por que?
Porque 10/07/2009 11:00 é maior que 10/07/2009 00:00. Simples assim! Por pura falta de atenção, mas acontece.

Maaaas, para ajudar a me deixar mais careca, este problema já estava calçado, então, só restou a "apelação"....
E foi constatado, não é que estavam comparando varchar?
E quando vc usa varchar, "12/06/2009" é maior que "10/07/2009", sendo assim, nosso intervalo estava furado.
Para ilustrar, estava mais ou menos assim:
select * from tmp_post where to_char(dt_post, 'dd/mm/yyyy hh24:mi') between '12/06/2009' and '10/07/2009';
Isto não traz nenhum registro.

Mas porque '12/06/2009' é maior que '10/07/2009'?
Estamos usando estes dois valores como varchar(string);
Primeiro o SGBD "compara" pelo primeiro caracter - ambos têm 1;
Segundo o SGBD "compara" pelo segundo caracter - 2 é maior que 0; Bingo!

Problema detectado, um cascudo no programador e agora vamos resolver!
Basta comparar por datas e não mais por varchar, da seguinte forma:
select * from tmp_post
where dt_post between to_date('12/06/2009', 'dd/mm/yyyy' ) and to_date('10/07/2009', 'dd/mm/yyyy');
Opa, mas com isso eu passo a ter o problema número 1, de retornar apenas dois registros (30/06 e 02/07).
Para trabalhar com isto, existem dois caminhos:
1) Mude a forma como vc está passando o parâmetro, concatenando a última hora com o segundo parâmetro, ficando:
select * from tmp_post
where dt_post between to_date('12/06/2009', 'dd/mm/yyyy' ) and to_date('10/07/2009 23:59:59', 'dd/mm/yyyy hh24:mm:ss');

Retorno: 30/06, 02/07 e 10/07
2) Formate a data que está armazenada, desprezando a hora, mas dependendo da configuração do SGBD, vc precisará transformar em string com a data adequada(com to_char) e depois formata para data, já sem as horas (to_date):
select * from tmp_post
where to_date(to_char(dt_post,'dd/
mm/yyyy'),'dd/mm/yyyy') between to_date('12/06/2009', 'dd/mm/yyyy' ) and to_date('10/07/2009 23:59:59', 'dd/mm/yyyyhh24:mm:ss');

Retorno: 30/06, 02/07 e 10/07

Na opção 1, vc precisa concatenar o valor fixo '23:59:59';
Na opção 2, vc precisa utilizar duas funções do SGBD, to_char e depois to_date;

Como penso que o mais importante quando estiver trabalhando com datas é manter o controle do dado que vc está trabalhando, prefiro a segunda opção. Mesmo utilizando um pouco mais de recurso do SGBD.

Por enquanto é isso.

Abs. e até a próxima!
Ed

quarta-feira, 1 de julho de 2009

Enfim, criei um blog

Boas Pessoal!

Enfim, criei vergonha na cara e decidi fazer meu blog.

Pretendo abordar por aqui assuntos sobre programação em banco de dados (pgsql, PL/SQL, T-SQL), modelagem de dados, BD's no geral, além de relatar minhas aventuras no aprendizado de Ruby on Rails e por fim expressar minha opinião sobre qualquer coisa.

Usarei muito como armazém de idéias e coisas legais também.

Imagine a cena: no meio da madrugada, vc naquele desenvolvimento violento, precisando escrever uma query de 8º Dan usando uma função que vc fez a alguns meses atrás, os neurônios embebecidos de café já não ajudam muito, sua pen drive não está por perto, então, lá vamos nós camelar e fazer de novo. Maaaasss se tivesse no blog eu conseguiria acessar/lembrar e ir p/ casa ainda antes de amanhecer! :D

Abs.,
Ed