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


86

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

Есть ли лучший способ, чем получить временную метку unix и разделить на 2 592 000 (секунд) и округлить в MySQL?

Ответы:


16

Функция РАЗНДАТ (РАЗНДАТ) может дать вам количество дней между двумя датами. Что более точно, поскольку ... как вы определяете месяц? (28, 29, 30 или 31 день?)


68
PERIODDIFF может точно рассчитать количество месяцев.
Макс Касерес,

10
Когда дело касается дат, термин «более точный» является невероятно субъективным. Если, например, вы работаете в бизнесе с месячными циклами, вам необходимо знать, сколько месяцев проходит между двумя датами, что может быть важнее, чем количество дней, по той самой причине, которую вы изложили выше. Сколько длится месяц? Если мне нужно подсчитать месяц, я не могу просто разделить его на 30.
Томас Пейн

6
Это неправильный ответ; вы должны отметить ответ Макса как правильный ниже.
Грэм Чарльз

1
Ответ Макса неверен, если вам нужно количество полных месяцев с учетом високосных лет и разного количества дней в месяце. PERIODDIFFпросто принимает значение ГГГГММ, поэтому, если вы сами не учитываете дни перед передачей значений ГГГГММ, тогда все, что вы вычисляете, - это количество месяцев, округленное до ближайшего месяца, если число месяцев меньше целого. См. Ответ Зейна ниже.
rgvcorley

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

220

Разница в месяцах между любыми двумя датами:

Я удивлен, что об этом еще не упомянули:

Взгляните на функцию 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комментарий к выбранному ответу очень
ошибочны,

5
Спасибо, в моем случае мне нужны инклюзивные даты, поэтому я заменяю все «enddate» на «date_add (enddate, interval 1 day)». Тогда с 01.03.2014 по 31.05.2014 будет давать 3.00 вместо 2.97
Sven Tore

2
Большое вам спасибо ..... особое спасибо за "Разница в месяцах с точностью:". Вы звезда mysql
Видхи

1
Согласовано. Это правильный ответ. Именно то, что я искал! Благодарю.
Джон Голосовать

105

PERIOD_DIFF вычисляет месяцы между двумя датами.

Например, чтобы вычислить разницу между now () и столбцом времени в your_table:

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

1
Есть идеи, что он делает, если это может быть 11 месяцев и 1 день, то есть 12 месяцев, но если вы перейдете на 30-дневные месяцы, то все равно будет 11?
Darryl Hein

1
11 месяцев и 1 день вернут 11 месяцев в примере выше. PERIOD_DIFF не имеет смысла в месяцах 30 и 31 день.
Макс Касерес,

27

Использую также PERIODDIFF . Чтобы получить год и месяц даты, я использую функцию EXTRACT :

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

2
Невероятно! Этот DATE_FORMATметод использует на 50% меньше времени , спасибо! +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;
Smolla 06

2
Это неверно, PERIOD_DIFFне принимает значения дня, поэтому вы вычисляете количество месяцев с округлением до ближайшего месяца, если количество месяцев меньше целого. Вы должны отредактировать свой ответ, чтобы прояснить это.
rgvcorley

12

Как показывают многие из приведенных здесь ответов, «правильный» ответ зависит от того, что именно вам нужно. В моем случае мне нужно округлить до ближайшего целого числа .

Рассмотрим эти примеры: 1 января -> 31 января: 0 полных месяцев и почти 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 (МЕСЯЦ, '2018-01-31', '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> ВЫБРАТЬ PERIOD_DIFF (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

Я предпочитаю этот способ, потому что все это ясно поймет с первого взгляда:

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

Станислав - этот метод как раз подходит, если вы хотите знать данные ПО НОМЕРУ МЕСЯЦА. У меня есть несколько отчетов, которые показывают продажи по месяцам (январь / февраль / март и т. Д.), Но данных так много (графики по кварталам / годам / прошлым годам и т. Д.), Что я не могу сгруппировать их по чему-либо. Мне нужно извлечь необработанные данные и просмотреть их в цикле - когда это 1/31, многие вычисления переводят «следующий месяц» в март, когда мы, люди, знаем, что это февраль. ЕСЛИ это 2/4, некоторые расчеты говорят, что 31 января - это «в этом месяце», а
Скотт

6

Есть ли способ лучше? да. Не используйте метки времени MySQL. Кроме того, что они занимают 36 байт, работать с ними совсем не удобно. Я бы рекомендовал использовать юлианские даты и секунды с полуночи для всех значений даты и времени. Их можно объединить в UnixDateTime. Если это сохранено в DWORD (беззнаковое 4-байтовое целое число), тогда даты вплоть до 2106 могут быть сохранены в секундах с момента epoc, 01.01.1970 DWORD max val = 4,294,967,295 - DWORD может содержать 136 лет секунд

Юлианские даты очень удобны для работы при вычислении даты. Значения UNIXDateTime удобны для работы при вычислении даты и времени. На них также не стоит смотреть, поэтому я использую временные метки, когда мне нужен столбец, который я не буду выполнять много вычислений. с, но мне нужна краткая информация.

Преобразование в юлианский и обратно может быть выполнено очень быстро на хорошем языке. Используя указатели, у меня это примерно до 900 Clks (это также преобразование из STRING в INTEGER, конечно)

Когда вы попадаете в серьезные приложения, которые используют информацию о дате и времени, например, на финансовых рынках, даты по юлианскому календарю являются де-факто.


Откуда у вас информация о том, что они занимают 36 байт ? В руководстве MySQL указано, что для столбца TIMESTAMP требуется 4 байта . dev.mysql.com/doc/refman/5.1/en/storage-requirements.html
poncha

начните планировать ошибку 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

Выполните этот код, и он создаст функцию datedeifference, которая даст вам разницу в формате даты yyyy-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;

1
Как вы думаете, можно было бы немного поправить и прокомментировать свой код, чтобы он был более понятным? Спасибо
Darryl Hein

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.