7 de ago. de 2013

Filtrando dados de acordo com um intervalo de horas

O sistema da empresa na qual trabalho hoje tem uma tela de filtro de logs. Essa tela filtrava os logs apenas por data e tivemos a necessidade de acrescentar filtros para um intervalo de horas específico do dia. Queríamos, por exemplo, visualizar os logs entre 9:30hs e 10:15hs da manhã, de segunda a sexta.

Como a linguagem SQL não tras nenhuma facilidade para esse tipo de filtro, tivemos que colocar a caixola para funcionar. A solução é bem simples.

Meu campo que guarda a informação sobre o inicio do log é o campo INICIO_LOG. O primeiro passo é calcular o valor em minutos desse campo desde a zero-hora e criar um campo virtual na sua tabela resultado contendo esse valor em minutos. Em SQL-Server para calcular o valor em minutos de um campo data, desde a zero-hora, basta fazer:
DatePart(Hour, INICIO_LOG) * 60 + DatePart(Minute, INICIO_LOG) 
A partir daí, basta incluirmos esse campo virtual em nossa tabela resultado, bastando para isso usar um select na cláusula From:
select *
from (select DatePart(Hour, INICIO_LOG) * 60 + DatePart(Minute, INICIO_LOG) MINUTOS, * from LOGS) Logs
Agora temos uma tabela com o novo campo "MINUTOS".

Se eu quero visualizar os Logs entre 9:30hs e 10:15hs da manhã, preciso calcular quantos minutos eu tenho, desde a zero-hora, no 1o. e no 2o. parâmentros. Para 9:00hs temos 9 * 60 + 30 = 570 minutos e para 10:00hs temos 10 * 60 + 15 = 615 minutos. Se um registro de Log foi adicionado as 09:45hs da manhã, temos 9 * 60 + 45 = 585 minutos, cujo valor estará entre nosso 1o. parâmetro (570 minutos) e nosso 2o. parâmetro (615 minutos).

Nossa sentença SQL com os parâmetros fica da seguinte forma:
select *
from (select DatePart(Hour, INICIO_LOG) * 60 + DatePart(Minute, INICIO_LOG) MINUTOS, * from LOGS) Logs
where
  MINUTOS Between :MINUTO_INICIO and :MINUTO_FIM and
  INICIO_LOG Between :DATA_INICIO and :DATA_FIM
No código da aplicação usamos algum componente do tipo TDateTimePicker ou o que você quiser para solicitar os intervalos ao

usuário e calculamos o valor em minutos usando uma função:
function TVisualizaLogs.CalcMinutos(aDate: TDateTime): Integer;
var
  H, M, S, MS: Word;
begin
  DecodeTime(aDate, H, M, S, MS);
  Result := H * 60 + M;
end;
Passamos os parâmetros para nosso componente de acesso a dados da forma usual e o resto você já sabe.
ParamByName('MINUTO_INICIO').AsInteger := CalcMinutos(dtpInicio.Date);
ParamByName('MINUTO_FIM').AsInteger := CalcMinutos(dtpFim.Date);
ParamByName('DATA_INICIO').AsInteger := dtpInicio.Date;
ParamByName('DATA_FIM').AsInteger := dtpFim.Date;

Bom proveito.