SQLでN労働日後の日付を計算

SQLでN労働日後の日付を計算してみたため、記録のためやったことをまとめた。

前提となるテーブルは以下

CREATE TABLE CALENDAR (

DATE DATE NOT NULL,

IS_WORKING CHAR(1) NOT NULL WITH DEFAULT '',

PRIMARY KEY ( DATE ) )

 CALENDARテーブルに日付と労働日かどうかのIS_WORKINGが定義されている。

1. 指定された日付のN労働日後の日付を取得するSQL

SELECT MAX(DATE) FROM (

     SELECT DATE

     FROM CALENDAR

     WHERE

        DATE > '基準日付'

        AND IS_WORKING = '1'

     ORDER BY DATE ASC

     FETCH FIRST N+1 ROWS ONLY)

 2. 直近5年分の日付一覧とそのN労働日後の日付のペアを取得するSQL

SELECT T1.DATE AS BEFORE_DATE, T2.DATE AS AFTER_DATE 

FROM CALENDAR AS T1

INNER JOIN CALENDAR AS T2

    ON (T2.IS_WORKING = '1'

        AND T2.DATE > T1.DATE)

INNER JOIN CALENDAR AS T3

    ON (T3.IS_WORKING = '1'

        AND T3.DATE > T1.DATE

        AND T3.DATE <= T2.DATE)

WHERE

    T1.DATE <= CURRENT_DATE

    AND T1.DATE > CURRENT_DATE - 5 YEARS

GROUP BY T1.DATE, T2.DATE

HAVING COUNT(T3.DATE) = N+1

ただし、このSQLが重すぎるため、私のローカルでは実行時間が455秒以上になってしまう。

いろいろ調べて、ROW_NUMBER()を利用した改善版SQLは以下

SELECT T21.DATE AS BEFORE_DATE, T22.DATE AS AFTER_DATE

FROM

    (SELECT T01.DATE, MIN(T02.ROW_NUMBER) AS ROW_NUMBER

    FROM CALENDAR AS T01

    INNER JOIN (SELECT DATE, ROW_NUMBER() OVER (ORDER BY DATE ASC) AS ROW_NUMBER FROM CALENDAR WHERE IS_WORKING = '1') AS T02
        ON T02.DATE > T01.DATE
    GROUP BY T01.DATE) AS T21
INNER JOIN (SELECT DATE, ROW_NUMBER() OVER (ORDER BY DATE ASC) AS ROW_NUMBER FROM CALENDAR WHERE IS_WORKING = '1') AS T22
    ON T21.ROW_NUMBER = T22.ROW_NUMBER - N

WHERE

    T21.DATE <= CURRENT_DATE

    AND T21.DATE > CURRENT_DATE - 5 YEARS

 こちらのSQLはローカルで1秒以内で終わっている。