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


94

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

この関数は、指定された開始日と終了日の間で交差した指定された日付部分の境界のカウントを(符号付き整数値として)返します。

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つについて何か考えはありますか?


回答:


116
SELECT
  AGE('2012-03-05', '2010-04-01'),
  DATE_PART('year', AGE('2012-03-05', '2010-04-01')) AS years,
  DATE_PART('month', AGE('2012-03-05', '2010-04-01')) AS months,
  DATE_PART('day', AGE('2012-03-05', '2010-04-01')) AS days;

これは与える完全な2つの日付の間の年、月、日を...:

          age          | years | months | days
-----------------------+-------+--------+------
 1 year 11 mons 4 days |     1 |     11 |    4

より詳細なdatediff情報


8
年齢関数のために+1ですが、引数の順序が間違っていると思います:)
dcarneiro 2013年

2
select date_part('month', age('2010-04-01', '2012-03-05'));与える-11。それは月の正しい違いではありません
smac89 2015年

1
select date_part( 'month'、age( '2012-03-05'、 '2010-04-01'));
ズーコ2016

35
これは、数ヶ月+年間などを考慮していません。それだけでない転がり日チェック-つまりSELECT date_part('day', age('2016-10-05', '2015-10-02'))リターン3
ドン・チードル

問題は、2つの日付の間に、年の境界の交差がいくつあるか、月の境界の交差がいくつあるかをどのように数えるかです。age()何年も何ヶ月もこれを行うために使用することは常に間違っています。間2014-12-312015-01-01しばらく前には、月と年に0を与えるdatediff()あなたはただ1つを追加することはできません1と1を与えるage()ため、結果age()+1の間の時に1を与えるだろう2015-01-01し、2015-01-02一方で、datediff()今では0を与えるだろう
アンドレ・C.アンデルセン

147

単にそれらを引く:

SELECT ('2015-01-12'::date - '2015-01-01'::date) AS days;

結果:

 days
------
   11

2
はい、これは2つの日付の間の日数を知る必要があるPostgreSQLで機能します。
icl7126 2015年

すごい!参考までに、2つの日付の間の狭い範囲でレコードを注文するために使用しました。例:range_end - range_start ASC, id DESC
Edison Machado

1
このメソッドは型を返すことに注意してください。これintervalは単純にとしてキャストすることはできませんintpostgresで間隔を時間数に変換するにはどうすればよいですか?。@IgorRomanchenkoの回答で述べたように、date_part/age関数の組み合わせを使用すると、型が返されますdouble precision
WebWanderer 2017年

2
考え直してみると、これは正しい方法です。このメソッドを使用して2つの日付の間の日数を取得すると、月と年の間の日数がカウントされますが、date_part/age回答は、受け入れられると、2つの日付の日数部分の差として日数を提供します。date_part('day', age('2016-9-05', '2015-10-02'))リターン3.
WebWanderer

1
問題は日数ではなく、2つの日付の間で越えなければならない月と年の境界の数です。アスカーはすでに何日もする方法を知っていました。
アンドレC.アンデルセン

41

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

この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つの日付間の時間ではなく、日付文字列の各部分の文字通りの違いを取得するということです。

IE:

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

Years(double precision)=-1;

Months(double precision)=-11;

Days(double precision)=-4;


7
これは受け入れられた答えでなければなりません。私が提案する唯一の微調整は、intにキャストする代わりにceil()を使用することです(切り捨てるのではなく、部分的な日数を切り上げるため)。
ロブ

2
良い点@Rob。読者はこれに注意する必要があります。私はこれがより好みであると見ることができます。ほとんどの場合、値を日付間の文字通りの日数として切り捨てたいと思いますが、日数の四捨五入をどこで使用するかについてもわかります。
WebWanderer 2017年

2
このアプローチは、英国の夏時間によって3倍になる可能性があります。1年に23時間の日と、25時間の日があります
。– qcode peter 2017

うわー@qcodepeter!いいキャッチ!あなたは絶対に正しいです!どうすれば修正できますか?
WebWanderer 2017

1
これは実際には質問に答えません。問題は、2つの日付の間にいくつの年の境界交差があるか、およびいくつの月の境界交差があるかをどのように数えるかです。この回答は、日数にのみ回答し、うるう年は考慮していません。
アンドレC.アンデルセン

9

必要とほぼ同じ機能(atiruzの回答に基づいて、ここから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 */

この答えは正しくありません。MSSQLのDATEDIFF()関数は1に対して戻りますdatediff(year, '2015-02-14', '2016-01-03')。あなたはこれらの日付の間に年に一度の境界を渡す必要があるためです:docs.microsoft.com/en-us/sql/t-sql/functions/...
アンドレ・C.アンデルセン

元の質問はMSSQLではなくPostgreSQLに関するものだったので、答えは正解です。
Riki_tiki_tavi

難しいと思うかもしれませんが、元々の質問は「PostgreSQLでSQLServer関数datediffを実装する方法」を見つけることでした。MS SQL Serverのユーザーは、単にSQLServerと呼ぶ傾向があります。「SQLServer」をグーグルで検索してみてください:i.imgur.com/USCHdLS.pngユーザーは、ある特定のテクノロジーから別のテクノロジーに関数を移植したいと考えていました。
アンドレC.アンデルセン

すみません、あなたは正しかった。急いでいて、最初のコメントの意味がわからなかったようです。答えが更新されました。
Riki_tiki_tavi

7
SELECT date_part ('year', f) * 12
     + date_part ('month', f)
FROM age ('2015-06-12'::DATE, '2014-12-01'::DATE) f

結果:6


1

この質問は誤解に満ちています。まず、質問を完全に理解しましょう。質問者は、MS SQLServer関数を実行したときと同じ結果を取得したいと考えています。 DATEDIFF ( datepart , startdate , enddate )datepartとりをddmmまたはyy

この関数は次のように定義されています。

この関数は、指定された開始日と終了日の間で交差した指定された日付部分の境界のカウントを(符号付き整数値として)返します。

これは、日境界、月境界、または年境界をいくつ超えるかを意味します。それらの間に何日、何ヶ月、何年あるかではありません。それが理由です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

1

Riki_tiki_taviの答えを拡張して、そこにデータを取得したいと思います。SQLServerが行うほとんどすべてのことを行う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;
$$;

1

@WebWandererの答えは、SQLサーバーを使用したDateDiffに非常に近いですが、不正確です。これは、age()関数を使用しているためです。

たとえば、「2019-07-29」と「2020-06-25」の間の日数は332を返す必要がありますが、age()関数を使用すると327が返されます。age()は「10mons 27 days」を返すため、毎月30日と間違っています。

正確な結果を得るには、タイムスタンプを使用する必要があります。例えば

ceil((select extract(epoch from (current_date::timestamp - <your_date>::timestamp)) / 86400))


0

これは、出力を含む完全な例です。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

1
この回答は、datediff(yy、 '2010-04-01'、 '2012-03-05')= 2および月バージョンを複製する方法の質問には回答していません。
アンドレC.アンデルセン

0

もう1つの解決策、「年」の違いのバージョン:

SELECT count(*) - 1 FROM (SELECT distinct(date_trunc('year', generate_series('2010-04-01'::timestamp, '2012-03-05', '1 week')))) x

    2

(1行)

そして、何ヶ月も同じトリック:

SELECT count(*) - 1 FROM (SELECT distinct(date_trunc('month', generate_series('2010-04-01'::timestamp, '2012-03-05', '1 week')))) x

   23

(1行)

実際のクエリでは、generate_seriesの代わりに、時間/日/週などでグループ化されたタイムスタンプシーケンスが存在する場合があります。

これ'count(distinct(date_trunc('month', ts)))'は、selectの「左側」で使用できます。

SELECT sum(a - b)/count(distinct(date_trunc('month', c))) FROM d

ここでは、簡潔にするためにgenerate_series()を使用しました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.