我有一个表,其中一列是'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解决方案?
答案 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)