web-dev-qa-db-ja.com

SQLで月と日の両方の2つの日付の差を取得する

2つの日付の差を取得する必要があります。差が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日間。

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

19
jen

正確さのために更新されました。元は@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デモ:

 + -------------- + -------------- + -------------- --- + ----------- + -------- + 
 | DATE_1 | DATE_2 | NUM_OF_MONTHS | MONTH(S)| DAY(S)| 
 + -------------- + -------------- + --------- -------- + ----------- + -------- + 
 | 2012年1月1日| 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 | 
 + -------------- + -------------- + ------ ----------- + ----------- + -------- + 

最後の2つの日付について、Oracleは月の小数部分(日を与える)を誤って報告することに注意してください。 0.1290は、正確に4日に対応します。Oracleでは、1か月の31日(3月と4月の両方)を考慮します。

2
Ravi Thapliyal

次の理由から、あなたの質問は十分に定義されていないと思います。

Months_betweenに依存する回答は、次の問題に対処する必要があります。関数が2013-02-28と2013-03-31の間、2013-01-28と2013-02-28の間、および2013- 01-31および2013-02-28(一部の回答者はこれらの機能を実際に使用していなかったか、または現在いくつかの製品コードを確認する必要があると思われます!)

これは文書化された動作であり、それぞれの月の最後の日付、または月の同じ日に該当する日付は、月の整数倍であると判断されます。

したがって、2013-02-28と2013-01-28または2013-01-31を比較すると同じ結果の「1」が得られますが、2013-01-29または2013-01-30と比較すると0.967741935484とそれぞれ0.935483870968-ある日付が他の日付に近づくと、この関数によって報告される差が大きくなる可能性があります。

これが許容できない状況である場合は、より複雑な関数を記述するか、月に30(たとえば)日を想定した計算に頼る必要があります。後者の場合、2013-02-28および2013-03-31にどのように対処しますか?

2
David Aldridge

ここでは、今日とCREATED_DATEDATEテーブル内のフィールド。これは明らかに過去の日付です。

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を変換係数として使用します。とにかく、私の目的には十分でした。

1
vapcguy

これはあなたが言及したことですか?

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;
1
planben

私が投稿したソリューションは、30日間で1か月を検討します

  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);
0
Harshit