Різниця в місяцях між датами в MySQL


85

Я хочу розрахувати кількість місяців між двома полями дати.

Чи є кращий спосіб, ніж отримання мітки часу unix та ділення на 2 592 000 (секунди) та округлення в MySQL?

Відповіді:


16

Функція DATEDIFF може дати вам кількість днів між двома датами. Що точніше, оскільки ... як ви визначаєте місяць? (28, 29, 30 або 31 день?)


68
PERIODDIFF може точно розрахувати кількість місяців.
Max Caceres

10
"Точніше" - неймовірно суб'єктивно, коли справа стосується побачень. Наприклад, якщо ви займаєтеся бізнесом, який має щомісячні цикли, потрібно знати, скільки місяців знаходиться між двома датами, може бути важливішим за кількість днів саме з тієї причини, яку ви вказали вище. Скільки триває місяць? Якщо мені потрібен підрахунок місяців, я не можу просто поділити на 30.
Томас Пейн

6
Це не правильна відповідь; ви повинні позначити відповідь Макса правильною, нижче.
Грем Чарльз

1
Відповідь Макса є неправильною, якщо ви хочете, щоб була ціла кількість місяців, правильно враховуючи високосний рік та різну кількість днів у місяці. PERIODDIFFпросто приймає значення РРРРР, тож, якщо ви самі не враховуєте дні до того, як передасте значення РРРРР, тоді все, що ви обчислюєте, - це кількість місяців, округлена до найближчого місяця, якщо менше цілої кількості місяців. Дивіться відповідь Зейна нижче.
rgvcorley

2
Чому так багато голосів за PERIODDIFFкоментар? Perioddiff не приймає денних значень, тому ви обчислюєте кількість місяців, округлених до найближчого місяця, якщо менше місяців
rgvcorley,

218

Місячна різниця між будь-якими двома датами:

Я здивований, про це ще не згадували:

Погляньте на функцію TIMESTAMPDIFF () у MySQL.

Це дозволяє вам передати два TIMESTAMPабо DATETIMEзначення (або навіть DATEякщо MySQL буде автоматично перетворювати), а також одиницю часу, на якій ви хочете базувати свою різницю.

Ви можете вказати MONTHяк одиницю в першому параметрі:

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

В основному він отримує кількість місяців, що минули з першої дати у списку параметрів. Це рішення автоматично компенсує різну кількість днів у кожному місяці (28,30,31), а також враховуючи високосний рік - вам не доведеться турбуватися про що-небудь із цього.


Місячна різниця з точністю:

Це трохи складніше, якщо ви хочете ввести десяткову точність у кількості минулих місяців, але ось як ви можете це зробити:

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
  )

Де startdateі де enddateє ваші параметри дати, будь то з двох стовпців дати в таблиці або як вхідні параметри з сценарію:

Приклади:

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

8
Це правильна відповідь, усі голоси за PERIODDIFFкоментар до обраної відповіді дуже провідні
rgvcorley

5
Дякую, у моєму випадку мені потрібні були включені дати, тому я замінюю всі "enddate" на "date_add (enddate, інтервал 1 день)". Тоді 2014-03-01 по 2014-05-31 дасть 3,00 замість 2,97
Свен Торе

2
Велике спасибі ..... особливе спасибі за "Місячна різниця з точністю:". Ти зірка MySQL
Відхі

1
Домовились. Це правильна відповідь. Саме те, що я шукав! Дякую.
Джон Воте

104

PERIOD_DIFF обчислює місяці між двома датами.

Наприклад, для обчислення різниці між now () та часовим стовпцем у вашій_таблиці:

select period_diff(date_format(now(), '%Y%m'), date_format(time, '%Y%m')) as months from your_table;

1
Будь-яка ідея, що це робить, коли це може бути 11 місяців і 1 день, тобто 12 місяців, але якщо перейти на 30-денні місяці, то це все одно буде 11?
Дарріл Хайн,

1
11 місяців та 1 день повернуть 11 місяців із прикладом вище. PERIOD_DIFF не має сенсу 30 проти 31-денних місяців.
Max Caceres

26

Я також використовую PERIODDIFF . Щоб отримати рік і місяць дати, я використовую функцію EXTRACT :

  SELECT PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM NOW()), EXTRACT(YEAR_MONTH FROM time)) AS months FROM your_table;

2
Неймовірно! Він використовує на 50% менше часу, ніж цей DATE_FORMATметод, дякую! +1
Девід Родрігес,

1
Ось для мітки часу unix SELECT PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM NOW()), EXTRACT(YEAR_MONTH FROM FROM_UNIXTIME(time, "%Y%m%d"))) AS months FROM your_table;
Смолла

2
Це неправильно, PERIOD_DIFFне приймає денних значень, тому ви обчислюєте кількість місяців, округлених до найближчого місяця, якщо менше, ніж ціла кількість місяців. Вам слід відредагувати свою відповідь, щоб це було зрозуміло.
rgvcorley

12

Як показують багато відповідей тут, правильна відповідь залежить від того, що саме вам потрібно. У моєму випадку мені потрібно округлити до найближчого цілого числа .

Розглянемо ці приклади: 1 січня -> 31 січня: це 0 цілих місяців і майже 1 місяць. 1 січня -> 1 лютого? Це 1 цілий місяць і рівно 1 місяць.

Щоб отримати кількість цілих (повних) місяців, використовуйте:

SELECT TIMESTAMPDIFF(MONTH, '2018-01-01', '2018-01-31');  => 0
SELECT TIMESTAMPDIFF(MONTH, '2018-01-01', '2018-02-01');  => 1

Щоб отримати округлену тривалість у місяцях, ви можете використовувати:

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

Це точне значення +/- 5 днів і для діапазонів понад 1000 років. Відповідь Зейна, очевидно, є більш точною, але вона занадто багатослівна на мій смак.


Я не згоден ... ВИБЕРІТЬ TIMESTAMPDIFF (МІСЯЦЬ, '31.01.2018', '2018-02-04'); дає вам 0 місяців різниці ... ВИБЕРІТЬ TIMESTAMPDIFF (МІСЯЦЬ, '2018-01-31', '2018-03-01'); дає вам 1 ... якщо 5 днів - 0, 29 днів - 1?
Скотт

@Scott, є один цілий місяць (який називається лютий) між січнем і березнем. TIMESTAMPDIFF просто вказує кількість повних місяців між датами. Якщо ви хочете, щоб будь-який місячний період дорівнював одному місяцю, використовуйте округлу версію, яку я подав.
IanS

8

З посібника MySQL:

PERIOD_DIFF (P1, P2)

Повертає кількість місяців між періодами P1 і P2. P1 та P2 повинні мати формат YYMM або YYYYMM. Зверніть увагу, що аргументи періоду P1 та P2 не є значеннями дати.

mysql> ВИБЕРІТЬ ПЕРІОД_ДІФФ (200802,200703); -> 11

Тож можливо можливо зробити щось подібне:

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;

Де d1 і d2 - вирази дати.

Мені довелося використовувати оператори if (), щоб переконатись, що місяці були двозначним числом, наприклад 02, а не 2.


8

Я віддаю перевагу цьому шляху, тому що evryone це зрозуміє з першого погляду:

SELECT
    12 * (YEAR(to) - YEAR(from)) + (MONTH(to) - MONTH(from)) AS months
FROM
    tab;

Станіслав - цей метод абсолютно правильний, якщо ви хочете знати дані ЗА МІСЯЧНИМ НОМЕРОМ. У мене є кілька звітів, які показують продажі за місяцями (січень / лютий / березень тощо), але даних стільки (діаграми за кварталами / роками / минулі роки тощо), що я не можу групувати за будь-чим. Мені потрібно взяти вихідні дані та прокрутити їх - коли це 1/31, багато розрахунків ставлять "наступний місяць" на березень, коли ми, люди, знаємо, що це лютий. ЯК це 2/4, деякі розрахунки говорять, що 1/31 - "цього місяця", але 1/28 - "минулого місяця"
Скотт,

6

Чи є кращий спосіб? так. Не використовуйте мітки часу MySQL. Окрім того, що вони займають 36 байт, з ними зовсім не зручно працювати. Я б рекомендував використовувати дату і секунди Джуліана з півночі для всіх значень дати / часу. Вони можуть бути об'єднані, щоб сформувати UnixDateTime. Якщо це зберігається в DWORD (4-байтове ціле число без підпису), то дати аж до 2106 можуть зберігатися як секунди з епохи, 01.01.1970 DWORD max val = 4,294,967,295 - DWORD може містити 136 років секунд

З Джуліаном Датами дуже приємно працювати, коли робите обчислення дати. Значення UNIXDateTime добре використовувати, коли робите обчислення дати / часу. Ні те, ні інше не варто дивитись, тому я використовую мітки часу, коли мені потрібен стовпець, який я не буду робити багато з, але я хочу короткий огляд.

Перехід на Джуліан і назад можна зробити дуже швидко гарною мовою. Використовуючи покажчики, я зменшую приблизно до 900 кліків (це також перетворення з STRING в INTEGER, звичайно)

Коли ви потрапляєте в серйозні програми, які використовують інформацію про дату / час, наприклад, фінансові ринки, дати Джуліана де-факто.


Звідки ви взяли інформацію про те, що вони займають 36 байт ? Посібник MySQL встановлює вимогу до зберігання для стовпця TIMESTAMP 4 байти . dev.mysql.com/doc/refman/5.1/en/storage-requirements.html
понча

почніть планувати помилку Y2106 зараз! ;-)
ErichBSchulz

5

Запит буде таким:

select period_diff(date_format(now(),"%Y%m"),date_format(created,"%Y%m")) from customers where..

Дає кількість календарних місяців з моменту створення позначки дати в записі клієнта, дозволяючи MySQL робити вибір місяця внутрішньо.


2
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

2

Виконайте цей код, і він створить функцію dateeifference, яка дасть вам різницю у форматі дати рррр-мм-дд.

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;

1
Як ви думаєте, чи могли б ви трохи почистити та прокоментувати свій код, щоб він став трохи зрозумілішим? Дякую
Дарріл Хайн

1

Це залежить від того, як ви хочете визначити кількість місяців. Дайте відповіді на ці запитання: "Яка різниця у місяцях: 15 лютого 2008 - 12 березня 2009". Чи визначено це чітким числом днів, яке залежить від високосного року - який це місяць, або того самого дня попереднього місяця = 1 місяць.

Розрахунок за дні :

15 лютого -> 29 (високосний рік) = 14 березня 2008 + 365 = 1 березня 2009. 1 березня -> 12 березня = 12 днів. 14 + 365 + 12 = 391 день. Разом = 391 день / (середнє число днів у місяці = 30) = 13,03333

Розрахунок місяців :

15 лютого 2008 - 15 лютого 2009 = 12 лютого 15 -> 12 березня = менше 1 місяця Разом = 12 місяців, або 13, якщо 15 лютого - 12 березня вважається "минулим місяцем"



1

Мені потрібна була різниця в місяці з точністю. Хоча рішення Зейна Бієна йде у правильному напрямку, його другий та третій приклади дають неточні результати. День лютого, поділений на кількість днів у лютому, не дорівнює дню в травні, поділеному на кількість днів у травні. Отже, другий приклад повинен виводити ((31-5 + 1) / 31 + 13/30 =) 1.3043, а третій приклад ((29-27 + 1) / 29 + 2/30 + 3 =) 3.1701.

Я отримав такий запит:

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

1

Функція PERIOD_DIFF ()

Одним із способів MySQL PERIOD_DIFF () повертає різницю між двома періодами. Періоди мають бути в одному форматі, тобто РРРРРМ або РРРМ. Слід зазначити, що періоди не є значеннями дати.

Код:

SELECT PERIOD_DIFF(200905,200811);

введіть тут опис зображення


0

Ви можете отримати роки, місяці та дні таким чином:

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

0

Ви також можете спробувати це:

select MONTH(NOW())-MONTH(table_date) as 'Total Month Difference' from table_name;

АБО

select MONTH(Newer_date)-MONTH(Older_date) as 'Total Month Difference' from table_Name;

0

Проста відповідь, вказана дата початку як ins_frm та дата завершення як ins_to

SELECT convert(TIMESTAMPDIFF(year, ins_frm, ins_to),UNSIGNED) as yrs,
       mod(TIMESTAMPDIFF(MONTH, ins_frm, ins_to),12) mnths
FROM table_name

Насолоджуйтесь :)))


0

Спробуйте це

SELECT YEAR(end_date)*12 + MONTH(end_date) - (YEAR(start_date)*12 + MONTH(start_date))

Ласкаво просимо до Stack Overflow! Не забудьте перевірити дату запитань і чи однакові рішення мають існуючі відповіді.
lehiester

-1

Цей запит у мене спрацював :)

SELECT * FROM tbl_purchase_receipt
WHERE purchase_date BETWEEN '2008-09-09' AND '2009-09-09'

Просто потрібно дві дати і отримує значення між ними.


абсолютно неправильна відповідь, він хоче підрахувати місяці
Шейн Роватт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.