2017-01-06 7 views
0

私はここで挑戦的な課題に直面しており、その日は一日を過ごしました。手順でしか解決できませんでしたすべてのプロジェクトで実行するには時間がかかりすぎる。SQLで祝日、例外、週末をスキップする指定日にN営業日を追加するDB2

私は(関数や手続きなし)可能な場合は、単一のクエリでそれを解決したいと思います。

ここでは、プログラミング言語やSQL関数/プロシージャでこれをやっている質問がいくつかあります(これもまた解決しました)。それだけでSQL

背景情報とそれを解決することができるのであれば、私は聞いてるのよです:

  • projectテーブル
  • phaseテーブル
  • holidayテーブル
  • dayexception休日または週末の日をキャンセルする(作業日としての日付を作成する)、プロジェクトに関連付けられているテーブル
  • プロジェクトは0-N個のフェーズES
  • start dateを持ち、durationと(システムが必要とする)draworder
  • 営業日週末と休日ではないではありませんすべての日です(その日付がdayexceptionテーブル内にある場合には例外がある)
  • project |   phase(s)      | Dayexception  | Holiday 
        id | id pid start  duration draworder | pid  date | date 
        1 | 1  1 2014-01-20  10   0  | 1  2014-01-25 | 2014-01-25 
         | 2  1 2014-02-17  14   2  |     |  
    

    project id 1phase id 1ためENDDATEが実際に2014-01-31目を参照してくださいです:

は、この次のシナリオを検討しますEは、以下のデータを生成した: 以下のデータの日付(及び今後)dd/mm/yyyy(ブラジル形式)としてフォーマットされ、値をN私はすべてでビューdaysOfYearを作成し、上記データを生成するnull

proj pha  start   day  weekday dayexcp  holiday workday 
1  1 20/01/2014 20/01/2014  2  N   N  1 
1  1 20/01/2014 21/01/2014  3  N   N  1 
1  1 20/01/2014 22/01/2014  4  N   N  1 
1  1 20/01/2014 23/01/2014  5  N   N  1 
1  1 20/01/2014 24/01/2014  6  N   N  1 
1  1 20/01/2014 25/01/2014  7 25/01/2014 25/01/2014 1 
1  1 20/01/2014 26/01/2014  1  N   N  0 
1  1 20/01/2014 27/01/2014  2  N  27/01/2014 0 
1  1 20/01/2014 28/01/2014  3  N   N  1 
1  1 20/01/2014 29/01/2014  4  N   N  1 

あります2014年から2015年までの日数(それは大きくても小さくても、年回しのケースでは2年で作成されます)をCTEクエリで確認できます。そして、次のSELECT文:

select ph.project_id proj, 
     ph.id phase_id pha, 
     ph.start, 
     dy.curday day, 
     dy.weekday, /*weekday here is a calling to the weekday function of db2*/ 
     doe.exceptiondate dayexcp, 
     h.date holiday, 
     case when exceptiondate is not null or (weekday not in (1,7) and h.date is null) 
      then 1 else 0 end as workday 
    from phase ph 
     inner join daysofyear dy 
      on (year(ph.start) = dy.year) 
     left join dayexception doe 
      on (ph.project_id = doe.project_id 
       and dy.curday = truncate(doe.exceptiondate)) 
     left join holiday h 
      on (dy.curday = truncate(h.date)) 
where ph.project_id = 1 
    and ph.id = 1 
    and dy.year in (year(ph.start),year(ph.start)+1) 
    and dy.curday>=ph.start 
    and dy.curday<=ph.start + ((duration - 1) days) 
    order by ph.project_id, start, dy.curday, draworder 

このシナリオを解決するためには、私は次のクエリを作成:

select project_id, 
     min(start), 
     max(day) + sum(case when workday=0 then 1 else 0 end) days as enddate 
    from project_phase_days /*(view to the above select)*/ 

これは正しく返します。

proj  start   enddate 
1  20/01/2014  31/01/2014 

私は解決できなかった問題があります最後の終了日(max(day))に追加する日数(非稼働日sum(case when workday=0 then 1 else 0 end) days)が週末または休日または例外である場合。私のクエリは

proj  start  enddate 
81 14/04/2014 23/04/2014 

戻ります上記のデータと

proj pha  start   day  weekday dayexcp holiday workday 
81 578 14/04/2014 14/04/2014  2  N  N  1 
81 578 14/04/2014 15/04/2014  3  N  N  1 
81 578 14/04/2014 16/04/2014  4  N  N  1 
81 578 14/04/2014 17/04/2014  5  N  N  1 
81 578 14/04/2014 18/04/2014  6  N 18/04/2014 0 
81 578 14/04/2014 19/04/2014  7  N     0 
81 578 14/04/2014 20/04/2014  1  N 20/04/2014 0 
      /*the below data I added to show the problem*/    
81 578 14/04/2014 21/04/2014  2  N 21/04/2014 0 
81 578 14/04/2014 22/04/2014  3  N     1 
81 578 14/04/2014 23/04/2014  4  N     1 
81 578 14/04/2014 24/04/2014  5  N     1 

しかし、正しい結果は次のようになります。

は(以下、位相のための期間は7ある)次のシナリオを参照してください。 enddate24/04/2014としています。これは、前のデータセットで確認できるように、最終日の後の日が週末や休日(またはその問題の例外)である場合、クエリには考慮されないためです。私の時間外にある21/04/2014日も休日です。

私はまた、期間が終わるまで反復ごとに日を追加するphaseテーブルの上にCTEを作成しようとしましたが、DB2は私が上の左結合を追加できなくなるので、私はexceptionsholidaysを追加することができませんでしたCTE再帰。

with CTE (projectid, start, enddate, duration, level) as (
    select projectid, start, start as enddate, duration, 1 
     from phase 
     where project_id=1 
     and phase_id=1 
     UNION ALL 
    select projectid, start, enddate + (level days), duration, 
      case when isWorkDay(enddate + (level days)) then level+1 else level end as level 
     from CTE left join dayexception on ... 
       left join holiday on ... 
     where level < duration 
) select * from CTE 

PS:このように上記のクエリがあるため、DB2の制限のとisWorkDayだけで(それはdayexceptionと休日テーブル値の場合である)の例として、ある動作しません。

ご不明な点がございましたら、コメントにお尋ねください。 ご協力いただければ幸いです。ありがとう。

答えて

1

前後の営業日をカウントする方法。

バックグラウンド最後の世紀私はこの技術を使ったこの会社で働いていました。これは疑似コードの回答です。彼らの目的のためにはうまくいった。

必要なものは、日付列とID列が1つずつ増えていくテーブルです。テーブルには営業日のみを記入してください...これは別の日付の観測日のために難しい部分です。 2017-01-02のように私が働く休日は本当に認識された休日AFAIKではありませんでした。

今後200営業日を取得する方法。

  1. date> =現在の日付にmin(id)を選択します。
  2. id = id + 200の日付を選択します。

過去200営業日を取得する方法。

  1. 日付> =現在の日付のテーブルからmin(id)を選択します。
  2. id = id-200の日付を選択します。

営業日以内。

select count(*) from myBusinessDays where "date" between startdate and enddate 

これは擬似コードです。

+0

実際はかなり良いアイデアです。しかし、 'ID'カラムは必要ありません。RDBMに応じて、' ROW_NUMBER'、 'ROWID'などを使うことができます。私が好きなアイデアは、仕事の日のテーブル/ビューを持つことです。私は、非稼働日や例外を除外/追加することができます。ありがとうございました。私は月曜日に仕事に戻り、私はこの経験を共有します。 +1 –

+0

スコープのクリープの細目をまっすぐに保つことができれば行番号。 – danny117

+0

こんにちは@ danny117。私は最終的な解決策を追加しました。私はあなたの答えを私の問題を解決するための中心的なアイデアを与えたものとしてマークします。ありがとうございました。 –

1

@ danny117という答えを使用して、私は自分の問題を解決するためのクエリを作成することができました。正確に彼のアイデアではなく、それは私にそれを解決するための指示を与えたので、私は正しい答えとしてマークし、この答えはそれを解決するために実際のコードを共有することです。

まず、作成したビューを期間に分けましょう。私が言ったように、私はdaysofyearを2014年と2015年のデータで作成しました(私の最終的な解決策では、最終結果に大きな影響を与えずにかなり大きな間隔を追加しました)。シモンズ:私はその後、私の過去のデータに基づいて日量(大きな相+かなりのマージン)を追加し、私の質問に、クエリで別のビューを作成したという見解と、ここで日付形式はブラジル形式であるdd/mm/yyyy

create or replace view daysofyear as 
with CTE (curday, year, weekday) as (
     select a1.firstday, year(a1.firstday), dayofweek(a1.firstday) 
     from (select to_date('01/01/1990', 'dd/mm/yyyy') firstday 
       from sysibm.sysdummy1) as a1 
     union all 
     select a.curday + 1 day as sumday, 
      year(a.curday + 1 day), 
      dayofweek(a.curday + 1 day) 
     from CTE a 
     where a.curday < to_date('31/12/2050', 'dd/mm/yyyy') 
) 
select * from cte; 

ここでは、次のとおりです。その後

create or replace view project_phase_days as 
select ph.project_id proj, 
     ph.id phase_id pha, 
     ph.start, 
     dy.curday day, 
     dy.weekday, /*weekday here is a calling to the weekday function of db2*/ 
     doe.exceptiondate dayexcp, 
     h.date holiday, 
     ph.duration, 
     case when exceptiondate is not null or (weekday not in (1,7) and h.date is null) 
      then 1 else 0 end as workday 
    from phase ph 
     inner join daysofyear dy 
      on (year(ph.start) = dy.year) 
     left join dayexception doe 
      on (ph.project_id = doe.project_id 
       and dy.curday = truncate(doe.exceptiondate)) 
     left join holiday h 
      on (dy.curday = truncate(h.date)) 
where dy.year in (year(ph.start),year(ph.start)+1) 
    and dy.curday>=ph.start 
    and dy.curday<=ph.start + ((duration - 1) days) + 200 days 
              /*max duration in database is 110*/ 

私は、このクエリを作成:

select p.id, 
     a.start, 
     a.curday as enddate 
    from project p left join 
     (
     select p1.project_id, 
       p1.duration, 
       p1.start, 
       p1.curday, 
       row_number() over (partition by p1.project_id 
            order by p1.project_id, p1.start, p1.curday) rorder 
      from project_phase_days p1 
     where p1.validday=1 
     ) as a 
     on (p.id = a.project_id 
      and a.rorder = a.duration) 
order by p.id, a.start 

何それがないと、私の見解から、すべての就業日を選択している(して参加しましたproject_id, start date and current day (curday)project_idに基づいて私の他の日ビュー)ROWNUMBER私は、あなたたちは私が提供して喜んでいるでしょうより詳細な説明が必要な場合はa.rorder = a.duration

ある問題を解決したトリック一部を取得するためにproject tableに参加します。

関連する問題