Postgresql在月份范围之间选择

时间:2012-07-02 00:05:35

标签: sql postgresql date select date-range

我有一个表,其中一列是'YYYY-MM-DD'格式的日期。我可以使用select来获取每月范围内的所有数据吗?假设我想要从2012-01-xx到2013-04-xx的所有数据。所以我基本上都在寻找类似下面给出的SQL查询:

SELECT * FROM table WHERE date IN BETWEEN '2012-01' AND '2013-04' (INVALID QUERY)

由于每个月都以'01'开头,我可以修改上述查询以调整开始条件。

SELECT * FROM table WHERE date IN BETWEEN '2012-01-01' AND '2013-04' (INVALID QUERY)

现在问题出现了结束日期。我必须手动计算给定月份的最后日期,将所有因素考虑在内,如月份长度,闰年等,因为如果给定日期无效,查询将失败。所以目前我正在做这样的事情:

SELECT * FROM table WHERE date IN BETWEEN '2012-01-01' AND 'VALID_MONTH_END_DATE' (VALID Query)

我想知道是否有办法避免这种有效的结束日期计算?

澄清

我已经想到了下个月的第一天,但​​即便如此,我还是要用一些逻辑来说,如果是十二月,下一个月就是明年一月。我想知道是否只有SQL解决方案?

5 个答案:

答案 0 :(得分:12)

最好避免BETWEEN进行日期范围比较。更好地使用>=<,因为它与日期和日期时间列/值一样有效。

一种方式(如果你可以在外部建立日期):

WHERE date >= DATE '2012-01-01' 
  AND date < DATE '2013-05-01'      --- first date of the next month

你也可以使用日期算术:

WHERE date >= DATE '2012-01-01' 
  AND date < DATE ('2013-04-01' + INTERVAL '1 MONTH')

OVERLAPS运营商:

WHERE (date, date) OVERLAPS
      (DATE '2012-01-01', DATE '2013-05-01')

您还应该阅读Postgres文档:Date/Time Functions and Operators

manual explains here为什么OVERLAPS以这种方式运作:

  

每个时间段被认为代表半开时间间隔   开始&lt; =时间&lt;结束,除非在这种情况下开始和结束相等   代表那个单一时刻。这意味着例如两个   只有一个共同点的时间段不会重叠。

答案 1 :(得分:4)

这是报告环境中非常常见的需求。我创建了几个函数来适应这些日期操作

CREATE OR REPLACE FUNCTION public.fn_getlastofmonth (
  date
)
RETURNS date AS
$body$
begin
    return (to_char(($1 + interval '1 month'),'YYYY-MM') || '-01')::date - 1;
end;
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;

然后你可以使用......

WHERE date >= '2012-01-01' 
  AND date < fn_getlastofmonth('2013-04-01') 

答案 2 :(得分:2)

没试过,但值得一试

SELECT * 
FROM some_table 
WHERE some_date 
BETWEEN '2012-01-01' AND date('2013-04-01') - integer '1'

http://www.postgresql.org/docs/9.1/static/functions-datetime.html

答案 3 :(得分:0)

上述所有答案都提供了一种有效的解决方案,但在某种程度上是不完整的。由于我一直在寻找一个仅限SQL的解决方案(无功能),我将结合上述解决方案中的最佳技巧。

我的问题的理想解决方案是:

SELECT * FROM table
WHERE date >= '2012-01-01' AND date < date('2013-04-01') + interval '1 month'

修改

我这里没有使用重叠功能,因为我将start和end的默认日期值作为'epoch'和'now'传递。如果用户未指定任何时间范围,则查询变为:

SELECT * FROM table
WHERE date >= 'epoch' AND date < 'now'

重叠函数无法处理'epoch'和'now'并给出和SQL错误,而上述代码对两种情况都完美地工作。

PS:我已经在某种程度上提出了所有正确的答案,并引导我解决这个问题。

答案 4 :(得分:0)

SET search_path=tmp;

DROP TABLE zdates;
CREATE TABLE zdates
        ( zdate timestamp NOT NULL PRIMARY KEY
        , val INTEGER NOT NULL
        );
-- some data
INSERT INTO zdates(zdate,val)
SELECT s, 0
FROM generate_series('2012-01-01', '2012-12-31', '1 day'::interval ) s
        ;

UPDATE zdates
SET val = 1000 * random();

DELETE FROM zdates
WHERE random() < 0.1;

-- CTE to round the intervals down/up to the begin/end of the month
WITH zope AS (
        SELECT date_trunc('month', zdate)::date AS zbegin
        ,  date_trunc('month', zdate+interval '1 month')::date AS zend
        , val AS val
        FROM zdates
        )
SELECT z.zbegin
        , z.zend
        , COUNT(*) AS zcount
        , SUM(val) AS zval
FROM zope z
GROUP BY z.zbegin, z.zend
ORDER BY z.zbegin, z.zend
        ;

结果:

CREATE TABLE
INSERT 0 366
UPDATE 366
DELETE 52
   zbegin   |    zend    | zcount | zval  
------------+------------+--------+-------
 2012-01-01 | 2012-02-01 |     28 | 13740
 2012-02-01 | 2012-03-01 |     28 | 14923
 2012-03-01 | 2012-04-01 |     26 | 13775
 2012-04-01 | 2012-05-01 |     25 | 11880
 2012-05-01 | 2012-06-01 |     25 | 12693
 2012-06-01 | 2012-07-01 |     25 | 11082
 2012-07-01 | 2012-08-01 |     26 | 13254
 2012-08-01 | 2012-09-01 |     28 | 13632
 2012-09-01 | 2012-10-01 |     28 | 16461
 2012-10-01 | 2012-11-01 |     23 | 12622
 2012-11-01 | 2012-12-01 |     24 | 12554
 2012-12-01 | 2013-01-01 |     28 | 14563
(12 rows)