关于sql:获取PostgreSQL范围内的日期列表

Getting date list in a range in PostgreSQL

我想在PostgreSQL数据库中获取两个日期(包括它们)之间的日期列表。 例如,如果我有:

  • 开课日期:2012年6月29日
  • 截止日期:2012年7月3日

那么结果应该是:

1
2
3
4
5
29 june 2012
30 june 2012
1 july 2012
2 july 2012
3 july 2012

在PostgreSQL中执行此操作的最佳方法是什么?

谢谢。


1
2
3
SELECT CURRENT_DATE + i
FROM generate_series(DATE '2012-06-29'- CURRENT_DATE,
     DATE '2012-07-03' - CURRENT_DATE ) i

甚至更短:

1
2
SELECT i::DATE FROM generate_series('2012-06-29',
  '2012-07-03', '1 day'::INTERVAL) i


作为timestamp

1
2
3
4
5
6
7
8
9
SELECT generate_series('2012-06-29', '2012-07-03', '1 day'::INTERVAL);

    generate_series    
------------------------
 2012-06-29 00:00:00-03
 2012-06-30 00:00:00-03
 2012-07-01 00:00:00-03
 2012-07-02 00:00:00-03
 2012-07-03 00:00:00-03

或者转换为date

1
2
3
4
5
6
7
8
9
SELECT (generate_series('2012-06-29', '2012-07-03', '1 day'::INTERVAL))::DATE;

 generate_series
-----------------
 2012-06-29
 2012-06-30
 2012-07-01
 2012-07-02
 2012-07-03


这应该这样做:

1
2
SELECT DATE '2012-06-29' + i
FROM generate_series(1, (SELECT DATE '2012-07-3' - DATE '2012-06-29')) i

如果你不想在subselect中重复start_date,那么事情就会变得复杂一些:

1
2
3
4
5
6
7
8
9
WITH min_max (start_date, end_date) AS (
   VALUES (DATE '2012-06-29', DATE '2012-07-3')
), date_range AS (
  SELECT end_date - start_date AS duration
  FROM min_max
)
SELECT start_date + i
FROM min_max
  CROSS JOIN generate_series(1, (SELECT duration FROM date_range)) i;

(请参阅maniek的答案,以获得更好的"无重复"问题)


对于这样的事情,在系统中有一个日期表通常很方便。

就像数字表一样,它们可以非常有用并且比使用生成日期更快,特别是当您扩展到大型数据集时。

从1900年到2100年这样的日期表将非常小,因此存储空间不大。

编辑:Dunno为什么这会被投票,它可能是最好的表现。此外,它还有许多其他优点。想要将订单链接到季度绩效数字?它是表之间的简单链接。 (Order.OrderDate - > Dates.Date - > Dates.Quarter - > PerformanceTotal.Quarter)等处理工作日的情况相同,例如一个月的最后一个工作日,或上个月的第一个星期二。像数字表一样,我强烈推荐它们!


1
SELECT generate_series('2012-06-29', '2012-07-03', '1 day'::INTERVAL)::DATE;


如果日期范围应来自表表达式,则可以使用以下构造:

1
2
3
4
5
6
7
8
9
10
11
12
DROP TABLE tbl ;
CREATE TABLE tbl (zdate DATE NOT NULL );
INSERT INTO tbl(zdate) VALUES( '2012-07-01') , ('2012-07-09' );

WITH mima AS (
        SELECT MIN(zdate)::TIMESTAMP AS mi
        , MAX(zdate)::TIMESTAMP AS ma
        FROM tbl
        )
SELECT generate_series( mima.mi, mima.ma, '1 day':: INTERVAL):: DATE
FROM mima
        ;

需要强制转换,因为generate_series()不接受日期参数。


这个PLpg / SQL函数可以解决这个问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CREATE OR REPLACE FUNCTION getDateList(date1 DATE, date2 DATE)
RETURNS SETOF DATE AS
$BODY$
DECLARE
    COUNT INTEGER;
    lower_limit INTEGER :=  0;
    upper_limit INTEGER :=  date2 - date1;
BEGIN
    FOR COUNT IN lower_limit..upper_limit LOOP
        RETURN NEXT date1 + COUNT;
    END LOOP;
    RETURN;
END;
$BODY$
LANGUAGE plpgsql VOLATILE

如果您已经拥有要查询的数据库:

1
2
3
4
5
6
7
8
9
SELECT
   TO_CHAR(date_column,'DD Mon YYYY')
FROM
   some_table
WHERE
   date_column BETWEEN '29 Jun 2012' AND '3 JUL 2012'

GROUP BY date_column
ORDER BY date_column

这将导致:

1
2
3
4
5
"29 Jun 2012"
"30 Jun 2012"
"01 Jul 2012"
"02 Jul 2012"
"03 Jul 2012"