Postgres generate_series不包括日期范围

时间:2015-08-03 13:01:35

标签: postgresql date generate-series

我正在创建订阅管理系统,并需要生成未来两年即将开始的结算日期列表。我已经能够使用generate_series来获取相应的日期:

SELECT i::DATE
FROM generate_series('2015-08-01', '2017-08-01', '1 month'::INTERVAL) i

我需要采取的最后一步是从计算中排除特定的日期范围。这些排除的日期范围可以是任何时间范围。此外,不应将它们考虑在generate_series的时间范围内。

例如,假设我们将日期范围排除在' 2015-08-27'到2015-09-03'。生成的generate_series应该从计算中排除该周的日期,并且基本上将所有未来一个月的结算日期推迟到未来一周

2015-08-01
2015-09-10
2015-10-10
2015-11-10
2015-12-10

2 个答案:

答案 0 :(得分:2)

首先,您要在接下来的两年内创建一个时间序列的日期,EXCEPT您的停电日期:

SELECT dt
FROM generate_series('2015-08-01'::date, '2017-08-01'::date, interval '1 day') AS s(dt)
EXCEPT
SELECT dt
FROM generate_series('2015-08-27'::date, '2015-09-03'::date, interval '1 day') as ex1(dt)

请注意,您可以根据需要拥有尽可能多的EXCEPT个子句。对于个别停电日(而不是范围),您可以使用VALUES子句而不是SELECT

然后,您在该时间序列上进行窗口化,以生成可计费天数的行数:

SELECT row_number() OVER (ORDER BY dt) AS rn, dt
FROM (<query above>) x

然后您选择要开票的日期:

SELECT dt
FROM (<query above>) y
WHERE rn % 30 = 1; -- billing on the first day of the period

(后面的查询遵循克雷格30天的结算建议)

收率:

SELECT dt
FROM (
  SELECT row_number() OVER (ORDER BY dt) AS rn, dt
  FROM (
    SELECT dt
    FROM generate_series('2015-08-01'::date, '2017-08-01'::date, interval '1 day') AS s(dt)
    EXCEPT
    SELECT dt
    FROM generate_series('2015-08-27'::date, '2015-09-03'::date, interval '1 day') as ex1(dt)
  ) x
) y
WHERE rn % 30 = 1;

答案 1 :(得分:0)

您必须拆分调用以生成排除系列。有点像这样:

  • 3个查询联盟
  • 第一个查询从
  • 开始将日期从开始范围拉到排除范围
  • 第二个查询会在排除范围和结束日期之间提取日期
  • 第三个查询会在没有任何系列日期跨越排除范围时提取日期

注意:您仍然需要一种循环排除列表的方法(如果有的话)。此查询也可能效率不高,因为可以通过函数或过程代码更好地处理此类场景。