Translate

quarta-feira, 13 de novembro de 2013

Postgresql - diferença entre datas

Neste post vamos apresentar 4 exemplos de como calcular a diferença entre datas.

Caso tenha interesse, faça o download ou veja o script no Github.

Para calcular a diferença entre duas datas, em dias, no postgresql utilizamos o operador aritmético menos "-".
data_final - data_inicial

Para calcular a diferença entre duas datas, em dias, meses e anos utilizamos a função age.

age(data_final, data_inicial)

1º Exemplo

Cenário: em uma imobiliária, um funcionário precisa fazer um levantamento de quantos dias são necessários para alugar os imóveis. Para fazer este cálculo vamos utilizar a tabela "tb_locacao". Esta tabela possui três colunas:
  • imovel_id: número de identificação do imóvel;
  • data_oferta: data de início da oferta do imóvel;
  • data_locacao: a data em que o imóvel foi alugado;
Veja a imagem da tabela "tb_locacao":


Solução: temos que calcular a diferença entre as colunas "data_locação" e "data_oferta":

Para calcularmos a diferença em dias executamos a sentença abaixo:

SELECT
imovel_id,
data_oferta,
data_locacao,
/*Calculo da diferença entre a data da locação e a data de oferta*/
data_locacao - data_oferta AS quantidade_dias
FROM tb_locacao;

Após executarmos a sentença acima, teremos o resultado exibido na tabela abaixo:


Caso seja necessário incluir o primeiro dia na contagem da diferença, subtraia o intervalo de 1 dia da coluna "data_oferta" (data inicial) . Veja a sentença abaixo:

SELECT
imovel_id,
data_oferta,
data_locacao,
data_locacao - data_oferta AS quantidade_dias,
/*Calculo da diferença entre a data da locação e a data de oferta, incluindo na contagem o primeiro dia*/
data_locacao - (data_oferta  -  INTERVAL '1 DAY')  :: DATE  AS dif_dias_primeiro_dia
FROM tb_locacao;

Após executarmos a sentença acima, teremos o resultado exibido na tabela abaixo:


Observação: perceba que quando subtraímos datas o primeiro dia não é considerado, se precisarmos considerar o 1º dia devemos descontar um dia data inicial utilizando "INTERVAL".  

2º Exemplo

Cenário: exibir o período necessário para alugar os imóveis (idêntico ao exemplo anterior), mas no seguinte formato.
x anos y meses z dias 


Para calcularmos a diferença no formato acima, executamos a sentença a seguir:

SELECT
imovel_id,
data_oferta,
data_locacao,
/*Calculo da diferença entre a data da locação e a data de oferta*/
AGE(data_locacao, data_oferta) AS intervalo
FROM tb_locacao;


Após executar a sentença acima é exibido o seguinte resultado:




O intervalo é exibido em inglês. Para exibir o formato em português utilizamos a função to_char para formatar o resultado da função age. Para conhecer mais sobre a função to_char, leia o artigo PostgreSql - Formatar data

TO_CHAR(valor, formato)

Veja abaixo alguns formatos:

FORMATOS QUE PODEM SER UTILIZADOS NA FUNÇÃO TO_CHAR
Formato Descrição
YYYY ano
YY 2 últimos digitos do ano
MM Mês de 01 à 12
DD Dia  de 01 à 31
HH Hora do dia de 01 à 12
HH12 Hora do dia de 01 à 12
HH24 Hora do dia de 01 à 24
MI Minuto do dia de 01 à 59
SS Segundo do dia de 01 à 59
MS Milissegundo do dia de 000 à 999
US Micro-segundo do dia de 000000 à 999999
MONTH Nome do mês em maiúsculo. Exemplo: JANEIRO
month Nome do mês em minúsculo. Exemplo: janeiro
Month Nome do mês com a primeira letra em maiúsculo. Ex.: Janeiro
DAY Nome do dia em maiúsculo. Ex.: SEGUNDA
day Nome do dia em minúsculo. Ex.: segunda
Day Nome do dia com a primeira letra em maiúsculo. Ex.: Segunda
D Dia da semana de 1 à 7. Domingo = 1 e Sábado = 7.
DDD Dia do ano de 1 à 365 ou 366 (bissexto).
WW Dia da semana de 1 à 53.

Consulte outros formatos no manual do postgresql.

A sentença abaixo formatara o intervalo em dia, mês e ano.

SELECT
imovel_id,
data_oferta,
data_locacao,
/*Calculo da diferença entre a data da locação e a data de oferta*/
TO_CHAR ( AGE(data_locacao, data_oferta) , 'YY "ano(s)" MM "mes(es)" DD "dia(s)" ' ) AS intervalo
FROM tb_locacao;

Perceba que "ano(s)", "mes(es)" e "dias" estão entre aspas duplas para que a função to_char não os converta e ocorra um erro.

Após a execução da sentença, será exibido o seguinte resultado:



3º Exemplo

Cenário: uma loja virtual vai oferecer uma oferta especial, aos clientes que estão cadastrados a mais de 90 dias no seu site. Para fazer este cálculo vamos utilizar a tabela "tb_cadastro". Esta tabela possui duas colunas:

  • cliente_id: número de identificação do cliente;
  • data_cadastro: data de cadastro do cliente;
Veja a imagem da tabela "tb_cadastro":


Solução: temos que calcular a diferença entre o dia atual (hoje) e a coluna "data_cadastro". Antes de efetuarmos o cálculo devemos saber como retornar o dia atual.
Para retorna o dia atual utilizamos a função:

CURRENT_DATE

Para exibir o dia atual execute a sentença abaixo (quando executei esta sentença o dia era 04/03/2014):

SELECT CURRENT_DATE;


Agora que já conhecemos a função que calcula o dia atual (hoje), vamos executar a sentença para verificar os clientes que estão cadastrados a mais de 90 dias.

SELECT 
cliente_id,
data_cadastro,
/*calcula a diferença de dias*/
CURRENT_DATE  - data_cadastro AS quantidade_dias
FROM tb_cadastro
/*filtra somente os clientes que são cadastrados há mais que 90 dias*/
WHERE CURRENT_DATE  - data_cadastro > 90;

Após executarmos a sentença acima, filtramos os clientes que estão cadastrados a mais de 90 dias. Veja a imagem na tabela abaixo:



4º Exemplo

Cenário: uma fábrica quer saber quais os equipamentos ficaram por um período maior igual a 80 horas em manutenção. Vamos utilizar a tabela "tb_manutencao" para fazer esta consulta, veja a imagem abaixo:


Esta tabela possui 3 colunas:
  • equipamento_id: número de identificação de equipamento;
  • data_inicio: data de inicial de manutenção do equipamento;
  • data_final: data final de manutenção do equipamento;

Após executarmos a sentença abaixo, filtramos os equipamentos que ficaram em manutenção por 80 ou mais horas.

SELECT
equipamento_id,
data_inicio,
data_final,
AGE(data_final, data_inicio)
FROM tb_manutencao
WHERE AGE(data_final, data_inicio) >= '80 hour'

Veja o resultado na imagem abaixo:

Repare que o intervalo é retornado em dias e horas. 

1 day = 24 horas

Para comprovar que os intervalos do primeiro e segundo registros tem um período igual ou superior a 80 horas, vamos efetuar o calculo:

Primeiro registro:
3 days 16:51:54 = 3* 24 h + 16:51:54 h = 88:51:54

Segundo registro:
3 days 13:03:55 = 3* 24 h + 13:03:55 h = 85:03:55 
Em breve postarei mais exemplos de funções com data.

13 comentários:

  1. Este comentário foi removido por um administrador do blog.

    ResponderExcluir
  2. Exemplo para o leitor que queria incluir o primeiro dia na contagem da diferença

    1º EXEMPLO - DIFERENÇA ENTRE 01/01/2015 a 31/01/2015 = diferença de 31 dias.

    SELECT DATE '2015-01-31' - (DATE '2015-01-01' - INTERVAL '1 DAY') AS DIFERENCA_DATA

    Retorna '31 days' , mas o tipo de dado é INTERVAL, caso queira retornar um valor inteiro execute a sentença do 2º exemplo.

    2º EXEMPLO - DIFERENÇA DE DATA CONSIDERANDO O PRIMEIRO DIA E RETORNANDO INTEIRO

    SELECT DATE '2015-01-31' - DATE (DATE '2015-01-01' - INTERVAL '1 DAY') DIFERENCA_DATA_CONSIRANDO_PRIMEIRO_DIA

    ResponderExcluir
  3. Kelly, de coração.. muito obrigado mesmo por este post

    ResponderExcluir
  4. Obrigado por me ajudar, se não fosse por você estaria em apuros.

    ResponderExcluir
  5. Bom dia, gostaria de saber como que fica pra mostrar todos os dias como colunas quando eu passar um período na consulta.
    Exemplo:
    Preciso fazer uma reserva e quero ver a disponibilidade dos quartos, dai eu entro com a dataInicial e dataFinal e na consulta mostra como colunas os dias que percorrem entre esse periodo e me diga qual quarto está reservado e qual disponivel. É possível isso ?

    Desde já meu muito obrigado.

    ResponderExcluir
  6. 3 days 16:51:54 = 3* 24 h + 16:51:54 h = 88:51:54, como faço para deixar no formato em Horas?

    ResponderExcluir
  7. Algum exemplo de como calcular a diferença de dias entre linha atual e data da linha anterior ?

    ResponderExcluir
    Respostas
    1. Boa tarde, segue o exemplo de como calcular a diferença de dias entre a data da linha atual e data da linha anterior.
      -No 1º Exemplo: o campo data é do tipo date.
      -No 2º Exemplo: o campo data_hora do tipo timestamp, ou seja, data e hora.
      Talvez um deles te ajude, segue o exemplo.


      /**
      *1º Exemplo
      */

      /**
      *Cria a tabela de venda tb_vendas_ex01
      */

      CREATE TABLE tb_vendas_ex01
      (
      cliente_id integer,
      data date
      );

      /**
      *Insere os registros na tabela tb_vendas_ex01
      */
      INSERT INTO tb_vendas_ex01 VALUES
      (540, '2019-04-09'),
      (541, '2020-06-10'),
      (541, '2020-06-05'),
      (542, '2020-08-05'),
      (543, '2020-08-07'),
      (544, '2020-08-14'),
      (545, '2020-08-21'),
      (546, '2020-09-07'),
      (547, '2020-09-10'),
      (548, '2020-09-24'),
      (549, '2020-10-15'),
      (550, '2020-10-20'),
      (541, '2020-10-25');


      /**
      *Visualiza os registros na tabela tb_vendas_ex01
      */
      SELECT * FROM tb_vendas_ex01;

      /**
      *Calcula o intervalo entre as vendas
      */

      SELECT
      cliente_id,
      data,
      data - LAG(data) OVER (ORDER BY data) AS intervalo_em_dias_tipo_inteiro,
      AGE
      (
      data, LAG(data) OVER (ORDER BY data)
      ) AS intervalo_tipo_interval_yy_mm_dd_hh_mm_ss,
      TO_CHAR
      (
      AGE
      (
      data, LAG(data) OVER (ORDER BY data)
      ),
      'YY "ano(s)" MM "mes(es)" DD "dia(s)"'
      ) AS intervalo_format_yy_mm_dd_hh_mm_ss
      FROM tb_vendas_ex01;




      /**
      *2º Exemplo
      */

      /**
      *Cria a tabela de venda tb_vendas_ex02
      */

      CREATE TABLE tb_vendas_ex02
      (
      cliente_id integer,
      data_hora timestamp without time zone
      );

      /**
      *Insere os registros na tabela tb_vendas_ex02
      */
      INSERT INTO tb_vendas_ex02 VALUES
      (540, '2019-04-20 11:32:01'),
      (541, '2020-05-23 11:32:01'),
      (541, '2020-08-05 12:31:01'),
      (542, '2020-08-06 08:20:01'),
      (543, '2020-08-07 23:36:45'),
      (544, '2020-08-07 23:47:13'),
      (545, '2020-08-07 23:58:17'),
      (546, '2020-08-08 08:05:19'),
      (547, '2020-08-08 08:15:27'),
      (548, '2020-08-08 09:50:08'),
      (549, '2020-08-09 01:00:06'),
      (550, '2020-08-09 21:00:05'),
      (541, '2020-08-10 15:31:01');


      /**
      *Visualiza os registros na tabela tb_vendas_ex02
      */
      SELECT * FROM tb_vendas_ex02;

      /**
      *Calcula o intervalo entre as vendas
      */

      SELECT
      cliente_id,
      data_hora,
      data_hora - LAG(data_hora) OVER (ORDER BY data_hora) AS intervalo_dd_hh_mm_ss,
      EXTRACT( day FROM data_hora - LAG(data_hora) OVER (ORDER BY data_hora)) AS extrair_so_qtd_dias,
      AGE
      (
      data_hora, LAG(data_hora) OVER (ORDER BY data_hora)
      ) AS intervalo_yy_mm_dd_hh_mm_ss,
      TO_CHAR
      (
      AGE
      (
      data_hora, LAG(data_hora) OVER (ORDER BY data_hora)
      ),
      'YY "ano(s)" MM "mes(es)" DD "dia(s)" HH24 "HORA(S)" MI "MINUTO(S)" SS "SEGUNDOS(S)"'
      ) AS intervalo_format_yy_mm_dd_hh_mm_ss
      FROM tb_vendas_ex02;

      Excluir
  8. Eu tenho uma duvida, supomos que tenho vários registro de inicio e fim de conexões, por exemplo:

    ID data_inicio data_fim
    1;"2020-11-03"; "2020-11-07"
    2;"2020-11-03"; "2020-11-07"
    3;"2020-10-26"; "2020-11-07"
    4;"2020-10-26"; "2020-11-07"
    5;"2020-11-07"; "2020-11-07"
    6;"2020-11-06"; "2020-11-07"

    No primeiro registro, a conexao ficou ativa entre 03/11 e 07/11,
    logo os dias 4, 5 e 6 ela estava ativa tambem.
    Ai eu queria saber, como saber quantas conexoes estavam ativas em cada dia do mes? fazendo isso usando somente select.


    ResponderExcluir