我正在寻找一个更简单的解决方案来查找给定日期的年份值的最近日期(相对于sysdate)。实施例(在格式“日/月/年”)Oracle sql查询一年中最近的日期
sysdate = "01/01/2012" input = 365 result = "31/12/2011"
sysdate = "01/01/2012" input = 366 result = "31/12/2012"
sysdate = "01/01/2012" input = 1 result = "01/01/2012"
sysdate = "31/12/2012" input = 1 result = "01/01/2013"
基本上所得到的日期可以在当前年,前一年或明年。最初我写了一个如下所示的小程序。这里我使用的是参考日期而不是sysdate来测试结果。这适用于一年中的输入日不是366的情况。但是,当它是366时,这会失败,并且可能需要进一步向前和向后旅行以找到最近的有效日期。在添加闰年检查(所有条件4,100,400等)后,代码变得非常混乱。
如果你能提出一个更简单,更好,更简单的解决方案(函数或单个查询),我将不胜感激。请不要使用针对Oracle特有的复杂结构,因为我将不得不将它们移植到DB2。而且,效率最不受关注,因为它不会被严重执行。
CREATE OR REPLACE PROCEDURE test(ref_date_str varchar2, doy number) IS
ref_date date ;
nearest_date date ;
BEGIN
ref_date := to_date(ref_date_str, 'dd/mm/yyyy') ;
WITH choices AS
(
SELECT trunc(ref_date, 'yyyy') + doy - 1 AS choice_date FROM dual
UNION
SELECT trunc(trunc(ref_date, 'yyyy') - 1, 'yyyy') + doy - 1 AS choice_date FROM dual
UNION
SELECT add_months(trunc(ref_date, 'yyyy'), 12) + doy - 1 AS choice_date FROM dual
)
SELECT choice_date INTO nearest_date FROM choices WHERE abs(ref_date - choice_date) =
(SELECT min(abs(ref_date - choice_date)) FROM choices) AND rownum < 2 ;
dbms_output.put_line(to_char(nearest_date, 'dd/mm/yyyy')) ;
END ;
/
按道理我正在考虑的算法是
for each year backwards from current year
if a valid date found for the doy, and it is <= sysdate
first_date = this valid date
exit loop
for each year forward from current year
if a valid date found for the doy, and it is > sysdate
second_date = this valid date
exit loop
chosen_date = closest_to_sysdate_among(first_date, second_date)
感谢&问候, Reji
编辑1:下面给出的是我在上面已经给出了算法的实现(有一些代码冗余)。我仍然期待着解决方案的替代或改进。
CREATE OR REPLACE FUNCTION GetNearestDate(reference_date DATE, day_of_year NUMBER) RETURN DATE IS
valid_date_1 DATE ;
valid_date_2 DATE ;
iter_date DATE ;
BEGIN
iter_date := trunc(reference_date, 'yyyy') ;
WHILE TRUE
LOOP
valid_date_1 := iter_date + day_of_year - 1 ;
IF valid_date_1 < add_months(iter_date, 12) AND valid_date_1 <= reference_date THEN
EXIT ;
END IF ;
iter_date := trunc(iter_date - 1, 'yyyy') ;
END LOOP ;
iter_date := trunc(reference_date, 'yyyy') ;
WHILE TRUE
LOOP
valid_date_2 := iter_date + day_of_year - 1 ;
IF valid_date_2 < add_months(iter_date, 12) AND valid_date_2 > reference_date THEN
EXIT ;
END IF ;
iter_date := add_months(iter_date, 12) ;
END LOOP ;
IF abs(valid_date_1 - reference_date) <= abs(valid_date_2 - reference_date) THEN
RETURN valid_date_1 ;
END IF ;
RETURN valid_date_2 ;
END ;
/
EDIT 2:检查“?valid_date_ < ADD_MONTHS”是为了保证这个日期是相同的(重复)当年的自己(否则,366的值,对于非闰年接下来将返回一年的开始日期)。此外,与参考日期相关联的比较是为了防范例如(参考=“2012年1月30日”,输入= 365)。这里valid_date_1应该是“2011年12月31日”而不是“2012年12月30日”,因为第一个日期与参考日期最接近,日年值为365.
也许我不明白,但是你正在寻找的日期存储在一个表中,或者你只是在寻找最近的日期? – Ben
不错的问题。我认为这是闰年。如果你在2000年左右寻找366,最近的一个可能会在4年后。 – Rene
@Ben,不幸的是日期被存储在表中。它包含modified_date,start_day,end_day(例如:31/12/2012,365,1,在这种情况下,预期转换为365 = 2012年12月30日和1 = 2013年1月1日。modified_date接近日期值,但可以在之前,之间或之后(例如:2012年12月31日,1日)。现在我面临将这几天转换为实际日期值的问题。虽然时间之间的差距保证不到365,因此不需要复杂的解决方案,但我认为我将使这个功能足够万无一失来处理任何一般情况。 – mpathi