PostgreSQL : 두 날짜 사이의 일 / 월 / 년


94

PostgreSQL에서 SQLServer 기능 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'를 할 수 있다는 것을 알고 있지만 다른 두 가지에 대해서는 어떤 생각이 있습니까?


답변:


120
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;

이렇게하면 두 날짜 사이에 전체 년, 월, 일 이 제공됩니다 .

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

더 자세한 datediff 정보 .


8
때문에 나이 기능의 하나는,하지만 난 그것의 인수가 : 잘못된 순서로 생각
dcarneiro

2
select date_part('month', age('2010-04-01', '2012-03-05'));제공합니다 -11. 즉 달의 정확한 차이가 아니다
smac89

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

35
이것은 수개월 + 년 등을 고려하지 않습니다. 그것은 단지 롤링 데이 체크를합니다 – 즉 SELECT date_part('day', age('2016-10-05', '2015-10-02'))반환3
Don Cheadle

문제는 몇 년의 경계 교차점과 두 날짜 사이에 몇 개의 월 경계 교차점이 있는지 계산하는 방법입니다. age()몇 년, 몇 달 동안 이것을 사용하는 것은 항상 잘못된 것입니다. 사이 2014-12-312015-01-01얼마 전하면, 월, 년 0 줄 것이다 datediff()당신은 단지 하나를 추가 할 수 없습니다 (1)과 1 줄 것 age()때문에 결과 age()+11 사이를 줄 것이다 2015-01-012015-01-02반면, datediff()것 지금 0 제공
앙드레 C. 앤더슨

148

간단히 빼기 :

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

결과:

 days
------
   11

2
예, 두 날짜 사이의 일 수를 알아야 할 때 PostgreSQL에서 작동합니다.
icl7126

큰! 참고로 두 날짜 사이의 더 작은 범위로 레코드를 주문하는 데 사용했습니다. 예 :range_end - range_start ASC, id DESC
Edison Machado

1
이 메서드는 interval단순히으로 캐스팅 할 수없는 type을 반환합니다 int. Postgres를 사용하여 간격을 시간 수로 어떻게 변환합니까? . @IgorRomanchenko의 답변에서 언급했듯이 date_part/ age함수 콤보를 사용하면 유형이 반환됩니다double precision
WebWanderer

2
다시 생각하면 이것이 올바른 방법입니다. 이 방법을 사용하여 두 날짜 사이의 일 수를 구하면 월과 년 사이의 일 수를 계산하는 반면 date_part/ age대답은 허용되는대로 두 날짜의 일 부분의 차이로 일을 제공합니다. date_part('day', age('2016-9-05', '2015-10-02'))3. 반환
WebWanderer

1
문제는 일에 관한 것이 아니라 두 날짜 사이에 교차해야하는 월과 년 경계의 수에 관한 것입니다. 질문자는 이미 며칠을 처리하는 방법을 알고있었습니다.
André C. Andersen

41

나는 최선의 답을 찾기 위해 시간을 보냈고 나는 그것을 가지고 있다고 생각한다.

이 SQL은 두 날짜 사이의 일 수를 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'));

.. 두 날짜 사이의 시간이 아니라 날짜 문자열 부분 사이의 문자적인 차이를 얻을 수 있다는 것입니다.

IE :

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

Years(double precision)=-1;

Months(double precision)=-11;

Days(double precision)=-4;


7
이것은 받아 들여진 대답이어야합니다. 내가 제안하는 유일한 비틀기는 int로 캐스팅하는 대신 ceil ()을 사용하는 것입니다 (잘라내는 대신 부분 일을 반올림하기 위해).
Rob

2
좋은 지적 @Rob. 독자는 이것을 기록해야합니다. 나는 이것이 더 선호하는 것으로 볼 수 있습니다. 대부분의 경우 날짜 사이의 문자 그대로 일 수로 내 값을 자르고 싶지만 일 수를 반올림해야하는 위치도 알 수 있습니다.
WebWanderer

2
이 접근 방식은 영국의 일광 절약 시간으로
인해 실패

와우 @qcodepeter! 잘 잡아! 당신은 절대적으로 정확합니다! 어떻게 고칠까요?
WebWanderer

1
이것은 실제로 질문에 대한 답이 아닙니다. 문제는 몇 년의 경계 교차점과 두 날짜 사이에 몇 개의 월 경계 교차점이 있는지 계산하는 방법입니다. 이 답변은 며칠이 있는지 만 답하고 윤년은 고려하지 않습니다.
André C. Andersen

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 */

이 대답은 틀 렸습니다. DATEDIFF()MS SQL의 기능은 반환 1을 위해 datediff(year, '2015-02-14', '2016-01-03'). 당신이 그 날짜 사이의 일단 올해 경계를 통과해야하기 때문이다 : docs.microsoft.com/en-us/sql/t-sql/functions/...
앙드레 C. 앤더슨

원래 질문은 MS SQL이 아니라 PostgreSQL에 관한 것이기 때문에 정답입니다.
Riki_tiki_tavi

나는 그것이 어려운 선택 일 수 있다는 것을 이해하지만, 원래의 질문은 "PostgreSQL에서 SQLServer 함수 datediff를 구현하는 방법"을 찾는 것이었다. MS SQL Server 사용자는 SQL Server라고 부르는 경향이 있습니다. "SQL Server"를 검색해보십시오. i.imgur.com/USCHdLS.png 사용자는 하나의 특정 기술에서 다른 기술로 함수를 이식하고 싶었습니다.
André C. Andersen

죄송합니다. 당신 말이 맞았습니다. 서두르고 첫 댓글의 의미를 이해하지 못한 것 같습니다. 답변이 업데이트되었습니다.
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


3

@WebWanderer의 답변은 SQL 서버를 사용하는 DateDiff에 매우 가깝지만 정확하지 않습니다. 이는 age () 함수의 사용 때문입니다.

예를 들어 '2019-07-29'와 '2020-06-25'사이의 날은 332를 반환해야하지만 age () 함수를 사용하면 327을 반환합니다. age ()는 '10 mons 27 days '를 반환하고 매월 30 일로 정확하지 않습니다.

정확한 결과를 얻으려면 타임 스탬프를 사용하십시오. 예

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


베스트 답변처럼 보입니다
urmurmur

1

이 질문은 오해로 가득 차 있습니다. 먼저 질문을 완전히 이해하겠습니다. 아스 커는 실행할 때와 동일한 결과를 얻을 싶어 MS SQL 서버 기능을 한다 , 또는를 .DATEDIFF ( datepart , startdate , enddate )datepartddmmyy

이 함수는 다음에 의해 정의됩니다.

이 함수는 지정된 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

1

Riki_tiki_tavi의 답변을 확장하고 데이터를 가져오고 싶습니다. SQL Server가 수행하는 거의 모든 작업을 수행하는 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;
$$;

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 및 월 버전을 복제하는 방법에 대한 질문에 대답하지 않습니다.
André C. Andersen

0

또 하나의 솔루션, '년'차이 버전 :

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 sum(a - b)/count(distinct(date_trunc('month', c))) FROM d

여기서는 간결함을 위해 generate_series ()를 사용했습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.