2016-10-27 15 views
2

私は、異なる国のカレンダーに基づいて2つの日付間の営業日数を計算する関数(Oracle SQL)を作成しました。何千回も実行する必要があるため、速度は非常に重要です。 開始日、終了日、および指定されたカレンダーの名前(たとえば、 'US'、 'UK'など)を入力します。入力パラメータは異なるSQLで渡されますが、今はまだテスト中です。 この機能は休日テーブルを使用して、休日の間の休業日を検索します。この表には、各国の週末と祝祭日がすべて含まれています。問題は、カーソルを使用していても機能がかなり遅いことです。機能をより速くする方法について、どんなヒントもありますか?関数内の選択クエリが問題であると思われます。私は間違っているかもしれないが、それにも関わらず私は何をすべきか分からない。SQLの日付の最適化機能

CREATE OR REPLACE FUNCTION Test (in_dt1   DATE, 
               in_dt2   DATE, 
               in_Calender VARCHAR) 
    RETURN NUMBER 
    IS 
    Count_days NUMBER := 0; 
    beg_dt  DATE; 
    End_dt  DATE; 
    Curr_dt  DATE; 
    Cursor_dato DATE; 

    CURSOR C1 
    IS 
     SELECT b.holiday 
     FROM calendars a 
      INNER JOIN holidays b ON a.calik = b.calik 
     WHERE a.cal = in_Calender AND holiday >= in_dt1 AND holiday <= in_dt2; 
    BEGIN 
     OPEN c1; 

    beg_dt := LEAST (in_dt1, in_dt2); 
    End_dt := GREATEST (in_dt1, in_dt2); 

    Curr_dt := beg_dt + 1; 

    <<OUTER>> 
    WHILE Curr_dt <= End_dt 
    LOOP 
     FETCH c1 INTO cursor_dato; 

     WHILE cursor_dato IS NULL 
     LOOP 
     Count_days := Count_days + 1; 
     Curr_dt := Curr_dt + 1; 
     EXIT OUTER WHEN Curr_dt = End_dt + 1; 
     END LOOP; 

     WHILE Curr_dt < cursor_dato 
     LOOP 
     Count_days := Count_days + 1; 
     Curr_dt := Curr_dt + 1; 
     EXIT OUTER WHEN Curr_dt = End_dt; 
     END LOOP; 

     IF Curr_dt > cursor_dato 
     THEN 
     Count_days := Count_days + 1; 
     END IF; 

     Curr_dt := Curr_dt + 1; 
    END LOOP; 

    CLOSE c1; 

    RETURN Count_days; 
END; 

答えて

5
select trunc (end_dt) - trunc(beg_dt) from dual; 

次の2つの日付の間の日数を示します。

select count(1) from holidays where holiday >= beg_dt AND holiday <= end_dt; 

2つの日付間の休日の数を指定します。

最初にあなたの結果を教えてください。カーソルやループなどがなければ。

だから、のようなもの:私は、適切な休日をカウントする国のためのパラメータを追加しました

CREATE OR REPLACE FUNCTION Test (in_dt1   DATE, 
            in_dt2   DATE, 
            in_Calender VARCHAR2, 
            p_country  varchar2) 
    RETURN NUMBER 
    IS 
     Count_days NUMBER := 0; 
     beg_dt  DATE; 
     End_dt  DATE; 
     count_holidays number := 0; 
    begin 

     beg_dt := LEAST (in_dt1, in_dt2); 
     End_dt := GREATEST (in_dt1, in_dt2); 

     count_days := trunc(end_dt) - trunc(beg_dt); 

     select count(1) 
     into count_holidays 
     from holidays 
     where holiday >= beg_dt 
     AND holiday <= end_dt 
     and country = p_country; 

     return Count_days - count_holidays; 
    end; 

+0

なぜ、デュアルから選択するのですか? 'count_days:= trunc(beg_dt) - trunc(end_dt);'?特にパフォーマンスが考慮されている場合は、無意味なコンテキスト切り替えを避けることをお勧めします。また、なぜ 'count(1)'? ['count(*)'は高速です!](https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:1156151916789#25335122556076)* {;-) – Boneist

+0

@Boneistあなたは絶対に正しいです。 SQLとして投稿したコピーされたコード – Kacper

+0

@Boneist私は、最近のバージョンのoracleではcount(*)とcount(1)の論争が解明されていると信じています。少なくとも11g以上。 – Sentinel