Получите разницу между двумя датами как в месяцах, так и в днях в sql

Мне нужно получить разницу между двумя датами, скажем, если разница составляет 84 дня, я, вероятно, должен вывести как 2 месяца и 14 дней, код, который у меня есть, просто дает итоги. Вот код

SELECT Months_between(To_date('20120325', 'YYYYMMDD'),
       To_date('20120101', 'YYYYMMDD'))
       num_months,
       ( To_date('20120325', 'YYYYMMDD') - To_date('20120101', 'YYYYMMDD') )
       diff_in_days
FROM   dual; 

Выход:

NUM_MONTHS    DIFF_IN_DAYS
2.774193548       84

Например, мне нужно, чтобы результат этого запроса был 2 месяца и 14 дней в худшем случае, иначе я не буду возражать, если у меня будут точные дни после числа месяцев, потому что эти дни на самом деле не 14, потому что все месяцы не имеют 30 дней.


person ErrorNotFoundException    schedule 16.07.2012    source источник
comment
Разве ни у кого нет ответа на это?   -  person ErrorNotFoundException    schedule 01.11.2012
comment
Разве это не должно быть 2 месяца и 24 дня?   -  person Noel    schedule 14.08.2013


Ответы (10)


select 
  dt1, dt2,
  trunc( months_between(dt2,dt1) ) mths, 
  dt2 - add_months( dt1, trunc(months_between(dt2,dt1)) ) days
from
(
    select date '2012-01-01' dt1, date '2012-03-25' dt2 from dual union all
    select date '2012-01-01' dt1, date '2013-01-01' dt2 from dual union all
    select date '2012-01-01' dt1, date '2012-01-01' dt2 from dual union all
    select date '2012-02-28' dt1, date '2012-03-01' dt2 from dual union all
    select date '2013-02-28' dt1, date '2013-03-01' dt2 from dual union all
    select date '2013-02-28' dt1, date '2013-04-01' dt2 from dual union all
    select trunc(sysdate-1)  dt1, sysdate               from dual
) sample_data

Полученные результаты:

|                        DT1 |                       DT2 | MTHS |     DAYS |
----------------------------------------------------------------------------
|  January, 01 2012 00:00:00 |   March, 25 2012 00:00:00 |    2 |       24 |
|  January, 01 2012 00:00:00 | January, 01 2013 00:00:00 |   12 |        0 |
|  January, 01 2012 00:00:00 | January, 01 2012 00:00:00 |    0 |        0 |
| February, 28 2012 00:00:00 |   March, 01 2012 00:00:00 |    0 |        2 |
| February, 28 2013 00:00:00 |   March, 01 2013 00:00:00 |    0 |        1 |
| February, 28 2013 00:00:00 |   April, 01 2013 00:00:00 |    1 |        1 |
|   August, 14 2013 00:00:00 |  August, 15 2013 05:47:26 |    0 | 1.241273 |

Ссылка для тестирования: SQLFiddle

person jen    schedule 13.08.2013
comment
Пожалуйста, что представляют собой dt2 и dt1? Я думал, что у меня были свидания? - person ErrorNotFoundException; 14.08.2013
comment
Надеюсь, вы не против, я добавил в ваш запрос несколько примеров данных. Ни один из других ответов не дает столь точных результатов. Большинство даже отдаленно не близко. Результаты примерно 28 февраля действительно показывают разницу. Предполагая, что 30 или 31 день в месяц не всегда сработает. Я не уверен, существует ли официальный алгоритм для подсчета месяцев и дней, но эти результаты мне кажутся лучше всего. - person Jon Heller; 15.08.2013
comment
@Stanley Я присоединился к jonearles и добавил еще два примера для високосного года и даты со временем, а также ссылку на сайт, где любой может протестировать решение онлайн. - person ThinkJet; 15.08.2013
comment
Можете ли вы попробовать следующие пары свиданий и посмотреть, понятны ли вам ответы? 2013-01-28 и 2013-02-28, 2013-01-29 и 2013-02-28, 2013-01-30 и 2013-02-28, и 2013-01-31 и 2013-02-28. - person David Aldridge; 15.08.2013
comment
Наконец-то Джен. Это то, что я искал !!!!! Спасибо. На это ушло один год и один месяц :) - person ErrorNotFoundException; 15.08.2013

Обновлено для корректности. Первоначально ответил @jen.

with DATES as (
   select TO_DATE('20120101', 'YYYYMMDD') as Date1,
          TO_DATE('20120325', 'YYYYMMDD') as Date2
   from DUAL union all
   select TO_DATE('20120101', 'YYYYMMDD') as Date1,
          TO_DATE('20130101', 'YYYYMMDD') as Date2
   from DUAL union all
   select TO_DATE('20120101', 'YYYYMMDD') as Date1,
          TO_DATE('20120101', 'YYYYMMDD') as Date2
   from DUAL union all
   select TO_DATE('20130228', 'YYYYMMDD') as Date1,
          TO_DATE('20130301', 'YYYYMMDD') as Date2
   from DUAL union all
   select TO_DATE('20130228', 'YYYYMMDD') as Date1,
          TO_DATE('20130401', 'YYYYMMDD') as Date2
   from DUAL
), MONTHS_BTW as (
   select Date1, Date2,
          MONTHS_BETWEEN(Date2, Date1) as NumOfMonths
   from DATES
)
select TO_CHAR(Date1, 'MON DD YYYY') as Date_1,
       TO_CHAR(Date2, 'MON DD YYYY') as Date_2,
       NumOfMonths as Num_Of_Months,
       TRUNC(NumOfMonths) as "Month(s)",
       ADD_MONTHS(Date2, - TRUNC(NumOfMonths)) - Date1 as "Day(s)"
from MONTHS_BTW;

SQLFiddle < / strong> Демо:

    +--------------+--------------+-----------------+-----------+--------+
    |   DATE_1     |   DATE_2     | NUM_OF_MONTHS   | MONTH(S)  | DAY(S) |
    +--------------+--------------+-----------------+-----------+--------+
    | JAN 01 2012  | MAR 25 2012  | 2.774193548387  |        2  |     24 |
    | JAN 01 2012  | JAN 01 2013  | 12              |       12  |      0 |
    | JAN 01 2012  | JAN 01 2012  | 0               |        0  |      0 |
    | FEB 28 2013  | MAR 01 2013  | 0.129032258065  |        0  |      1 |
    | FEB 28 2013  | APR 01 2013  | 1.129032258065  |        1  |      1 |
    +--------------+--------------+-----------------+-----------+--------+

Обратите внимание, как для последних двух дат Oracle неправильно сообщает десятичную часть месяцев (которая дает дни). 0.1290 соответствует ровно 4 дням, если Oracle считает 31 дня в месяце (как для марта, так и для апреля).

person Ravi K Thapliyal    schedule 14.08.2013
comment
Не очень точно. Он должен был вернуться через 24 дня. - person Noel; 14.08.2013
comment
@ Ramblin'Man Спасибо за внимание. Судя по всему, использование 31 дня в месяце решило проблему. Я действительно думал, что и 30, и 31 иногда возвращают точное количество дней, а иногда и отклонение на 1 количество дней (в зависимости от того, какие месяцы были выбраны), но раунд позаботится об этом. Если хотите, посмотрите еще раз. - person Ravi K Thapliyal; 14.08.2013
comment
Умножать на 30 или 31 - не лучшая идея. На некоторые даты это сломается, особенно когда речь идет о феврале. - person Noel; 15.08.2013
comment
@ Ramblin'Man Я провел еще несколько тестов. Я понимаю, о чем вы сейчас говорите. - person Ravi K Thapliyal; 15.08.2013
comment
Используйте 30.41667, то есть (365/12). Это среднее количество дней в месяце в году. Когда я поместил (365/12) в запрос SQLite, я обнаружил, что вместо этого он сделал это 30 (он каким-то образом затопил мой запрос, хотя я не знаю как), поэтому мне пришлось подключить 30.41667, прежде чем все стало точным, так что будьте осторожны для этого в случае, если это также возникает в Oracle, тем более, что вам придется FLOOR (или TRUNC, как я вижу выше) ваши MONTHS_BETWEEN, чтобы вычесть их, чтобы получить вашу десятичную и, следовательно, часть дней, чтобы умножить это на. - person vapcguy; 06.12.2016

Я думаю, что ваш вопрос недостаточно четко сформулирован по следующей причине.

Ответы, основанные на month_between, должны касаться следующей проблемы: функция сообщает ровно один месяц между 28 февраля 2013 г. и 31 марта 2013 г., а также между 28 января 2013 г. и 28 февраля 2013 г., а также между 2013–2013 гг. 01-31 и 2013-02-28 (я подозреваю, что некоторые респонденты не использовали эти функции на практике или теперь им придется пересмотреть некоторый производственный код!)

Это задокументированное поведение, при котором даты, которые являются последними в соответствующих месяцах, или даты, приходящиеся на один и тот же день месяца, считаются целым числом месяцев.

Таким образом, вы получите тот же результат «1» при сравнении 2013-02-28 с 2013-01-28 или с 2013-01-31, но сравнение его с 2013-01-29 или 2013-01-30 дает 0,967741935484 и 0,935483870968 соответственно - поэтому по мере приближения одной даты к другой разница, сообщаемая этой функцией, может увеличиваться.

Если это неприемлемая ситуация, вам придется написать более сложную функцию или просто полагаться на расчет, который предполагает 30 (например) дней в месяц. В последнем случае, что вы будете делать с 28 февраля 2013 г. и 31 марта 2013 г.?

person David Aldridge    schedule 15.08.2013
comment
Вы поднимаете несколько отличных моментов. Это простой, распространенный вопрос, на который нет однозначного ответа. Какое бы решение ни использовалось, оно, вероятно, должно включать заявление об отказе от ответственности. Я все еще собираюсь наградить принятый ответ, так как мне это кажется правильным. Но это может быть только потому, что я слишком много использовал months_between. - person Jon Heller; 16.08.2013
comment
Да, как говорит Джон Хеллер, хорошо видно, что от 2-28 до 3-31 равно 1, а не от 2-28 до 3-28 = 1. Вот почему, когда вы переходите к дням, вам действительно следует считать дни, а не MONTHS_BETWEEN. - person vapcguy; 06.12.2016

это то, что ты имел в виду?

select trunc(months_between(To_date('20120325', 'YYYYMMDD'),to_date('20120101','YYYYMMDD'))) months,
             round(To_date('20120325', 'YYYYMMDD')-add_months(to_date('20120101','YYYYMMDD'),
                           trunc(months_between(To_date('20120325', 'YYYYMMDD'),to_date('20120101','YYYYMMDD'))))) days
        from dual;
person planben    schedule 15.08.2013
comment
Это тоже правильно: P - person ErrorNotFoundException; 15.08.2013

Здесь я просто отмечаю разницу между сегодняшним днем ​​и полем CREATED_DATE DATE в таблице, которое, очевидно, является датой в прошлом:

SELECT  
((FLOOR(ABS(MONTHS_BETWEEN(CREATED_DATE, SYSDATE))) / 12) * 12) || ' months, '  AS MONTHS,
-- we take total days - years(as days) - months(as days) to get remaining days
FLOOR((SYSDATE - CREATED_DATE) -      -- total days
(FLOOR((SYSDATE - CREATED_DATE)/365)*12)*(365/12) -      -- years, as days
-- this is total months - years (as months), to get number of months, 
-- then multiplied by 30.416667 to get months as days (and remove it from total days)
FLOOR(((SYSDATE - CREATED_DATE)/365)*12 - (FLOOR((SYSDATE - CREATED_DATE)/365)*12)) * (365/12))
|| ' days ' AS DAYS 
FROM MyTable

Я использую (365/12) или 30,416667 в качестве коэффициента преобразования, потому что я использую общее количество дней и удаляю годы и месяцы (как дни), чтобы получить оставшееся количество дней. Во всяком случае, этого было достаточно для моих целей.

person vapcguy    schedule 08.12.2016

Решение, которое я публикую, будет рассматривать месяц с 30 днями

  select CONCAT (CONCAT (num_months,' MONTHS '), CONCAT ((days-(num_months)*30),' DAYS '))
  from ( 
  SELECT floor(Months_between(To_date('20120325', 'YYYYMMDD'),
   To_date('20120101', 'YYYYMMDD')))
   num_months,
   ( To_date('20120325', 'YYYYMMDD') - To_date('20120101', 'YYYYMMDD') )
   days
  FROM   dual);
person Harshit    schedule 14.08.2013
comment
Умножать на 30 или 31 - не лучшая идея. На некоторые даты это сломается, особенно когда речь идет о феврале. - person Noel; 15.08.2013
comment
Я умножаю на (365/12) или 30.41667, но способ использовать это не в том, чтобы напрямую находить фактические дни, используя полученную цифру, как указано выше. Вы можете использовать его, чтобы преобразовать MONTHS_BETWEEN в дни, что дает общее количество дней, а затем вычесть количество времени (в днях), которое составляет месяцы, чтобы найти фактические оставшиеся дни. - person vapcguy; 06.12.2016

Синтаксис MsSql: DATEDIFF (datepart, startdate, enddate)

Oracle: возвращает количество дней.

    select
  round(Second_date - First_date)  as Diff_InDays,round ((Second_date - First_date) / (30),1)  as Diff_InMonths,round ((Second_date - First_date) * (60*24),2)  as TimeIn_Minitues
from
  (
  select
    to_date('01/01/2012 01:30:00 PM','mm/dd/yyyy hh:mi:ss am') as First_date
   ,to_date('05/02/2012 01:35:00 PM','mm/dd/yyyy HH:MI:SS AM') as Second_date
  from
    dual
  ) result;

Демо: http://sqlfiddle.com/#!4/c26e8/36

person Satinder singh    schedule 16.07.2012
comment
Это синтаксис транзакции sql, а не оракул - person Oofpez; 16.07.2012
comment
Даже в tsql, который не дает OP, запрашивает - person Madhivanan; 16.07.2012
comment
Я использую Oracle и Talend Open Studio - person ErrorNotFoundException; 16.07.2012
comment
Что представляет date_part? - person ErrorNotFoundException; 16.07.2012
comment
Это не отличается от запроса, который я дал в моем вопросе, потому что он возвращает то же самое. Например, Ваш запрос повторяется 122 дня. В месяцах это должно было быть 4 месяца и один день, но вернулось 4,1 - person ErrorNotFoundException; 16.07.2012

Узнать год - месяц - день между двумя днями в Orale Sql


select 
trunc(trunc(months_between(To_date('20120101', 'YYYYMMDD'),to_date('19910228','YYYYMMDD')))/12) years ,
trunc(months_between(To_date('20120101', 'YYYYMMDD'),to_date('19910228','YYYYMMDD'))) 
-
(trunc(trunc(months_between(To_date('20120101', 'YYYYMMDD'),to_date('19910228','YYYYMMDD')))/12))*12
months,
             round(To_date('20120101', 'YYYYMMDD')-add_months(to_date('19910228','YYYYMMDD'),
                           trunc(months_between(To_date('20120101', 'YYYYMMDD'),to_date('19910228','YYYYMMDD'))))) days
        from dual;
person zico    schedule 17.09.2013

См. Запрос ниже (предполагается @ dt1> = @ dt2);

Declare @dt1 datetime = '2013-7-3'
Declare @dt2 datetime = '2013-5-2'

select abs(DATEDIFF(DD, @dt2, @dt1)) Days,
case when @dt1 >= @dt2
    then case when DAY(@dt2)<=DAY(@dt1)
        then Convert(varchar, DATEDIFF(MONTH, @dt2, @dt1)) + CONVERT(varchar, ' Month(s) ') + Convert(varchar, DAY(@dt1)-DAY(@dt2)) + CONVERT(varchar, 'Day(s).')
        else Convert(varchar, DATEDIFF(MONTH, @dt2, @dt1)-1) + CONVERT(varchar, ' Month(s) ') + convert(varchar, abs(DATEDIFF(DD, @dt1, DateAdd(Month, -1, @dt1))) - (DAY(@dt2)-DAY(@dt1))) + CONVERT(varchar, 'Day(s).')
    end
    else 'See asumption: @dt1 must be >= @dt2'
end In_Months_Days

Возврат:

Days | In_Months_Days

62   |   2 Month(s) 1Day(s).
person Hassan Farid    schedule 13.08.2013
comment
Вопрос Oracle, а не вопрос SQL Server. - person David Aldridge; 14.08.2013

person    schedule
comment
DateDiff находится в какой таблице Oracle, потому что он возвращает Invaid Identifier в двойном ›??? - person ErrorNotFoundException; 16.07.2012
comment
да. Вы правы, я скоро предоставлю вам конкретное решение оракула - person Sami; 16.07.2012
comment
Если dateiff не работает для вас, просто попробуйте date2-date1 вместо dateiff (day, date2, date1) Что бы сработало, поделитесь со мной, пожалуйста .. Спасибо - person Sami; 16.07.2012
comment
w3schools.com/sql/func_datediff_mysql.asp На самом деле пока у меня нет оракула движок, в противном случае я думаю, что простая функция sql (язык) также должна работать в оракуле - person Sami; 16.07.2012
comment
Пожалуйста, поймите требуемый результат, если он, скажем, 122 дня, это должно быть 2 месяца и 14 дней. - person ErrorNotFoundException; 12.04.2013
comment
DateDiff - это MySQL и MS SQL, а не Oracle. - person vapcguy; 06.12.2016