用户可以选择日期,如以下屏幕截图所示:
任何开始月/日和结束月/日组合都有效,例如:
第二种组合很难(我称之为“棘手的日期情景”),因为结束月/日的年份应<<>> 年后的 < em>开始月/日。也就是说,对于1900年(也在上面的屏幕截图中显示),完整日期将是:
编写一个SQL语句,选择日期介于开始月/日和结束月/日之间的表中的值,而不管选择开始日期和结束日期的方式。换句话说,这是一年包装问题。
查询作为参数接收:
考虑以下MySQL代码(有效):
end_year = start_year +
greatest( -1 *
sign(
datediff(
date(
concat_ws('-', year, end_month, end_day )
),
date(
concat_ws('-', year, start_month, start_day )
)
)
), 0
)
关于棘手的日期场景如何运作:
这在MySQL中起作用,因为日期的差异是正面的还是负面的。在PostgreSQL中,等效功能总是返回一个正数,而不管它们的相对时间顺序如何。 (但我减去日期的测试可能不正确。)
如何为PostgreSQL重写以下(破损)代码,以考虑起始和结束月/日对的相对时间顺序(关于引入棘手的年度时间位移)?
SELECT
m.amount
FROM
measurement m
WHERE
(extract(MONTH FROM m.taken) >= month1 AND
extract(DAY FROM m.taken) >= day1) AND
(extract(MONTH FROM m.taken) <= month2 AND
extract(DAY FROM m.taken) <= day2)
有任何想法,评论或问题吗?
(这些日期在PHP中预先解析为MM / DD格式。我的偏好是纯粹的PostgreSQL解决方案,但我愿意接受有关使用PHP可能使问题更简单的建议.SQL存在于存储过程中这是由JasperReports调用的,因此PHP可以触及的唯一项目是传递到报告引擎的日期。)
以下代码几乎可以使用:
select
(extract(YEAR FROM m.taken)||'-12-12')::date as start_date,
((extract(YEAR FROM m.taken)+
greatest(
0,
sign(
(extract(YEAR FROM m.taken)||'-12-12')::date -
(extract(YEAR FROM m.taken)||'-02-01')::date) ))||'-02-01')::date as end_date,
m.taken,
extract(YEAR FROM m.taken) as year_taken
from
measurement m
where
m.station_id = 200 AND
m.category_id = 1 AND
(m.taken, m.taken) OVERLAPS
((extract(YEAR FROM m.taken)||'-12-12')::date,
((extract(YEAR FROM m.taken)+
greatest(
0,
sign(
(extract(YEAR FROM m.taken)||'-12-12')::date -
(extract(YEAR FROM m.taken)||'-02-01')::date) ))||'-02-01')::date)
它产生正确的开始和结束日期约束:
start_date ;end_date ;taken ;year_taken
"1969-12-12";"1970-02-01";"1969-12-12";1969
"1969-12-12";"1970-02-01";"1969-12-13";1969
...
"1969-12-12";"1970-02-01";"1969-12-31";1969
"1970-12-12";"1971-02-01";"1970-12-12";1970
但是,1970-01-01
和1970-02-01
之间的值缺失,但必须包含在内。
PostgreSQL 8.4.4和PHP 5.2.10
答案 0 :(得分:1)
SELECT
m.amount
FROM
measurement m,
to_date('Dec 01 1900', 'Mon DD YYYY') AS A(d1),
to_date('Feb 28 1900', 'Mon DD YYYY') AS B(d2),
to_date('Feb 28 1901', 'Mon DD YYYY') AS C(d3)
WHERE m.taken
BETWEEN
d1 AND
CASE WHEN d2 < d1 THEN d3 ELSE d2 END
参考文献:conditional expressions,data type formatting functions。
编辑:对不起,我以为你想要特定的一年:SELECT
amount
FROM
(SELECT
M.amount, M.taken,
to_date('Dec 01 ' || extract(YEAR FROM M.taken), 'Mon DD YYYY'),
to_date('Feb 28 ' || extract(YEAR FROM M.taken), 'Mon DD YYYY')
FROM
measurement AS M
) AS A(amount, taken, d1, d2)
WHERE
(d2 >= d1 AND taken BETWEEN d1 AND d2)
OR
(d2 < d1 AND (taken <= d2 OR taken >= d1));
如果设置很大,则没有很多优化机会。在这种情况下,您可以使用SQL函数将所有日期转换为特定年份(say taken - (extract(year from taken) - 1900) * '1 year'::interval)
,然后与1900年12月2日和1900年2月28日进行比较。这样您可以索引此日期转换函数的结果和您不必为每个条目计算两个日期。