Sibainu Relax Room

柴犬と過ごす

自己結合について

日ごとに変動する値の上昇期間を求める

累積した日々の集計で日々変動する値があり、前日と比較して上昇している期間を求めるSQLを考えてみます。
今まで私が触ったことがあるSQLを使ったデータベースはACCESSのみですが、SQLをもう少し勉強したいと思い、「達人に学ぶSQL徹底指南書 ミック著」をチャレンジしてみました。
ACCESSでは実行できないSQLがたくさんありましたが、ACCESSでできるようにするにはどうしたらいいのかと考えながら読みました。
この本の中のP207が今後参考になると思い書き留めました。

SQL文の作成

左が元となる日々のデータを集積した表です。
価格の変動が日々あり、上昇した日は2021-09-23、2021-09-15~2021-09-20以下略で右が目的とする表です。元の表これをテーブル名累積表とします。

copy

SELECT
    A.*
    , (SELECT
           B.価格 
       FROM
           (SELECT
                X.*
                , (SELECT
                       COUNT(*) 
                   FROM 
                       累積表 AS Y 
                   WHERE 
                       Y.取引日 <= X.取引日
                  ) AS ROW
             FROM 
                 累積表 AS X
            ) AS B 
       WHERE
           B.ROW = A.ROW - 1
      ) AS 前日
FROM
    (SELECT 
         X.*
         , (SELECT 
                COUNT(*)
            FROM
                累積表 AS Y 
            WHERE 
                Y.取引日 <= X.取引日
           ) AS ROW
     FROM 累積表 AS X
    ) AS A;

上記SQLを実行すると右のような表になります。
土日、休日は取引がないため取引日は連続してません。前営業日と比較するためには、連続したインデックスが必要なってきます。それをROWとしています。
これで比較することができます。

copy

SELECT 
    AA.取引日
    , AA.価格
    , SWITCH(AA.価格 > AA.前日, 'UP'
             , AA.価格 < AA.前日, 'DOWN'
             , TRUE, 'STAY') AS DIFF
    , AA.ROW
FROM 
    (SELECT
        A.*
        , (SELECT
               B.価格 
           FROM
               (SELECT
                    X.*
                    , (SELECT
                           COUNT(*) 
                       FROM 
                           累積表 AS Y 
                       WHERE 
                           Y.取引日 <= X.取引日
                      ) AS ROW
                FROM 
                    累積表 AS X
               ) AS B 
           WHERE
               B.ROW = A.ROW - 1
          ) AS 前日
     FROM
         (SELECT    
              X.*
              , (SELECT 
                     COUNT(*)
                 FROM
                     累積表 AS Y 
                 WHERE 
                     Y.取引日 <= X.取引日
                ) AS ROW
          FROM 
              累積表 AS X
         ) AS A
    )  AS AA
WHERE
    SWITCH(AA.価格 > AA.前日, 'UP'
           , AA.価格 < AA.前日, 'DOWN'
           , TRUE, 'STAY') = 'UP';

上のSQL文を実行すると右の表になります。下のSQL文の中の仮表を上のSQLに置き換えて実行させたところACCESSが落ちてしまいました。
仕方がないので右のテーブルを作りこれを仮表とし、下記のSQLを実行すると最終目的の表となります。

copy

SELECT
    MIN(CC.取引日) AS 期間1
    , '~' AS ~
    ,MAX(CC.取引日) AS 期間2
FROM
    (SELECT
         AAA.取引日
         , COUNT(BBB.ROW) - MIN(AAA.ROW) AS GAP
     FROM
         仮表 AS AAA
     INNER JOIN
         仮表 AS BBB ON BBB.ROW <= AAA.ROW
     GROUP BY AAA.取引日
    ) AS CC
GROUP BY CC.GAP;

このSQLのしていることは、ROWの値と行数の差を求めそれをグループ化しています。
1-3,2-4,3-5、4-9、5-11、6-12、7-13、8-14、9-17を計算して-2、-2、-2、-5、-6、-6、-6、-6、-8
これをグループ化して取引日の最小値と最大値をとっています。