我正在尋找一個更簡單的解決方案來查找給定日期的年份值的最近日期(相對於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