DATA no PostgreSQL – Brincando com funções

O tempo no PostgresApós brincar de inserir e exibir datas e de fazer um pouco de aritimética com datas, chegou a hora de trabalhar com problemas mais complexos. Se você tiver alguma sugestão para um 4º ou até 5º post, deixe um comentário aqui.

Nesse ponto, você já deveria se sentir mais à vontade trabalhando com datas no PostgreSQL. Lembre-se sempre de consultar o capítulo 8 – “Data Types” e o capítulo 9 – “Functions and Operators” para conhecer um pouco mais sobre os tipos de dados disponíveis e as funções existentes para lidar com eles. Para hoje vamos fazer uma brincadeira simples com datas: vamos pegar o primeiro e o último dia útil do mês atual. Antes de mais nada, RTFM…. vá dar uma lida no manual para conhecer as funções do Postgres. Quanto mais eu releio a documentação, mais eu aprendo.

Mas vamos lá. Para começar, vamos pegar o Mês atual, com a função CURRENT_DATE que serve para pegar a data atual. Se você quiser utilizar as função semelhante, que retorna um TIMESTAMP ao invés de um DATE, você terá outras preocupações, como saber em que momento exato será considerado como “data atual”. Por enquanto é tudo que precisamos saber.

Bom, e se quisermos saber qual é o primeiro dia do mês atual? OK, podemos marretar que todo mês começa no dia primeiro, mas vamos trabalhar com SQL um pouco e conhecer outra função que serve para “arredondar” datas, o DATE_TRUNC:

Simples não? Se fossemos arredondar um número, pensaríamos em qual casa decimal (dédimos, centésimos, milésimos) iríamos arredondar, como na função TRUNC. Com datas, podemos definir arredondar no ano, mês, dia, hora, minuto, segundo, etc.

Mas vamos começar a complicar um pouco… e se nós quisermos pegar o último dia do mẽs? Bom, temos meses com 30, 31, 28 e 29 dias. O postgres já conhece bem o calendário, portanto, o que podemos fazer (sei que tem outra forma de fazer isso) é pegar o primeiro dia do mês seguinte e subtrair um dia. Vejamos alguns exemplos:

Ótimo, agora temos o primeiro e o último dia do mês atual, correto? Agora vamos gerar uma tabela com todos os dias do mês usando a função GENERATE_SERIES:

Agora vamos fazer a mesma coisa, mas vamos substituir o primeiro e o último dia do mês pelas expressões que criamos antes:

Pronto… estamos quase lá agora. Vamos pegar apenas o primeiro e o último dia usando as funções de agregação MIN e MAX:

Sim, parece que estamos dando voltas, não? Mas veja, para pegar o primeiro e o último dia do ano, só falta para nós filtrar o sábado e o domingo. E isso é fácil se utilizarmos a função EXTRACT (similar a função DATE_PART) que extrai apenas uma parte de uma data. No caso, queremos saber de qual dia da semana ela é:

Aqui utilizei o ISODOW que é o dia da semana, onde a segunda-feira  é o dia 1 e o domingo é o dia 7, conforme o padrão ISO. Também aproveitei para extrair apenas o dia do mês na hora de exibir a informação, o que é absolutamente opcional. Você pode testar esta consulta para outros intervalos, substituindo o CURRENT_DATE por outras datas.

Como sugestão para uma aplicação real você pode criar uma tabela de feriados e filtrar as datas dos feriados também:

Você sofisticar mais o problema criando uma tabela que diferencie com feriados nacionais, estaduais e municipais. Mas isso fica para você pensar, se realmente tiver que lidar com este tipo de problema.

16 pensamentos em “DATA no PostgreSQL – Brincando com funções”

  1. Muito bom seu post, mas eu tenho uma dúvida. Como pegar todos os registros de uma tabela do mês passado sem informar a data?

    1. Luis, tente algo como:

      SELECT * FROM tabela_com_data
      WHERE campo_data BETWEEN
      date_trunc(‘month’,current_date) – INTERVAL’1 month’ AND
      date_trunc(‘month’,current_date)

      1. Telles, muito obrigado por ter respondido, a alguns minutos essa minha dúvida também foi respondia através de um fórum, mas foi exatamente a mesma solução.

        Muito obrigado novamente.

  2. Ola.

    eu tenho um banco no PostgreSQL o o nome da tabela clientes e a coluna chamada nasc.

    Gostaria de saber como faço para pegar por exemplo todos os registros com o mes 4. ja tentei de todas as formas e nao consigo.
    No SQL SERVER eu consegui assim :

    SELECT nasc FROM clientes WHERE month(nasc)= ’12’ ;

    porem no PostgreSQL ele não aceita esse comando, da o seguinte erro:

    ERRO: função month(character varying) não existe
    LINE 1: SELECT nasc FROM clientes WHERE MONTH(nasc)= ’04’
    ^
    HINT: Nenhuma função corresponde com o nome e os tipos de argumentos informados.

    1. Bom dia Telles,

      Poderia me da mais uma forcinha eu entendi o que você passou porem so nao estou conseguindo aplicar isso agora no banco de dados conforme abaixo pode me ajudar ?

      select nasc from clientes, date_part(‘month’,nasc)=’04’

      1. Veja que a função date_part não retorna um VARCHAR. Veja o exemplo:

        postgres=# select current_date, date_part(‘month’,current_date), date_part(‘month’,current_date) = 2 test;
        date | date_part | test
        ————+———–+——
        2015-12-17 | 12 | f
        (1 row)

        postgres=# select current_date, date_part(‘month’,current_date), date_part(‘month’,current_date) = 12 test;
        date | date_part | test
        ————+———–+——
        2015-12-17 | 12 | t
        (1 row)

        1. Entendi… ele so vai me dizer se é verdadeiro ou falso….

          Porque so pra vc entender eu tenho um sistema onde quando o cliente digitar por exemplo o mes 04 ele vai listar todos os que são do mes 04 e gerando true ou false ele nao vai mostrar depois num tabela quem são essas pessoas.

          1. Meu caro, você precisa olhar fora da caixa… toda condição no WHERE é uma checagem de verdadeiro ou falso. Eu só coloquei no SELECT para você ver o resultado. Dê uma olhada na documentação….

            O que você quer é algo realmente simples e rápido:

            SELECT nasc FROM clientes WHERE MONTH(nasc)= 04;

            É só retirar as aspas simples. Entendeu?

  3. Cara, tu manda aí as postagens sobre situações, e quando mostra como se faz, parece que é tudo fácil.
    Muito bom, parabéns, show demais.
    Adicionado como favorito!

  4. Show seu post Telles,

    me tira uma dúvida, eu preciso que na minha consulta SELECT em um banco , o campo data de minha tabela “2013-07-18 12:56:19.553” apareça sem os milessegundos, dessa forma “2013-07-18 12:56:19”. Qual seria a melhor forma?

      1. amigo saudações estou com uma pequena duvida como ficaria uma consulta onde a coluna vencimento grava apenas o dia do vencimento “10” a consulta resultaria em todos com 7 ou mais dias de vencidos ?
        desde já agradeço a atenção

        1. Vamos supor que a data de vencimento seja hoje. Então você quer pegar datas menores que a data de vencimento – 7 dias, que seria:

          teste=# SELECT current_date – ‘7 days’::interval v;
          v
          ———————
          2016-03-09 00:00:00

          Depois vamos pegar o número do dia e transformar numa data:
          teste=# SELECT * from d;
          venc
          ——
          3
          17
          15
          22
          28
          8
          1
          5
          (8 registros)

          teste=# SELECT (date_trunc(‘month’,current_date) + venc * ‘1 day’::interval)::date from d;
          date
          ————
          2016-03-04
          2016-03-18
          2016-03-16
          2016-03-23
          2016-03-29
          2016-03-09
          2016-03-02
          2016-03-06
          (8 registros)

          Agora é só juntar tudo:
          teste=# SELECT (date_trunc(‘month’,current_date) + venc * ‘1 day’::interval)::date
          FROM d
          WHERE (date_trunc(‘month’,current_date) + venc * ‘1 day’::interval)::date < current_date - '7 days'::interval; date ------------ 2016-03-04 2016-03-02 2016-03-06 (3 registros) Espero ter ajudado

Dúvidas, sugestões, críticas, comentários e cervejas são bem vindos!!!