sqlpostgresqldatesinterval

AGE no SQL: diferenca entre datas como intervalo e idade em anos

A funcao AGE do PostgreSQL devolve a diferenca entre datas como anos, meses e dias, nao como dias brutos.

2 min de leituraReferencesql · postgresql · dates · interval · age

No PostgreSQL a funcao AGE responde "quanto tempo se passou" do jeito que uma pessoa entende: em anos, meses e dias, e nao como uma contagem bruta de dias. Por isso ela e a ferramenta natural para a idade de usuarios, o tempo de casa de funcionarios e o quao antigo e um pedido.

Dois argumentos contra um

AGE tem duas formas. Com dois argumentos ela calcula o intervalo entre dois momentos, e a ordem e AGE(end_ts, start_ts) — fim menos inicio.

SELECT AGE(TIMESTAMP '2024-03-01', TIMESTAMP '2021-11-15') AS gap;
-- 2 years 3 mons 14 days

Com um unico argumento o ponto de referencia passa a ser current_date de forma implicita (meia-noite de hoje), entao o resultado muda a cada dia:

SELECT AGE(TIMESTAMP '1990-06-17') AS since_birth;
-- evaluated against midnight today

Compare com a subtracao simples. O operador - sobre duas datas devolve apenas um numero de dias — util, mas nunca responde "quantos anos".

SELECT DATE '2024-03-01' - DATE '2021-11-15' AS raw_days;  -- 837

Idade de usuarios e antiguidade de pedidos

Vamos usar nossas tabelas. Para saber ha quanto tempo um usuario tem conta, passe created_at como argumento unico:

SELECT id, email, AGE(created_at) AS account_age
FROM users
ORDER BY created_at;

A antiguidade de um pedido funciona do mesmo jeito. E pratico filtrar pelo intervalo direto — o PostgreSQL compara um interval com um literal:

SELECT o.id, o.amount, AGE(o.created_at) AS order_age
FROM orders o
WHERE o.status = 'paid'
  AND AGE(o.created_at) > INTERVAL '90 days';

Para funcionarios AGE descreve o tempo de casa com clareza quando ha uma data de admissao (aqui created_at faz o papel de ponto de partida):

SELECT name, dept, AGE(NOW(), created_at) AS tenure
FROM employees
ORDER BY tenure DESC;

Por que os meses dependem do calendario

O traco principal do interval que AGE devolve: os meses sao contados pelo calendario, e nao como 30 dias fixos. O PostgreSQL avanca a data primeiro por anos inteiros, depois por meses inteiros, e so o resto e expresso em dias.

SELECT AGE(DATE '2024-03-31', DATE '2024-01-31') AS feb_gap;
-- 2 mons  (not 60 days, not 59)

Por causa disso, o mesmo periodo em dias pode dar um numero de meses diferente — fevereiro e mais curto que julho. Isso e correto para uma "idade", mas vira fonte de surpresas se voce esperava aritmetica em dias.

  • AGE sempre normaliza o resultado em anos, meses e dias.
  • Meses de calendario nao tem 30 dias — a duracao depende das datas reais.
  • Para uma contagem exata de dias use a subtracao date - date ou EXTRACT(EPOCH ...).

Extraindo anos com EXTRACT

O intervalo e impresso de forma legivel, mas para comparar e agrupar voce precisa de um numero. EXTRACT retira um campo do intervalo:

SELECT id, email,
       EXTRACT(YEAR FROM AGE(created_at))::int AS full_years
FROM users;

Pegadinha: EXTRACT(YEAR FROM AGE(...)) devolve apenas os anos do intervalo e descarta os meses. Para um usuario com "2 anos e 11 meses" voce obtem 2, e nao um 3 arredondado. Se voce quer anos completos, e exatamente isso; se quer o total de meses, calcule years * 12 + months.

SELECT id,
       EXTRACT(YEAR  FROM AGE(created_at)) * 12
     + EXTRACT(MONTH FROM AGE(created_at)) AS total_months
FROM users;

Diferencas em outros bancos

AGE e uma extensao do PostgreSQL sem equivalente direto no padrao.

  • MySQL: use TIMESTAMPDIFF(YEAR, start, end) para anos inteiros ou DATEDIFF para dias. Nao ha um intervalo anos-meses-dias pronto.
  • ClickHouse: oferece age('year', start, end) e dateDiff('day', ...); voce sempre informa a unidade de forma explicita, e tambem nao ha intervalo simbolico.

Se o codigo precisa ser portavel, calcule os anos com TIMESTAMPDIFF/dateDiff, e construa o intervalo legivel apenas no PostgreSQL.

Pratique com exercícios reais

Resolva exercícios no treinador de SQL com correção instantânea e dicas.

Abrir o treinador