Estoy buscando calcular el número de meses entre 2 campos de fecha y hora.
¿Hay una mejor manera que obtener la marca de tiempo de Unix y dividir por 2 592 000 (segundos) y redondear dentro de MySQL?
Estoy buscando calcular el número de meses entre 2 campos de fecha y hora.
¿Hay una mejor manera que obtener la marca de tiempo de Unix y dividir por 2 592 000 (segundos) y redondear dentro de MySQL?
Respuestas:
La función DATEDIFF puede darle el número de días entre dos fechas. ¿Cuál es más acertado, ya que ... cómo se define un mes? (¿28, 29, 30 o 31 días?)
PERIODDIFF
simplemente toma un valor YYYYMM, por lo que a menos que usted mismo tenga en cuenta los días antes de pasar los valores YYYYMM, todo lo que está calculando es el número de meses, redondeado al mes más cercano si hay menos de un número entero de meses. Vea la respuesta de Zane a continuación.
PERIODDIFF
comentario? Perioddiff no toma valores de día, por lo que está calculando el número de meses redondeado al mes más cercano si hay menos de un número entero de meses
Me sorprende que esto no se haya mencionado todavía:
Eche un vistazo al TIMESTAMPDIFF () en MySQL.
Lo que esto le permite hacer es pasar en dos TIMESTAMP
oDATETIME
valores (o incluso DATE
cuando MySQL se convertirá automáticamente), así como la unidad de tiempo en la que desea basar su diferencia.
Puede especificar MONTH
como unidad en el primer 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
Básicamente, obtiene el número de meses transcurridos desde la primera fecha en la lista de parámetros. Esta solución compensa automáticamente la cantidad variable de días en cada mes (28, 30, 31) y también tiene en cuenta los años bisiestos; no tiene que preocuparse por nada de eso.
Es un poco más complicado si desea introducir precisión decimal en el número de meses transcurridos, pero así es como puede hacerlo:
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
)
Donde startdate
yenddate
están sus parámetros de fecha, ya sea de dos columnas de fecha en una tabla o como parámetros de entrada de un script:
Ejemplos:
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
comentario sobre la respuesta seleccionada son muy importantes
PERIOD_DIFF calcula los meses entre dos fechas.
Por ejemplo, para calcular la diferencia entre now () y una columna de tiempo en your_table:
select period_diff(date_format(now(), '%Y%m'), date_format(time, '%Y%m')) as months from your_table;
También utilizo PERIODDIFF . Para obtener el año y el mes de la fecha, uso la función EXTRACCIÓN :
SELECT PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM NOW()), EXTRACT(YEAR_MONTH FROM time)) AS months FROM your_table;
DATE_FORMAT
método, ¡gracias! +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
no toma valores de día, por lo que está calculando el número de meses redondeado al mes más cercano si hay menos de un número entero de meses. Debes editar tu respuesta para que quede claro.
Como muestran muchas de las respuestas aquí, la respuesta "correcta" depende exactamente de lo que necesita. En mi caso, necesito redondear al número entero más cercano .
Considere estos ejemplos: 1 de enero -> 31 de enero: son 0 meses completos y casi 1 mes. 1 de enero -> 1 de febrero? Es 1 mes completo y exactamente 1 mes.
Para obtener la cantidad de meses enteros (completos) , use:
SELECT TIMESTAMPDIFF(MONTH, '2018-01-01', '2018-01-31'); => 0
SELECT TIMESTAMPDIFF(MONTH, '2018-01-01', '2018-02-01'); => 1
Para obtener una duración redondeada en meses, puede 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
Esto tiene una precisión de +/- 5 días y para rangos de más de 1000 años. La respuesta de Zane es obviamente más precisa, pero es demasiado detallada para mi gusto.
Del manual de MySQL:
PERIOD_DIFF (P1, P2)
Devuelve el número de meses entre los períodos P1 y P2. P1 y P2 deben tener el formato YYMM o YYYYMM. Tenga en cuenta que los argumentos de período P1 y P2 no son valores de fecha.
mysql> SELECT PERIOD_DIFF (200802,200703); -> 11
Entonces, puede ser posible hacer algo como esto:
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;
Donde d1 y d2 son las expresiones de fecha.
Tuve que usar las declaraciones if () para asegurarme de que los meses fueran un número de dos dígitos como 02 en lugar de 2.
Prefiero esta forma, porque todos lo entenderán claramente a primera vista:
SELECT
12 * (YEAR(to) - YEAR(from)) + (MONTH(to) - MONTH(from)) AS months
FROM
tab;
¿Existe una forma mejor? si. No use marcas de tiempo de MySQL. Aparte del hecho de que ocupan 36 Bytes, no es nada cómodo trabajar con ellos. Recomendaría usar la fecha juliana y los segundos desde la medianoche para todos los valores de fecha / hora. Estos se pueden combinar para formar un UnixDateTime. Si esto se almacena en un DWORD (entero de 4 bytes sin signo), las fechas hasta 2106 se pueden almacenar como segundos desde epoc, 01/01/1970 DWORD max val = 4,294,967,295 - Un DWORD puede contener 136 años de segundos
Es muy agradable trabajar con las fechas julianas cuando se realizan cálculos de fecha. Los valores de UNIXDateTime son buenos para trabajar cuando se realizan cálculos de fecha / hora. Ninguno de los dos es bueno para mirar, así que utilizo las marcas de tiempo cuando necesito una columna en la que no haré muchos cálculos con, pero quiero una indicación de un vistazo.
La conversión a Julian y viceversa se puede hacer muy rápidamente en un buen idioma. Usando punteros, lo reduzco a aproximadamente 900 Clks (esto también es una conversión de STRING a INTEGER, por supuesto)
Cuando ingresa a aplicaciones serias que usan información de fecha / hora como, por ejemplo, los mercados financieros, las fechas julianas son de facto.
La consulta será como:
select period_diff(date_format(now(),"%Y%m"),date_format(created,"%Y%m")) from customers where..
Da un número de meses calendario desde la fecha de creación en el registro de un cliente, lo que permite que MySQL haga la selección del mes 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
Ejecute este código y creará una función datedeifference que le dará la diferencia en el formato de fecha 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;
Esto depende de cómo desee que se defina el número de meses. Responda estas preguntas: '¿Cuál es la diferencia en meses: 15 de febrero de 2008 - 12 de marzo de 2009?'. ¿Está definido por un número claro de días que depende de los años bisiestos, qué mes es, o el mismo día del mes anterior = 1 mes?
Un cálculo de días :
15 de febrero -> 29 (año bisiesto) = 14 1 de marzo de 2008 + 365 = 1 de marzo de 2009. 1 de marzo -> 12 de marzo = 12 días. 14 + 365 + 12 = 391 días. Total = 391 días / (promedio de días en el mes = 30) = 13.03333
Un cálculo de meses :
15 de febrero de 2008 - 15 de febrero de 2009 = 12 15 de febrero -> 12 de marzo = menos de 1 mes Total = 12 meses, o 13 si del 15 de febrero al 12 de marzo se considera 'el último mes'
SELECT *
FROM emp_salaryrevise_view
WHERE curr_year Between '2008' AND '2009'
AND MNTH Between '12' AND '1'
Necesitaba un mes de diferencia con precisión. Aunque la solución de Zane Bien va en la dirección correcta, su segundo y tercer ejemplo dan resultados inexactos. Un día de febrero dividido por el número de días de febrero no es igual a un día de mayo dividido por el número de días de mayo. Entonces, el segundo ejemplo debería dar como resultado ((31-5 + 1) / 31 + 13/30 =) 1.3043 y el tercer ejemplo ((29-27 + 1) / 29 + 2/30 + 3 =) 3.1701.
Terminé con la siguiente 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
Puede obtener años, meses y días de esta manera:
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
También puedes probar esto:
select MONTH(NOW())-MONTH(table_date) as 'Total Month Difference' from table_name;
O
select MONTH(Newer_date)-MONTH(Older_date) as 'Total Month Difference' from table_Name;
Esta consulta funcionó para mí :)
SELECT * FROM tbl_purchase_receipt
WHERE purchase_date BETWEEN '2008-09-09' AND '2009-09-09'
Simplemente toma dos fechas y recupera los valores entre ellas.