EatSmartシステム部ブログ

ウェブサイトの開発や運営に関する情報です。

日次サマリデータから週次・月次グラフを出力する

WEBサービスのページごとのアクセスや、ユーザーの行動の数を日毎にまとめたサマリテーブルから、グラフを出力することになりました。 グラフを出力するにあたり、ロジックではなくSQLを工夫することで、週次・月次のデータを作ってみました。

週次グラフ

今回のサマリテーブルは、アクセスや行動がない場合レコードが作成されません。このため、歯抜けになった日付の扱いに工夫が必要です。 これには、PostgreSQLのGENERATE_SERIES関数を利用して、指定された範囲の日数分の集合から、日付を作り出しました。

週次グラフは、指定された範囲を開始日1週間毎に合計した数をプロットします。これは、週ごとにGROUP BYしSUMで合計を求めます。 日付の週を求めるため、日付を作るために利用したGENERATE_SERIES関数の結果を7で割ることで、週番号を作り出しました。

これらを実装すると、以下のようなSQLになりました。週の最初の日付とPV数とアクション数が出力されます。

SELECT
    TO_CHAR(TO_DATE(MIN(w.date),'YYYYMMDD'),'MM/DD') AS date,
    CASE WHEN SUM(w.pv) IS NULL THEN 0 ELSE SUM(w.pv) END AS pv,
    CASE WHEN SUM(w.action) IS NULL THEN 0 ELSE SUM(w.action) END AS action
FROM (
    SELECT
        m.date AS date,
        (m.i / 7) AS week_number,
        CASE WHEN SUM(l.pv) IS NULL THEN 0 ELSE SUM(l.pv) END AS pv,
        CASE WHEN SUM(l.action) IS NULL THEN 0 ELSE SUM(l.action) END AS action
    FROM
    (
        SELECT
            TO_CHAR((/*from*/::DATE) + arr.i,'YYYYMMDD') AS date,
            i
        FROM
            GENERATE_SERIES(0, EXTRACT(DAY FROM ((/*to*/::TIMESTAMP) - (/*from*/::TIMESTAMP)))::INTEGER) AS arr(i)
    ) m
    LEFT OUTER JOIN s_access_report l
    ON l.ymd = m.date
    GROUP BY m.date, m.i
) AS w
GROUP BY w.week_number
ORDER BY MIN(w.date)

月次グラフ

月次グラフも、週次グラフと同様に1週間毎に合計した数をプロットします。週次グラフとの違いは、月ごとにGROUP BYしSUMで合計を求めることです。 日付の月を求めるため、TO_CHAR関数を利用して日付からYYYYMM形式の文字列を作り出しました。

SELECT
    TO_CHAR(TO_DATE(MIN(w.date),'YYYYMMDD'),'MM/DD') AS date,
    CASE WHEN SUM(w.pv) IS NULL THEN 0 ELSE SUM(w.pv) END AS pv,
    CASE WHEN SUM(w.action) IS NULL THEN 0 ELSE SUM(w.action) END AS action
FROM (
    SELECT
        m.date AS date,
        (m.yyyymm) AS yyyymm,
        CASE WHEN SUM(l.pv) IS NULL THEN 0 ELSE SUM(l.pv) END AS pv,
        CASE WHEN SUM(l.action) IS NULL THEN 0 ELSE SUM(l.action) END AS action
    FROM
    (
        SELECT
            TO_CHAR((/*from*/::DATE) + arr.i,'YYYYMMDD') AS date,
            TO_CHAR((/*from*/::DATE) + arr.i,'YYYYMM') AS yyyymm
        FROM
            GENERATE_SERIES(0, EXTRACT(DAY FROM ((/*to*/::TIMESTAMP) - (/*from*/::TIMESTAMP)))::INTEGER) AS arr(i)
    ) M
    LEFT OUTER JOIN s_access_report l
    ON l.ymd = m.date
    GROUP BY m.date, m.yyyymm
) AS w
GROUP BY w.yyyymm
ORDER BY MIN(w.date)

まとめ

以上のSQLをもとにデータを作成し、JavaScriptのライブラリを利用して、グラフを描画することが出来ました。