Estou tentando calcular o número de meses entre 2 campos de data e hora.
Existe uma maneira melhor do que obter o carimbo de data / hora Unix e dividir por 2 592 000 (segundos) e arredondar dentro do MySQL?
Estou tentando calcular o número de meses entre 2 campos de data e hora.
Existe uma maneira melhor do que obter o carimbo de data / hora Unix e dividir por 2 592 000 (segundos) e arredondar dentro do MySQL?
Respostas:
A função DATEDIFF pode fornecer o número de dias entre duas datas. O que é mais preciso, pois ... como você define um mês? (28, 29, 30 ou 31 dias?)
PERIODDIFF
simplesmente assume um valor AAAAMM, portanto, a menos que você mesmo considere os dias antes de passar os valores AAAAMM, tudo o que você está calculando é o número de meses, arredondado para o mês mais próximo se houver menos do que um número inteiro de meses. Veja a resposta de Zane abaixo.
PERIODDIFF
comentário? Perioddiff não leva valores de dias, então você está calculando o número de meses arredondado para o mês mais próximo se houver menos do que um número inteiro de meses
Estou surpreso que ainda não tenha sido mencionado:
Dê uma olhada na função TIMESTAMPDIFF () no MySQL.
O que isso permite que você faça é passar dois valores TIMESTAMP
ou DATETIME
(ou mesmo DATE
que o MySQL se converta automaticamente), bem como a unidade de tempo na qual deseja basear sua diferença.
Você pode especificar MONTH
como unidade no primeiro parâmetro:
SELECT TIMESTAMPDIFF(MONTH, '2012-05-05', '2012-06-04')
-- Outputs: 0
SELECT TIMESTAMPDIFF(MONTH, '2012-05-05', '2012-06-05')
-- Outputs: 1
SELECT TIMESTAMPDIFF(MONTH, '2012-05-05', '2012-06-15')
-- Outputs: 1
SELECT TIMESTAMPDIFF(MONTH, '2012-05-05', '2012-12-16')
-- Outputs: 7
Basicamente, obtém o número de meses decorridos desde a primeira data na lista de parâmetros. Esta solução compensa automaticamente a quantidade variável de dias em cada mês (28,30,31), bem como leva em consideração os anos bissextos - você não precisa se preocupar com nada disso.
É um pouco mais complicado se você quiser introduzir a precisão decimal no número de meses decorridos, mas aqui está como você pode fazer isso:
SELECT
TIMESTAMPDIFF(MONTH, startdate, enddate) +
DATEDIFF(
enddate,
startdate + INTERVAL
TIMESTAMPDIFF(MONTH, startdate, enddate)
MONTH
) /
DATEDIFF(
startdate + INTERVAL
TIMESTAMPDIFF(MONTH, startdate, enddate) + 1
MONTH,
startdate + INTERVAL
TIMESTAMPDIFF(MONTH, startdate, enddate)
MONTH
)
Onde startdate
e enddate
estão seus parâmetros de data, seja de duas colunas de data em uma tabela ou como parâmetros de entrada de um script:
Exemplos:
With startdate = '2012-05-05' AND enddate = '2012-05-27':
-- Outputs: 0.7097
With startdate = '2012-05-05' AND enddate = '2012-06-13':
-- Outputs: 1.2667
With startdate = '2012-02-27' AND enddate = '2012-06-02':
-- Outputs: 3.1935
PERIODDIFF
comentário sobre a resposta selecionada são muito
PERIOD_DIFF calcula meses entre duas datas.
Por exemplo, para calcular a diferença entre now () e uma coluna de tempo em sua_tabela:
select period_diff(date_format(now(), '%Y%m'), date_format(time, '%Y%m')) as months from your_table;
Eu uso também PERIODDIFF . Para obter o ano e o mês da data, uso a função EXTRACT :
SELECT PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM NOW()), EXTRACT(YEAR_MONTH FROM time)) AS months FROM your_table;
DATE_FORMAT
método, obrigado! +1
SELECT PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM NOW()), EXTRACT(YEAR_MONTH FROM FROM_UNIXTIME(time, "%Y%m%d"))) AS months FROM your_table;
PERIOD_DIFF
não leva valores de dia, então você está calculando o número de meses arredondado para o mês mais próximo se houver menos de um número inteiro de meses. Você deve editar sua resposta para deixar isso claro.
Como muitas das respostas aqui mostram, a resposta 'certa' depende exatamente do que você precisa. No meu caso, preciso arredondar para o número inteiro mais próximo .
Considere estes exemplos: 1º de janeiro -> 31 de janeiro: São 0 meses inteiros e quase 1 mês de duração. 1 ° de janeiro -> 1 ° de fevereiro? É 1 mês inteiro e exatamente 1 mês de duração.
Para obter o número de meses inteiros (completos) , use:
SELECT TIMESTAMPDIFF(MONTH, '2018-01-01', '2018-01-31'); => 0
SELECT TIMESTAMPDIFF(MONTH, '2018-01-01', '2018-02-01'); => 1
Para obter uma duração arredondada em meses, você pode usar:
SELECT ROUND(TIMESTAMPDIFF(DAY, '2018-01-01', '2018-01-31')*12/365.24); => 1
SELECT ROUND(TIMESTAMPDIFF(DAY, '2018-01-01', '2018-01-31')*12/365.24); => 1
A precisão é de +/- 5 dias e para intervalos de mais de 1000 anos. A resposta de Zane é obviamente mais precisa, mas é muito prolixa para o meu gosto.
Do manual do MySQL:
PERIOD_DIFF (P1, P2)
Retorna o número de meses entre os períodos P1 e P2. P1 e P2 devem estar no formato YYMM ou YYYYMM. Observe que os argumentos de período P1 e P2 não são valores de data.
mysql> SELECT PERIOD_DIFF (200802,200703); -> 11
Portanto, pode ser possível fazer algo assim:
Select period_diff(concat(year(d1),if(month(d1)<10,'0',''),month(d1)), concat(year(d2),if(month(d2)<10,'0',''),month(d2))) as months from your_table;
Onde d1 e d2 são as expressões de data.
Tive que usar as instruções if () para ter certeza de que os meses eram um número de dois dígitos como 02 em vez de 2.
Prefiro assim, porque todo mundo vai entender claramente à primeira vista:
SELECT
12 * (YEAR(to) - YEAR(from)) + (MONTH(to) - MONTH(from)) AS months
FROM
tab;
Existe uma maneira melhor? sim. Não use carimbos de data / hora do MySQL. Além do fato de que eles ocupam 36 bytes, eles não são nada convenientes para trabalhar. Eu recomendaria usar a data juliana e os segundos a partir da meia-noite para todos os valores de data / hora. Eles podem ser combinados para formar um UnixDateTime. Se for armazenado em um DWORD (inteiro não assinado de 4 bytes), as datas até 2.106 podem ser armazenadas como segundos desde o período, 01/01/1970 DWORD max val = 4.294.967.295 - Um DWORD pode conter 136 anos de segundos
É muito bom trabalhar com datas julianas ao fazer cálculos de data. Valores UNIXDateTime são bons para trabalhar ao fazer cálculos de data / hora. Também não são bons de olhar, então eu uso os timestamps quando preciso de uma coluna que não irei fazer muitos cálculos com, mas eu quero uma indicação rápida.
Converter para Juliano e vice-versa pode ser feito muito rapidamente em um bom idioma. Usando ponteiros, baixei para cerca de 900 Clks (também é uma conversão de STRING para INTEGER, é claro)
Quando você entra em aplicativos sérios que usam informações de data / hora como, por exemplo, os mercados financeiros, as datas julianas são de fato.
A consulta será como:
select period_diff(date_format(now(),"%Y%m"),date_format(created,"%Y%m")) from customers where..
Fornece vários meses do calendário desde a data de criação criada em um registro do cliente, permitindo que o MySQL faça a seleção do mês internamente.
DROP FUNCTION IF EXISTS `calcula_edad` $$
CREATE DEFINER=`root`@`localhost` FUNCTION `calcula_edad`(pFecha1 date, pFecha2 date, pTipo char(1)) RETURNS int(11)
Begin
Declare vMeses int;
Declare vEdad int;
Set vMeses = period_diff( date_format( pFecha1, '%Y%m' ), date_format( pFecha2, '%Y%m' ) ) ;
/* Si el dia de la fecha1 es menor al dia de fecha2, restar 1 mes */
if day(pFecha1) < day(pFecha2) then
Set vMeses = VMeses - 1;
end if;
if pTipo='A' then
Set vEdad = vMeses div 12 ;
else
Set vEdad = vMeses ;
end if ;
Return vEdad;
End
select calcula_edad(curdate(),born_date,'M') -- for number of months between 2 dates
Execute este código e ele criará uma função datedeifference que lhe dará a diferença no formato de data aaaa-mm-dd.
DELIMITER $$
CREATE FUNCTION datedifference(date1 DATE, date2 DATE) RETURNS DATE
NO SQL
BEGIN
DECLARE dif DATE;
IF DATEDIFF(date1, DATE(CONCAT(YEAR(date1),'-', MONTH(date1), '-', DAY(date2)))) < 0 THEN
SET dif=DATE_FORMAT(
CONCAT(
PERIOD_DIFF(date_format(date1, '%y%m'),date_format(date2, '%y%m'))DIV 12 ,
'-',
PERIOD_DIFF(date_format(date1, '%y%m'),date_format(date2, '%y%m'))% 12 ,
'-',
DATEDIFF(date1, DATE(CONCAT(YEAR(date1),'-', MONTH(DATE_SUB(date1, INTERVAL 1 MONTH)), '-', DAY(date2))))),
'%Y-%m-%d');
ELSEIF DATEDIFF(date1, DATE(CONCAT(YEAR(date1),'-', MONTH(date1), '-', DAY(date2)))) < DAY(LAST_DAY(DATE_SUB(date1, INTERVAL 1 MONTH))) THEN
SET dif=DATE_FORMAT(
CONCAT(
PERIOD_DIFF(date_format(date1, '%y%m'),date_format(date2, '%y%m'))DIV 12 ,
'-',
PERIOD_DIFF(date_format(date1, '%y%m'),date_format(date2, '%y%m'))% 12 ,
'-',
DATEDIFF(date1, DATE(CONCAT(YEAR(date1),'-', MONTH(date1), '-', DAY(date2))))),
'%Y-%m-%d');
ELSE
SET dif=DATE_FORMAT(
CONCAT(
PERIOD_DIFF(date_format(date1, '%y%m'),date_format(date2, '%y%m'))DIV 12 ,
'-',
PERIOD_DIFF(date_format(date1, '%y%m'),date_format(date2, '%y%m'))% 12 ,
'-',
DATEDIFF(date1, DATE(CONCAT(YEAR(date1),'-', MONTH(date1), '-', DAY(date2))))),
'%Y-%m-%d');
END IF;
RETURN dif;
END $$
DELIMITER;
Isso depende de como você deseja que o número de meses seja definido. Responda a estas perguntas: 'Qual é a diferença em meses: 15 de fevereiro de 2008 - 12 de março de 2009'. É definido por um número claro de dias que depende dos anos bissextos - em que mês se trata, ou mesmo dia do mês anterior = 1 mês.
Um cálculo para dias :
15 de fevereiro -> 29 (ano bissexto) = 14 1º de março de 2008 + 365 = 1º de março de 2009. 1º de março -> 12 de março = 12 dias. 14 + 365 + 12 = 391 dias. Total = 391 dias / (média de dias no mês = 30) = 13,03333
Um cálculo de meses :
15 de fevereiro de 2008 - 15 de fevereiro de 2009 = 12 de 15 de fevereiro -> 12 de março = menos de 1 mês Total = 12 meses, ou 13 se 15 de fevereiro - 12 de março for considerado 'o mês anterior'
SELECT *
FROM emp_salaryrevise_view
WHERE curr_year Between '2008' AND '2009'
AND MNTH Between '12' AND '1'
Eu precisava da diferença do mês com precisão. Embora a solução de Zane Bien esteja na direção certa, seu segundo e terceiro exemplos fornecem resultados imprecisos. Um dia em fevereiro dividido pelo número de dias em fevereiro não é igual a um dia em maio dividido pelo número de dias em maio. Portanto, o segundo exemplo deve gerar ((31-5 + 1) / 31 + 13/30 =) 1,3043 e o terceiro exemplo ((29-27 + 1) / 29 + 2/30 + 3 =) 3,1701.
Acabei com a seguinte consulta:
SELECT
'2012-02-27' AS startdate,
'2012-06-02' AS enddate,
TIMESTAMPDIFF(DAY, (SELECT startdate), (SELECT enddate)) AS days,
IF(MONTH((SELECT startdate)) = MONTH((SELECT enddate)), 0, (TIMESTAMPDIFF(DAY, (SELECT startdate), LAST_DAY((SELECT startdate)) + INTERVAL 1 DAY)) / DAY(LAST_DAY((SELECT startdate)))) AS period1,
TIMESTAMPDIFF(MONTH, LAST_DAY((SELECT startdate)) + INTERVAL 1 DAY, LAST_DAY((SELECT enddate))) AS period2,
IF(MONTH((SELECT startdate)) = MONTH((SELECT enddate)), (SELECT days), DAY((SELECT enddate))) / DAY(LAST_DAY((SELECT enddate))) AS period3,
(SELECT period1) + (SELECT period2) + (SELECT period3) AS months
Você pode obter anos, meses e dias desta forma:
SELECT
username
,date_of_birth
,DATE_FORMAT(CURDATE(), '%Y') - DATE_FORMAT(date_of_birth, '%Y') - (DATE_FORMAT(CURDATE(), '00-%m-%d') < DATE_FORMAT(date_of_birth, '00-%m-%d')) AS years
,PERIOD_DIFF( DATE_FORMAT(CURDATE(), '%Y%m') , DATE_FORMAT(date_of_birth, '%Y%m') ) AS months
,DATEDIFF(CURDATE(),date_of_birth) AS days
FROM users
Você também pode tentar isso:
select MONTH(NOW())-MONTH(table_date) as 'Total Month Difference' from table_name;
OU
select MONTH(Newer_date)-MONTH(Older_date) as 'Total Month Difference' from table_Name;
Esta consulta funcionou para mim :)
SELECT * FROM tbl_purchase_receipt
WHERE purchase_date BETWEEN '2008-09-09' AND '2009-09-09'
Ele simplesmente pega duas datas e recupera os valores entre elas.