web-dev-qa-db-ja.com

PostgreSQL:2つの日付間の日/月/年

PostgreSQLで SQLServer-function datediff を実装する方法を探しています。あれは、

この関数は、指定されたstartdateとenddateの間で交差した指定されたdatepart境界のカウントを(符号付き整数値として)返します。

datediff(dd, '2010-04-01', '2012-03-05') = 704 // 704 changes of day in this interval
datediff(mm, '2010-04-01', '2012-03-05') = 23  // 23 changes of month
datediff(yy, '2010-04-01', '2012-03-05') = 2   // 2 changes of year

私は単に減算を使用して「dd」を実行できることを知っていますが、他の2つについてのアイデアはありますか?

67
gefei

次のようなものを試してください:

select age('2010-04-01', '2012-03-05'),
       date_part('year',age('2010-04-01', '2012-03-05')),
       date_part('month',age('2010-04-01', '2012-03-05')),
       date_part('day',age('2010-04-01', '2012-03-05'));

この関数は、2つの日付の間の年、月、日...fullを提供します。日付の変更数については、revouaが提供するリンクを使用してください。 http://www.sqlines.com/postgresql/how-to/datediff

94

単純にそれらを引きます:

select '2015-01-12'::date - '2015-01-01'::date;

これにより、以下が得られます。

?column? 
----------
   11
103
mehdi

最良の答えを探すのにしばらく時間を費やしましたが、私はそれを持っていると思います。

このSQLは、2つの日付間の日数をintegerとして提供します。

SELECT
    (EXTRACT(Epoch from age('2017-6-15', now())) / 86400)::int

..今日実行すると(2017-3-28)、以下が提供されます。

?column?
------------
77

受け入れられた答えについての誤解:

select age('2010-04-01', '2012-03-05'),
   date_part('year',age('2010-04-01', '2012-03-05')),
   date_part('month',age('2010-04-01', '2012-03-05')),
   date_part('day',age('2010-04-01', '2012-03-05'));

..は、2つの日付間の時間ではなく、日付文字列の部分間のリテラルの違いを取得するということです。

I.E:

Age(interval)=-1 years -11 mons -4 days;

Years(double precision)=-1;

Months(double precision)=-11;

Days(double precision)=-4;

32
WebWanderer

必要な機能とほぼ同じ(atiruzの回答、 here のUDFの短縮バージョンに基づく)

CREATE OR REPLACE FUNCTION datediff(type VARCHAR, date_from DATE, date_to DATE) RETURNS INTEGER LANGUAGE plpgsql
AS
$$
DECLARE age INTERVAL;
BEGIN
    CASE type
        WHEN 'year' THEN
            RETURN date_part('year', date_to) - date_part('year', date_from);
        WHEN 'month' THEN
            age := age(date_to, date_from);
            RETURN date_part('year', age) * 12 + date_part('month', age);
        ELSE
            RETURN (date_to - date_from)::int;
    END CASE;
END;
$$;

使用法:

/* Get months count between two dates */
SELECT datediff('month', '2015-02-14'::date, '2016-01-03'::date);
/* Result: 10 */

/* Get years count between two dates */
SELECT datediff('year', '2015-02-14'::date, '2016-01-03'::date);
/* Result: 1 */

/* Get days count between two dates */
SELECT datediff('day', '2015-02-14'::date, '2016-01-03'::date);
/* Result: 323 */

/* Get months count between specified and current date */
SELECT datediff('month', '2015-02-14'::date, NOW()::date);
/* Result: 47 */
9
Riki_tiki_tavi
SELECT date_part ('year', f) * 12
     + date_part ('month', f)
FROM age ('2015-06-12'::DATE, '2014-12-01'::DATE) f

結果:6

5
atiruz

Riki_tiki_taviの答えを拡張して、データを公開したいと思います。 SQLサーバーが実行するほとんどすべてを実行するdatediff関数を作成しました。そのようにして、任意のユニットを考慮することができます。

create function datediff(units character varying, start_t timestamp without time zone, end_t timestamp without time zone) returns integer
language plpgsql
 as
 $$
DECLARE
 diff_interval INTERVAL; 
 diff INT = 0;
 years_diff INT = 0;
BEGIN
 IF units IN ('yy', 'yyyy', 'year', 'mm', 'm', 'month') THEN
   years_diff = DATE_PART('year', end_t) - DATE_PART('year', start_t);

   IF units IN ('yy', 'yyyy', 'year') THEN
     -- SQL Server does not count full years passed (only difference between year parts)
     RETURN years_diff;
   ELSE
     -- If end month is less than start month it will subtracted
     RETURN years_diff * 12 + (DATE_PART('month', end_t) - DATE_PART('month', start_t)); 
   END IF;
 END IF;

 -- Minus operator returns interval 'DDD days HH:MI:SS'  
 diff_interval = end_t - start_t;

 diff = diff + DATE_PART('day', diff_interval);

 IF units IN ('wk', 'ww', 'week') THEN
   diff = diff/7;
   RETURN diff;
 END IF;

 IF units IN ('dd', 'd', 'day') THEN
   RETURN diff;
 END IF;

 diff = diff * 24 + DATE_PART('hour', diff_interval); 

 IF units IN ('hh', 'hour') THEN
    RETURN diff;
 END IF;

 diff = diff * 60 + DATE_PART('minute', diff_interval);

 IF units IN ('mi', 'n', 'minute') THEN
    RETURN diff;
 END IF;

 diff = diff * 60 + DATE_PART('second', diff_interval);

 RETURN diff;
END;
$$;

出力を含む完全な例を次に示します。 psql(10.1、サーバー9.5.10)。

30未満の値ではなく、58を取得します。
age()関数を削除し、前の投稿で言及した問題を解決しました。

drop table t;
create table t(
    d1 date
);

insert into t values(current_date - interval '58 day');

select d1
, current_timestamp - d1::timestamp date_diff
, date_part('day', current_timestamp - d1::timestamp)
from t;

     d1     |        date_diff        | date_part
------------+-------------------------+-----------
 2018-05-21 | 58 days 21:41:07.992731 |        58
0
Charlie 木匠

この質問は誤解に満ちています。最初に質問を完全に理解させます。質問者は、 MS SQL Server関数DATEDIFF ( datepart , startdate , enddate )を実行したときと同じ結果を得たいと考えています。ここで、datepartddmm、またはyyを取ります。

この関数は次によって定義されます:

この関数は、指定されたstartdateとenddateの間で交差した指定されたdatepart境界のカウントを(符号付き整数値として)返します。

これは、いくつの日の境界、月の境界、または年の境界が越えられるかを意味します。それらの間には何日、何ヶ月、何年もありません。 datediff(yy, '2010-04-01', '2012-03-05')が1ではなく2である理由は、これらの日付の間に2年未満があり、1年だけが過ぎたが、2010年から2011年、および2011年から2012年に2年の境界を越えたことを意味します。

以下は、ロジックを正しく複製するための最善の試みです。

-- datediff(dd`, '2010-04-01', '2012-03-05') = 704 // 704 changes of day in this interval
select ('2012-03-05'::date - '2010-04-01'::date );
-- 704 changes of day

-- datediff(mm, '2010-04-01', '2012-03-05') = 23  // 23 changes of month
select (date_part('year', '2012-03-05'::date) - date_part('year', '2010-04-01'::date)) * 12 + date_part('month', '2012-03-05'::date) - date_part('month', '2010-04-01'::date)
-- 23 changes of month

-- datediff(yy, '2010-04-01', '2012-03-05') = 2   // 2 changes of year
select date_part('year', '2012-03-05'::date) - date_part('year', '2010-04-01'::date);
-- 2 changes of year
0