123

我正在寻找一种在 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',但对其他两个有什么想法吗?

4

10 回答 10

199

只需减去它们:

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

结果:

 days
------
   11
于 2015-06-12T09:38:56.293 回答
146
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

更详细的日期差异信息

于 2013-07-24T11:54:59.847 回答
52

我花了一些时间寻找最佳答案,我想我知道了。

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

于 2017-03-28T22:46:03.387 回答
11

几乎与您需要的功能相同(基于 atiruz 的回答,UDF 的缩短版本 from here

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 */
于 2016-01-26T05:42:24.513 回答
7
SELECT date_part ('year', f) * 12
     + date_part ('month', f)
FROM age ('2015-06-12'::DATE, '2014-12-01'::DATE) f

结果:6

于 2015-06-12T18:17:57.937 回答
4

@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))

于 2020-06-25T20:06:35.937 回答
1

这个问题充满了误解。首先让我们充分理解这个问题。提问者希望获得与运行MS SQL Server 函数 where DATEDIFF ( datepart , startdate , enddate )、或时相同的结果。datepartddmmyy

该函数定义为:

此函数返回指定开始日期和结束日期之间跨越的指定日期部分边界的计数(作为有符号整数值)。

这意味着跨越了多少天界限、月界限或年界限。不是他们之间有多少天、几个月或几年。这就是为什么datediff(yy, '2010-04-01', '2012-03-05')是 2,而不是 1。这些日期之间的时间不到 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
于 2019-01-16T00:58:19.627 回答
0

这是一个带有输出的完整示例。psql(10.1,服务器 9.5.10)。

你得到 58,而不是小于 30 的值。
删除 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
于 2018-07-18T21:39:02.403 回答
0

我想扩展 Riki_tiki_tavi 的答案并在那里获取数据。我创建了一个 datediff 函数,它几乎可以完成 sql server 所做的所有事情。这样我们就可以考虑任何单位。

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;
$$;
于 2019-03-18T13:19:54.970 回答
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()。

于 2020-01-04T16:36:08.760 回答