2012-09-14 3 views
1

일년 중 특정 날짜의 가장 가까운 날짜 (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 등)에 대한 검사를 추가 한 후 코드는 진짜 난장판이되었습니다.

더 간단하고, 더 좋고, 절대 안전한 솔루션 (기능 또는 단일 쿼리)을 제안 할 수 있다면 감사하겠습니다. 오라클에 너무 복잡한 복잡한 구조를 사용하지 마십시오. 동일한 구조로 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 ; 
/ 

논리적으로 내가 고려하고있는 알고리즘은 & 안부, Reji

EDIT 1

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) 

덕분에 코드의 중복성). 나는 아직도 해결책에 대한 대안이나 개선을 기대하고있다.

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 ; 
/ 

편집 2 : 체크? "valid_date_ < ADD_MONTHS"는이 날짜가 같은 (반복) 년 자체 (그렇지 않으면 비 윤년 366의 값이 다음 반환에 있는지 확인하는 것입니다 년 시작일). 또한 참조 날짜와의 관련 비교는 (reference = "30/01/2012", input = 365) 같은 경우를 방지하는 것입니다. 여기에서 첫 번째 값은 연중 날짜 값이 365 인 가장 가까운 날짜이므로 유효 날짜 값은 '31/12/2011 '이 아니고'30/12/2012 '가 아니어야합니다.

+0

아마도 내가 이해하지 못 하겠지만 찾고있는 날짜가 테이블에 저장되어 있거나 가장 가까운 날짜를 찾고 있습니까? – Ben

+0

좋은 질문입니다. 나는 그 잡기가 도약이라고 생각한다. 2000 년경에 366 명을 찾으면 가장 가까운 사람은 4 년 뒤입니다. – Rene

+0

@Ben, 불행히도 날짜는 메모가 테이블에 저장됩니다. 수정 된 날짜, 시작일, 종료일을 포함합니다 (예 : 31/12/2012, 365, 1). 예상 전환은 365 = 30/12/12 및 1 = 01/01/2013입니다.modified_date는 요일 값에 가깝지만, 그 이전, 중간 또는 이후 일 수 있습니다 (예 : 31/12/2012, 1, 1). 이제 저는 요즘을 실제 날짜 값으로 변환하는 문제에 직면 해 있습니다. 요일 사이의 격차가 365 이하로 보장되지만 복잡한 솔루션이 필요하지는 않지만 일반적인 상황을 처리 할 수있을 정도로 기능을 충분히 보장 할 것이라고 생각했습니다. – mpathi

답변

0

DB2에서 (연속) 행을 생성하는 다음의 원리가있을 것으로 판단 . 필자는 WITH 절을 DB2와 함께 사용하기 위해 피해야했습니다. 피터슨의 해결책은이 방향으로 생각하는 데 도움이되었습니다.

CREATE OR REPLACE FUNCTION GetNearestDate(reference_date DATE, day_of_year NUMBER) RETURN DATE IS 
    nearest_date DATE ; 
BEGIN 
    SELECT valid_date INTO nearest_date 
    FROM 
    ( 
     SELECT first_date + day_of_year - 1 AS valid_date 
     FROM 
     ( 
      SELECT add_months(trunc(reference_date, 'YYYY'), (rownum-10) * 12) AS first_date 
      FROM all_objects 
      WHERE rownum <= 20 
     ) 
     WHERE to_char(first_date, 'YYYY') = to_char(first_date + day_of_year - 1, 'YYYY') 
     ORDER BY abs(first_date + day_of_year - 1 - reference_date), first_date 
    ) 
    WHERE rownum < 2 ; 

    RETURN nearest_date ; 

EXCEPTION WHEN OTHERS THEN 
    RETURN nvl(reference_date, sysdate) ; 
END ; 
/ 
1

효율성이

with dates as 
(SELECT to_date('01-01-1980', 'DD-MM-YYYY') + rownum day, 
    to_number(to_char(to_date('01-01-1980', 'DD-MM-YYYY') + rownum, 
         'DDD')) day_of_year 
    FROM ALL_OBJECTS 
WHERE ROWNUM <= 100 * 365) 
select t.* 
from (select dates.*, 
      abs(to_date('01-01-2012', 'DD-MM-YYYY') - dates.day) diff 
     from dates 
    where dates.day_of_year = 1 
    order by 3) t 
where rownum <= 1 

그래서 우리는 1980 후 그것이 그 해의 날 우리가주의 각 날짜에 대해 ~ 100 년 모든 날짜를 가지고 있지 문제 나는 다음과 같은 날짜와 미리 계산 된 테이블을 사용합니다. 그 후에 우리는 모든 날짜에 대한 거리를 계산합니다. 일년 중 첫 번째는 오름차순으로 정렬하고 최종 결과는 첫 번째 행이됩니다.

내부 쿼리는 오라클에 조금 다릅니다,하지만 난 당분간 사용할 계획하고있는 솔루션입니다뿐만 아니라

+0

감사합니다. Peterson. 그것은 약간의 무력이다 (당신의 잘못이 아니다). 그것은 확실히 내 논리와 결합하여 실행을 조금 쉽게 할 수있는 가능성을 열어줍니다. – mpathi

관련 문제