Como calcular a idade de uma pessoa no MySQL

Você tem uma ou várias datas de nascimento armazenadas em um banco de dados MySQL e gostaria de calcular as idades de cada pessoa, em relação à data de hoje.
O problema pode ser solucionado computando o intervalo entre as datas — sendo que a mesma abordagem pode ser usada para resolver inúmeras outras situações.
mysql logo over gray ubuntu wallpaper
Determinar a idade envolve calcular o intervalo, decorrido em anos, entre duas datas — mas você não pode fazer isto apenas dividindo a quantidade de dias decorridos por 365, sob pena de obter um resultado inexato — uma vez que este método não inclui os anos bissextos.
Algumas metodologias fazem a divisão por 365,25, o que ajuda a obter resultados menos inexatos.
Neste texto, vou abordar alguns métodos mais precisos para calcular a idade em anos e meses.

Como determinar uma idade em anos

Como já foi dito, não dá para efetuar a operação, contando o número de dias percorridos entre a data de nascimento e a data alvo, uma vez que anos diferentes podem ter uma quantidade diferente de dias.
Uma outra possibilidade é deduzir o ano de nascimento do ano atual. Veja:

SELECT YEAR('2015-06-22')-YEAR('1913-10-19')
    ->  AS "Idade de Vinícius de Moraes"\G
*************************** 1. row ***************************
Idade de Vinícius de Moraes: 102
1 row in set (0.00 sec)

O problema desta abordagem, é que em Junho de 2015, o poeta Vinícius de Moraes ainda teria 101 anos — uma vez que seu mês de nascimento é Outubro.
Ao lidar com clientes, sabemos que algumas pessoas se incomodam quando lhes atribuímos uma idade acima da que realmente têm — por isto é que precisamos determinar suas idades de maneira mais precisa.
Veja outro exemplo (mais complexo) da aplicação deste método.

SELECT NomeCliente AS Cliente,
    ->  DATE_FORMAT(DataNascimentoCliente,'%d de %M de %Y') AS "Data nasc.",
    ->  YEAR(CURDATE()) - YEAR(DataNascimentoCliente) AS Idade
    ->  FROM CadastroClientes
    ->  WHERE YEAR(DataNascimentoCliente) > 1983;

Em destaque, as idades que estão “adiantadas” em relação 22 de Junho de 2015:

+-------------------+-------------------------+-------+
| Cliente           | Data nasc.              | Idade |
+-------------------+-------------------------+-------+
| Christen Wiggins  | 11 de abril de 1984     |    31 |
| Justine Howard    | 22 de dezembro de 1984  |    31 |
| Anika Hammond     | 01 de agosto de 1985    |    30 |
| Alea Vang         | 09 de julho de 1984     |    31 |
| Fredericka Jensen | 29 de fevereiro de 1984 |    31 |
| Alexa Duncan      | 09 de abril de 1985     |    30 |
| Lynn Mclean       | 11 de novembro de 1984  |    31 |
| Allegra Cantrell  | 19 de março de 1984     |    31 |
+-------------------+-------------------------+-------+
8 rows in set (0.00 sec)

Exemplos de uso da função TIMESTAMPDIFF, para calcular a idade exata

Para evitar qualquer desconforto, criado pela falta de exatidão, use a função TIMESTAMPDIFF() — com ela, é possível obter a idade exata de uma pessoa.
Veja um exemplo de uso:

SELECT TIMESTAMPDIFF (YEAR,'1913-10-19',CURDATE())
    ->  AS "Idade de Vinícius de Moraes";

Se a data atual for 22 de Junho de 2015, o resultado para a idade de Vinícius de Moraes é:

+------------------------------+
| Idade de Vinícius de Moraes  |
+------------------------------+
|                          101 |
+------------------------------+
1 row in set (0.00 sec)

A lista de aniversariantes, anterior, também muda (para melhor) com esta abordagem:

SELECT NomeCliente AS Nome,
    ->  DATE_FORMAT(DataNascimentoCliente, '%d de %M de %Y') AS Nascimento,
    ->  TIMESTAMPDIFF(YEAR,DataNascimentoCliente,CURDATE()) AS Idade
    ->  FROM CadastroClientes
    ->  WHERE YEAR(DataNascimentoCliente) > 1983;
+-------------------+-------------------------+-------+
| Nome              | Nascimento              | Idade |
+-------------------+-------------------------+-------+
| Christen Wiggins  | 11 de abril de 1984     |    31 |
| Justine Howard    | 22 de dezembro de 1984  |    30 |
| Anika Hammond     | 01 de agosto de 1985    |    29 |
| Alea Vang         | 09 de julho de 1984     |    30 |
| Fredericka Jensen | 29 de fevereiro de 1984 |    31 |
| Alexa Duncan      | 09 de abril de 1985     |    30 |
| Lynn Mclean       | 11 de novembro de 1984  |    30 |
| Allegra Cantrell  | 19 de março de 1984     |    31 |
+-------------------+-------------------------+-------+
8 rows in set (0.00 sec)

Como obter a idade em meses ou dias

A função TIMESTAMPDIFF() permite estabelecer o resultado em MESES ou DIAS, de maneira simples.
Vejamos quantos meses de idade a cantora Pitty tem:

SELECT TIMESTAMPDIFF (MONTH,'1977-10-07',CURDATE()) AS "Idade da Pitty"\G
*************************** 1. row ***************************
Idade da Pitty: 452
1 row in set (0.00 sec)

Ou em dias:

SELECT TIMESTAMPDIFF (DAY,'1977-10-07',CURDATE()) AS "Idade da Pitty"\G
*************************** 1. row ***************************
Idade da Pitty: 13772
1 row in set (0.00 sec)

Este método pode ser usado em várias outras situações, como estabelecer há quanto tempo um documento está vencido, calcular dívidas etc.
Referência: http://dev.mysql.com/doc/refman/5.7/en/date-calculations.html

Como calcular intervalos de tempo decorrido entre dois horários no MySQL

Se você tem dois valores temporais, um horário de saída e outro de chegada, e deseja saber qual foi o tempo decorrido entre os dois, use a função TIME_TO_SEC() e sua contraparte SEC_TO_TIME().
Ambas, presentes no MySQL (desde a versão 3.22), oferecem uma forma (entre outras) de fazer cálculos envolvendo valores temporais, como horários e datas inteiras.
Você pode ver outros exemplos de uso destas funções aqui.
Se você quer adicionar 3 horas a um horário 15:35:00, não é possível fazer isto apenas somando 15:35:00 + 3 — são unidades diferentes, afinal.

Captura de tela: MySQL calculos envolvendo horas no banco de dados.
Clique para ampliar.

É aí que entra a função TIME_TO_SEC() — ela converte os valores para segundos.
Quando todas as grandezas envolvidas na operação estão usando a mesma unidade, é possível realizar qualquer conta.

SET @HoraSaida='15:20:00';
SET @HoraChegada='17:20:00';
SELECT @HoraSaida, @HoraChegada, TIME_TO_SEC(@HoraChegada) - TIME_TO_SEC(@HoraSaida) AS "Tempo decorrido em segundos";
_
+------------+--------------+-----------------------------+
| @HoraSaida | @HoraChegada | Tempo decorrido em segundos |
+------------+--------------+-----------------------------+
| 15:20:00   | 17:20:00     |                        7200 |
+------------+--------------+-----------------------------+
1 row in set (0.00 sec)

Ao terminar de realizar as operações aritméticas, você converte de volta, usando SEC_TO_TIME() — e voilá (desculpe o clichê) você tem um resultado apresentável, no formato de horário.

SELECT @HoraSaida, @HoraChegada,  SEC_TO_TIME(TIME_TO_SEC(@HoraChegada) - TIME_TO_SEC(@HoraSaida)) AS "Tempo decorrido em horas";
_
+------------+--------------+--------------------------+
| @HoraSaida | @HoraChegada | Tempo decorrido em horas |
+------------+--------------+--------------------------+
| 15:20:00   | 17:20:00     | 02:00:00                 |
+------------+--------------+--------------------------+
1 row in set (0.00 sec)

Note que intervalos de tempo decorrido podem ser negativos — para isto basta que o primeiro valor de horário seja posterior ao segundo. Ou seja, se você saiu de casa às 7:00:00, pro trabalho, e chegou às 6:45:00, você terá gasto -15 minutos de deslocamento — uma verdadeira viagem no tempo.

Como adicionar mais tempo a outra variável de tempo no MySQL

Você já tem uma variável ou um campo com um valor de tempo, definido em horas, minutos e segundos. O que você quer é adicionar valores a esta variável temporal — alguns segundos, minutos, horas ou dias a mais.
Uma das formas de resolver este problema e usar as funções de conversão do MySQL: TIME_TO_SEC() e SEC_TO_TIME().
Nesta abordagem, vamos usar TIME_TO_SEC() para ter certeza de que todos os valores envolvidos estão em segundos. Então, fazemos as operações aritméticas, cujos resultados serão apresentados em unidades de segundos.
Enfim, vamos usar a função SEC_TO_TIME() para converter de volta a valores temporais (de hora).
Tomemos como exemplo que 3 horas são equivalentes a (3*60*60) 10800 segundos — cada hora, possui 3600 segundos.
Ao adicionar mais uma hora (3600 seg.) a este valor, temos 14400 segundos.
Para transformar o valor resultante em um horário legível, usamos a função SEC_TO_TIME().
No exemplo prático, abaixo, vou mostrar como somar 3 horas ao horário original, constante nos campos de uma tabela>

SELECT TIME(DataCadastroCliente) AS "Horário original",
    -> SEC_TO_TIME(TIME_TO_SEC(DataCadastroCliente)+10800) as "Horário +3 horas"
    -> FROM CadastroClientes LIMIT 5;
+-------------------+-------------------+
| Horário original  | Horário +3 horas  |
+-------------------+-------------------+
| 10:03:39          | 13:03:39          |
| 21:44:49          | 24:44:49          |
| 11:58:30          | 14:58:30          |
| 20:52:29          | 23:52:29          |
| 09:43:57          | 12:43:57          |
+-------------------+-------------------+
5 rows in set (0.00 sec)

Como somar dois valores expressos em horas no MySQL

Se você tem dois valores temporais expressos em horas, é necessário converter ambos para segundos, antes de somá-los.
Veja um exemplo:

SELECT HoraChegada, TempoPerm
    -> SEC_TO_TIME(TIME_TO_SEC(HoraChegada) + TIME_TO_SEC(TempoPerm)) AS 'Hora + Tempo'
    -> FROM CheckIn;
+-------------+-----------+--------------+
| HoraChegada | TempoPerm | Hora + Tempo |
+-------------+-----------+--------------+
|    15:00:00 |  15:00:00 |     30:00:00 |
|    05:01:30 |  02:30:20 |     07:31:50 |
|    12:30:20 |  17:30:45 |     30:01:05 |
+-------------+-----------+--------------+

Note que valores temporais no MySQL (MySQL TIME values) não representam os horários no dia. Na verdade, estes valores se referem a tempo decorrido — por isto não zeram após as 24 horas.
Use um operador de módulo para chegar ao resultado desejado, neste caso:

SELECT HoraChegada, TempoPerm
    -> SEC_TO_TIME(MOD(TIME_TO_SEC(HoraChegada) + TIME_TO_SEC(TempoPerm), 86400) AS 'Hora + Tempo'
    -> FROM CheckIn;

Note que o valor 86400, usado acima, é o número de segundos contido dentro das 24 horas de um dia. No meu caso, esta query vai retornar o seguinte resultado:

+-------------+-----------+--------------+
| HoraChegada | TempoPerm | Hora + Tempo |
+-------------+-----------+--------------+
|    15:00:00 |  15:00:00 |     06:00:00 |
|    05:01:30 |  02:30:20 |     07:31:50 |
|    12:30:20 |  17:30:45 |     06:01:05 |
+-------------+-----------+--------------+

No MySQL, os valores TIME se delimitam entre -838:59:59 e 838:59:59 — o que corresponde a -3020399 a
3020399 segundos.
Se você tentar armazenar valores fora desta faixa, o MySQL vai simplesmente cortar o que estiver ultrapassando os limites do permitido.

Como decompor dados de data e hora, com o uso de funções de formatação, no MySQL

Obter informações de data ou hora, armazenadas no banco de dados é fácil. Se você quiser obter apenas parte da informação, o MySQL dispõe das funções DATE_FORMAT() e TIME_FORMAT() — ambas flexíveis o suficiente para retornar a informação na medida e no formato desejado.
Embora você disponha de funções (até mais) avançadas para isto, em linguagens como PHP, Python, Perl etc. — usar as funções do MySQL pode trazer ganhos de desempenho significativos para todo o sistema.
O assunto formatação de dados de data e hora é abordado em vários outros artigos, neste site, cuja leitura poderá complementar este post.
As funções DATE_FORMAT() e TIME_FORMAT() usam especificadores, para retirar da string que contém a informação de data ou hora a formatação ou substring desejada.
Como sempre, a melhor forma de entender, é através de exemplos. Observe a declaração abaixo e seu resultado:

SELECT DataCadastroCliente AS "Informação original",
    -> DATE_FORMAT(DataCadastroCliente, '%Y') AS "Ano",
    -> DATE_FORMAT(DataCadastroCliente, '%M') AS "Mês",
    -> DATE_FORMAT(DataCadastroCliente, '%d') AS "Dia"
    -> FROM CadastroClientes LIMIT 1;
+-----------------------+------+--------+------+
| Informação original   | Ano  | Mês    | Dia  |
+-----------------------+------+--------+------+
| 2016-03-29 10:03:39   | 2016 | março  | 29   |
+-----------------------+------+--------+------+
1 row in set (0.00 sec)

Para ver os nomes dos meses no idioma português, você precisa ajustar a variável de ambiente ‘lc_time_names’. Leia Como configurar o MySQL para exibir datas em português, para saber como fazer isto.
No próximo exemplo, vamos usar ver alguns resultados possíveis para a função TIME_FORMAT() do MySQL:

SELECT DataCadastroCliente AS "Informação original",
    -> TIME_FORMAT(DataCadastroCliente, '%H') AS "Hora",
    -> TIME_FORMAT(DataCadastroCliente, '%i') AS "Minutos"
    -> FROM CadastroClientes LIMIT 1;
+-----------------------+------+---------+
| Informação original   | Hora | Minutos |
+-----------------------+------+---------+
| 2016-03-29 10:03:39   | 10   | 03      |
+-----------------------+------+---------+
1 row in set (0.00 sec)

As letras, dentro das declarações, acompanhadas de aspas simples e de um sinal ‘%’ são chamadas especificadores e sua função é especificar exatamente que parte da string, que contém data e hora, há de ser exibida e de que forma a informação deverá ser mostrada.

Você pode encontrar uma tabela completa de especificadores possíveis no MySQL, no texto Como formatar a exibição das datas e das horas no MySQL.
Já que o MySQL armazena suas datas em um formato diferente daquele que usamos no dia a dia, os especificadores podem ser usados para “humanizar” e aproximar mais a formatação para o seu cliente final, tal como no exemplo que segue:

SELECT CidadeCliente AS "Cidade de origem",
    -> TIME_FORMAT(DataCadastroCliente, '%H horas e %i minutos') AS "Momento da chegada"
    -> FROM CadastroClientes LIMIT 3;
+------------------+-----------------------+
| Cidade de origem | Momento da chegada    |
+------------------+-----------------------+
| Tarbes           | 10 horas e 03 minutos |
| Solingen         | 21 horas e 44 minutos |
| Pizzoferrato     | 11 horas e 58 minutos |
+------------------+-----------------------+
3 rows in set (0.00 sec)

Como formatar a exibição de datas e horas no MySQL.

Você pode exibir as datas armazenadas no MySQL de inúmeras formas, limitadas apenas pela sua imaginação. Tanto é possível usar um formato padrão, como 1997-19-01 (CCYY-MM-DD), quanto um formato mais “literário”, como “dezenove de Janeiro de mil novecentos e noventa e nove”.
Para obter o efeito desejado, você pode usar funções do próprio MySQL ou de alguma linguagem de programação, usada para aceder ao banco de dados.
Como já foi dito, no artigo Formatos de data e hora no MySQL, o banco de dados segue o padrão ISO 8601 para armazenar os valores referentes a datas. Você não pode mudar isto.
Mas é fácil pegar esta informação e exibi-la da maneira que você achar melhor.
Para realizar a tarefa, use as funções do MySQL DATE_FORMAT() ou TIME_FORMAT().

Como usar as funções de formatação de data e hora no MySQL

Como explicado no artigo Formatos de data e hora no MySQL, ele usa o padrão ISO para armazenar este tipo de dados e não é possível alterar isto.
A função DATE_FORMAT() usa 2 argumentos, dentre os valores DATE, DATETIME ou TIMESTAMP e uma string descrevendo como o resultado deverá ser exibido.
Dentro da string de formatação, use sequencias especiais para especificar qual parte da data deverá ser exibida:

Especificador Descrição
%a Nome do dia da semana abreviado (Sun, Mon, Tue etc.) — você provavelmente já viu esta forma abreviada (em inglês) no seu relógio de pulso
%b Nome do mês abreviado (Jan, Feb, Mar etc.)
%c Número do mês
%D Dia do mês, com o sufixo indicador de numeral ordinal, em inglês (0th, 1st, 2nd, 3rd etc.)
%d Dia do mês, em numerais cardinais (00 a 31)
%e Dia do mês, numérico, sem a necessidade de acrescentar um zero para formar 2 dígitos (0, 1, 2 … 31)
%f Microssegundos (000000 … 999999)
%H Hora no formato 24h (00 … 23)
%h Hora (01 … 12)
%I Hora (1 …12)
%i Minutos em formato numérico (00 … 59)
%j Dia do ano (001 … 366)
%k Hora do dia (0 … 23)
%l Hora do dia (1 … 12)
%M Nome do mês (January, February etc.)
%m Mês em formato numérico (00 … 12)
%p AM ou PM
%r Hora do dia em formato 12h (hh:mm:ss) seguido de AM ou PM, para indicar se é antes ou depois de meio dia)
%S Segundos (00 … 59)
%s Segundos (00 … 59)
%T Hora do dia, em formato 24h (hh:mm:ss)
%U Semana do ano (00 … 53), em que Sunday (Domingo) é o primeiro dia da semana; WEEK() mode 0
%u Semana do ano (00 … 53), em que Monday (Segunda-feira) é o primeiro dia da semana; WEEK() mode 1
%V Semana do ano (01 … 53), onde Sunday é o primeiro dia da semana; WEEK() mode 2; usado com %X
%v Semana do ano (01 … 53), onde Monday é o primeiro dia da semana; WEEK() mode 3; usado com %x
%W Nome do dia da semana (SundaySaturday)
%w Número do dia da semana (0=Domingo, 1=Segunda-feira etc.)
%X Ano para a semana em que Domingo é o primeiro dia. Valor numérico, quatro dígitos; usado com %V
%x Ano para a semana em que a Segunda é o primeiro dia. Valor numérico, quatro dígitos; usado com %v
%Y Ano em formato numérico, com 4 dígitos
%y Ano em formato numérico, com 2 dígitos
%% Um caractere “%” literal

A faixa de valores dos especificadores para meses e dias começam em zero por que o MySQL permite o armazenamento de datas incompletas, tal como ‘2016-00-00’.

O idioma usado para nomes de dias, meses e abreviações é controlado pelo valor da variável de sistema lc_time_names.

Leia o artigo Como alterar o idioma de exibição dos nomes de meses e dias da semana no MySQL, para saber como ver o valor atual de lc_time_names e como alterá-lo.
Veja um exemplo do funcionamento da função DATE_FORMAT:

SELECT DATE_FORMAT('2021-08-06','%W %a %M %b');
_

Bom saber que meu aniversário cai numa sexta-feira, no ano 2021 😉

+-----------------------------------------+
| DATE_FORMAT('2021-08-06','%W %a %M %b') |
+-----------------------------------------+
| Friday Fri August Aug                   |
+-----------------------------------------+
1 row in set (0.00 sec)

Aproveite para verificar em que dia da semana o seu aniversário irá cair, no ano que vem.
Se você tem uma tabela, com campos de datas, pode obter sua visualização em formatos diferentes e, talvez, mais atraentes. Adapte o exemplo, que segue, ao seu caso:

SELECT
     -> NomeCliente,
     -> DataNascimentoCliente,
     -> DATE_FORMAT(DataNascimentoCliente, '%M %d, %Y')
     -> from CadastroClientes
     -> LIMIT 5;
+------------------+-----------------------+-------------------------------------------------+
| NomeCliente      | DataNascimentoCliente | DATE_FORMAT(DataNascimentoCliente, '%M %d, %Y') |
+------------------+-----------------------+-------------------------------------------------+
| Christen Wiggins | 1984-04-11 19:32:05   | April 11, 1984                                  |
| Justine Howard   | 1984-12-22 02:53:07   | December 22, 1984                               |
| Brenda Ellis     | 1981-05-14 03:12:50   | May 14, 1981                                    |
| Hayfa Mendoza    | 1983-10-01 22:40:44   | October 01, 1983                                |
| Kylie Mckay      | 1969-12-16 11:01:39   | December 16, 1969                               |
+------------------+-----------------------+-------------------------------------------------+
5 rows in set (0.00 sec)

Use a cláusula AS, para melhorar a aparência da saída dos dados:

SELECT
     -> NomeCliente AS 'Cliente',
     -> DATE_FORMAT(DataNascimentoCliente, 'dia %d de %M de %Y') AS 'Data de nascimento'
     -> from CadastroClientes
     -> LIMIT 5;
+------------------+----------------------------+
| Cliente          | Data de nascimento         |
+------------------+----------------------------+
| Christen Wiggins | dia 11 de abril de 1984    |
| Justine Howard   | dia 22 de dezembro de 1984 |
| Brenda Ellis     | dia 14 de maio de 1981     |
| Hayfa Mendoza    | dia 01 de outubro de 1983  |
| Kylie Mckay      | dia 16 de dezembro de 1969 |
+------------------+----------------------------+
5 rows in set (0.00 sec)

Como usar a função TIME_FORMAT()

Por fim, a função TIME_FORMAT() tem a mesma sintaxe de DATE_FORMAT(), só que ela se refere à parte do horário, especificamente.
Veja um exemplo de uso:

SELECT NomeCliente,
     -> DataCadastroCliente,
     -> TIME_FORMAT(DataCadastroCliente, '%r') AS 'Formato 12h',
     -> TIME_FORMAT(DataCadastroCliente, '%T') AS 'Formato 24h'
     -> FROM CadastroClientes
     -> LIMIT 50,2\G;
*************************** 1. row ***************************
        NomeCliente: Rhoda Pearson
DataCadastroCliente: 2012-11-25 17:58:58
        Formato 12h: 05:58:58 PM
        Formato 24h: 17:58:58
*************************** 2. row ***************************
        NomeCliente: Glenna Jarvis
DataCadastroCliente: 2016-06-30 07:42:35
        Formato 12h: 07:42:35 AM
        Formato 24h: 07:42:35
2 rows in set (0.00 sec)

Use a tabela de especificadores (acima) para personalizar sempre os resultados das suas consultas MySQL, quando envolverem datas e horários.