1

我已经为此工作了几个小时,没有运气并且碰壁了。我的数据如下所示:

Date1          Date2
2012-05-06     2012-05-05
2012-03-20     2012-01-05

我要做的是在两个日期之间的每个月的计数中加 1。所以我的输出理想情况下是这样的:

Year    Month    Sum
2012    2        1

换句话说,它应该检查两个日期之间的“空”月份并将它们加 1。

这是我到目前为止制定的代码。它基本上会计算两个日期之间的月数,并将它们分组为月和年。

SELECT 
    EXTRACT(YEAR FROM Date2::date) as "Year", 
    EXTRACT(MONTH FROM Date2::date) as "Month",
    SUM(DATE_PART('year', Date1::date) - DATE_PART('year', Date2::date)) * 12 +
    (DATE_PART('month', Date1::date) - DATE_PART('month', Date2::date))
FROM
    test
GROUP BY 
    "Year", 
    "Month",
ORDER BY
    "Year" DESC, 
    "Month" DESC;

这就是我卡住的地方——我不知道如何为每个“空”月实际添加 1。

4

3 回答 3

3

测试设置

使用一些示例行(应在问题中提供):

CREATE TABLE test (
   test_id serial PRIMARY KEY
 , date1   date NOT NULL
 , date2   date NOT NULL
);

INSERT INTO test(date1, date2)
VALUES
   ('2012-03-20', '2012-01-05')  -- 2012-02 lies in between
 , ('2012-01-20', '2012-03-05')  -- 2012-02 (reversed)
 , ('2012-05-06', '2012-05-05')  -- nothing
 , ('2012-05-01', '2012-06-30')  -- still nothing
 , ('2012-08-20', '2012-11-05')  -- 2012-09 - 2012-10
 , ('2012-11-20', '2013-03-05')  -- 2012-12 - 2013-02
;

Postgres 9.3或更新版本

使用LATERAL连接:

SELECT to_char(mon, 'YYYY') AS year
     , to_char(mon, 'MM')   AS month
     , count(*) AS ct
FROM  (
   SELECT date_trunc('mon',    least(date1, date2)::timestamp) + interval '1 mon' AS d1
        , date_trunc('mon', greatest(date1, date2)::timestamp) - interval '1 mon' AS d2
   FROM   test
   ) sub1
 , generate_series(d1, d2, interval '1 month') mon  -- implicit CROSS JOIN LATERAL
WHERE  d2 >= d1 -- exclude ranges without gap right away
GROUP  BY mon
ORDER  BY mon;

Postgres 9.2 或更高版本

没有LATERAL,还没有。改用子查询:

SELECT to_char(mon, 'YYYY') AS year
     , to_char(mon, 'MM')   AS month
     , count(*) AS ct
FROM  (
   SELECT generate_series(d1, d2, interval '1 month') AS mon
   FROM  (
      SELECT date_trunc('mon',    least(date1, date2)::timestamp) + interval '1 mon' AS d1
           , date_trunc('mon', greatest(date1, date2)::timestamp) - interval '1 mon' AS d2
      FROM   test
      ) sub1
   WHERE  d2 >= d1 -- exclude ranges without gap right away
   ) sub2
GROUP  BY mon
ORDER  BY mon;

结果

 year | month | ct
------+-------+----
 2012 |     2 |  2
 2012 |     9 |  1
 2012 |    10 |  1
 2012 |    12 |  1
 2013 |     1 |  1
 2013 |     2 |  1

db<>fiddle这里
是 SQL Fiddle。

解释

您正在寻找两个日期之间的完整日历月。

这些查询以升序或降序处理任何日期或时间戳,并且应该表现良好。

WHERE子句是可选的,因为generate_series()如果 start > end 则不返回任何行。但是先验地排除空范围应该更快一些。

演员 totimestamp使它更清洁和更快。理由:

于 2012-07-27T00:18:56.540 回答
0

age(timestamp1, timestamp2) => 返回区间

我们尝试从间隔中提取年份和月份并相应地添加它们。

选择提取(年龄(时间戳1,时间戳2))* 12 +提取(年龄(时间戳1,时间戳2)中的月份)

于 2014-10-09T12:28:06.340 回答
0

AFAIK,您可以简单地在 postgresql 中减去/添加日期

'2001-06-27 14:43:21'::DATETIME - '2001-06-27 14:33:21'::DATETIME = '00:10:00'::INTERVAL

所以在你的情况下,请求部分应该看起来像

DATE_PART('month', Date1::datetime - Date2::datetime) as "MonthInterval"
于 2012-07-26T15:53:54.600 回答