8

我有一个表格,其中一列是“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)

我想知道是否有任何方法可以避免这种有效的结束日期计算?

澄清

我已经想到了下个月的第一天,但​​即使那样我也必须应用一些逻辑,如果是 12 月,下个月将是明年的 1 月。我想知道是否可以使用仅 SQL 的解决方案?

4

5 回答 5

14

最好避免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 文档:日期/时间函数和运算符

手册在这里解释了为什么这样OVERLAPS工作:

每个时间段都被认为表示半开区间 start <= time < end,除非 start 和 end 相等,在这种情况下它表示单个时间瞬间。这意味着例如只有一个共同端点的两个时间段不重叠。

于 2012-07-02T00:13:17.220 回答
5

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

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') 
于 2012-07-02T00:20:59.817 回答
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

于 2012-07-02T00:15:01.843 回答
0

上面的所有答案都提供了一个可行的解决方案,但以某种方式不完整。由于我一直在寻找仅 SQL 的解决方案(无功能),因此我结合了上述解决方案中的最佳技巧。

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

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

编辑

我在这里没有使用重叠函数,因为我将开始和结束的默认日期值作为“纪元”和“现在”传递。如果用户未指定任何时间范围,则查询变为:

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

Overlap 函数无法处理 'epoch' 和 'now' 并给出 SQL 错误,而上面的代码在这两种情况下都能完美运行。

PS:我已经在某种程度上支持了所有正确的答案,并引导我找到了这个解决方案。

于 2012-07-02T00:52:28.870 回答
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)
于 2012-07-02T10:56:00.687 回答