ダッシュボードレポート - いいね数

これは「いいね」のダッシュボードレポートのSQLバージョンです。

このクエリは、指定された期間内のサイト上のすべての投稿に与えられた「いいね」の総数を日ごとにレポートします。

-- [params]
-- date :start_date = 2023-12-08
-- date :end_date = 2024-01-10

WITH date_range AS (
  SELECT date_trunc('day', series) AS date
  FROM generate_series(
    :start_date::timestamp,
    :end_date::timestamp,
    '1 day'::interval
  ) series
)

SELECT
  dr.date::date,
  COALESCE(pa.likes_count, 0) AS likes_count
FROM
  date_range dr
LEFT JOIN (
  SELECT
    date_trunc('day', pa.created_at) AS action_date,
    COUNT(*) AS likes_count
  FROM post_actions pa
  WHERE pa.post_action_type_id = 2
    AND pa.created_at >= :start_date
    AND pa.created_at <= (:end_date::date + 1)
  GROUP BY action_date
) pa ON dr.date = pa.action_date
ORDER BY dr.date

SQLクエリの説明

クエリの主な構造は、date_rangeという名前のCTE(共通テーブル式)に基づいて構築されており、これはユーザー定義期間内の各日を表すタイムスタンプのシリーズを生成するために使用されます。

パラメータ

クエリは2つのパラメータを受け入れます。

  • :start_date: レポートを生成する期間の開始日。
  • :end_date: レポートを生成する期間の終了日。

共通テーブル式: date_range

  • generate_seriesは、:start_dateから:end_dateまで、'1 day’の間隔でインクリメントされたタイムスタンプのセットを作成する関数です。
  • date_trunc('day', series)は、タイムスタンプをその日の始まりに切り捨て、事実上すべてのタイムスタンプをそれぞれの日の00:00:00に正規化します。
  • 結果は、:start_dateから:end_dateまでの全期間をカバーする、1行に1つの日付のセットになります。

サブクエリ: 「いいね」のカウント

サブクエリは、post_actionsテーブルの行をカウントすることにより、日ごとの「いいね」の数を計算するために使用されます。

  • このクエリは、アクションタイプが「いいね」を示すエントリに対してpost_actionsをフィルタリングします(post_action_type_id = 2は「いいね」を意味します)。
  • アクションを日付範囲にフィルタリングし、最後の日の「いいね」を含めるために終了日に1日追加します。
  • 結果を日ごとにグループ化し、各日の「いいね」をカウントします。

メインクエリ: 結果のマージ

クエリの最後のセクションは、date_range CTEのすべての日付のセットと、サブクエリからの「いいね」のカウントをマージします。

  • LEFT JOINにより、対応する「いいね」アクションがない日付(サブクエリで見つかった結合がない)でも、date_rangeのすべての日付が結果に含まれることが保証されます。
  • COALESCEは、NULLカウント(「いいね」がない日)をゼロに置き換えるために使用され、レポートが「いいね」アクティビティのない日を正確に反映するようにします。
  • 最終的な結果セットは、指定された期間に与えられた「いいね」の時系列ビューを提供するために、日付で並べ替えられます。

結果例

date likes_count
2023-12-08 123
2023-12-09 156
2023-12-10 278
2023-12-11 134
2023-12-12 89
「いいね!」 2

AND pa.deleted_at IS NULL を追加して、いいねをフィルタリングし、削除してから結合する必要があるか、それともダッシュボードクエリ自体で可能な変更か?

「いいね!」 2

現在のダッシュボードレポートには削除された「いいね」が含まれているため、AND pa.deleted IS NULL を追加すると、このクエリとダッシュボードレポートとの一致方法が変わってしまいます。

ただし、基盤となるダッシュボードレポートを変更して、削除された「いいね」を含めないようにすることは、検討すべき良い変更かもしれません。

「いいね!」 2

私のフォーラムはそれほど大きくなく、リアクションのほとんどは「スタッフ」(管理者、モデレーター、TL=4)から来ています。一般ユーザーと「スタッフ」からのいいねの数を比較し、1日あたりの投稿数をリストアップして、何が起こっているのか、そしてリアクションの使用率を向上させるためにどこに力を集中する必要があるのかをよりよく把握したいと思いました。

私と私の仲間であるChatGPTがこれを思いつきました:

-- [params]
-- date :start_date = 2024-01-01
-- date :end_date = 2024-12-31

WITH date_range AS (
  SELECT date_trunc('day', series) AS date
  FROM generate_series(
    :start_date::timestamp,
    :end_date::timestamp,
    '1 day'::interval
  ) series
),
staff_users AS (
  SELECT id
  FROM users
  WHERE admin = true OR moderator = true OR trust_level = 4
)

SELECT
  dr.date::date,
  COALESCE(pa_non_staff.regular_likes_count, 0) AS regular_likes_count,
  COALESCE(pa_staff.staff_likes_count, 0) AS staff_likes_count,
  COALESCE(pa_non_staff.regular_likes_count, 0) + COALESCE(pa_staff.staff_likes_count, 0) AS total_likes,
  COALESCE(posts_count.posts_per_day, 0) AS posts_per_day
FROM
  date_range dr
LEFT JOIN (
  SELECT
    date_trunc('day', pa.created_at) AS action_date,
    COUNT(*) AS regular_likes_count
  FROM post_actions pa
  WHERE pa.post_action_type_id = 2
    AND pa.user_id NOT IN (SELECT id FROM staff_users)
    AND pa.created_at >= :start_date
    AND pa.created_at <= (:end_date::date + 1)
  GROUP BY action_date
) pa_non_staff ON dr.date = pa_non_staff.action_date
LEFT JOIN (
  SELECT
    date_trunc('day', pa.created_at) AS action_date,
    COUNT(*) AS staff_likes_count
  FROM post_actions pa
  WHERE pa.post_action_type_id = 2
    AND pa.user_id IN (SELECT id FROM staff_users)
    AND pa.created_at >= :start_date
    AND pa.created_at <= (:end_date::date + 1)
  GROUP BY action_date
) pa_staff ON dr.date = pa_staff.action_date
LEFT JOIN (
  SELECT
    date_trunc('day', p.created_at) AS post_date,
    COUNT(*) AS posts_per_day
  FROM posts p
  WHERE p.created_at >= :start_date
    AND p.created_at <= (:end_date::date + 1)
  GROUP BY post_date
) posts_count ON dr.date = posts_count.post_date
ORDER BY dr.date

@SaraDevの元のクエリからの変更点(ありがとう、Sara!):
SQL変更点の概要

  1. スタッフグループの生成:
    usersテーブルからスタッフユーザーを特定するためのstaff_users CTEを追加しました。スタッフユーザーは、次のいずれかの条件を満たすユーザーとして定義されます。
    • admin = true
    • moderator = true
    • trust_level = 4
  2. スタッフからのいいねの分離:
    staff_usersグループ内のuser_idpost_actionsをフィルタリングすることにより、スタッフユーザーからのいいね数を計算するサブクエリを追加しました(staff_likes_count)。
  3. 非スタッフいいね列名の変更:
    非スタッフからのいいねの出力ラベルをlikes_countからregular_likes_countに変更しました。
  4. 合計いいね数の追加:
    regular_likes_countstaff_likes_countを合計するtotal_likes列を導入しました。
  5. 1日あたりの投稿数の追加:
    1日あたりの投稿数を計算するサブクエリを追加し(posts_per_day)、日付範囲に結合しました。
    (はい、ChatGPTもこの変更点のリストを作成してくれました。)

結果例:

日付 一般ユーザーからのいいね数 スタッフからのいいね数 1日あたりの投稿数
24/1/1 0 6 7
24/1/2 0 5 3
24/1/3 1 0 4
24/1/4 1 2 5
24/1/5 9 9 30
24/1/6 0 1 11
24/1/7 2 4 11
24/1/8 0 5 18
24/1/9 0 0 2
24/1/10 0 0 7
24/1/11 0 4 5
24/1/12 4 0 4
24/1/13 6 0 10
24/1/14 1 7 18
24/1/15 2 4 7
週単位で集計した同じクエリの結果(平滑化のため)
-- [params]
-- integer :weeks_ago = 52

WITH date_range AS (
  SELECT date_trunc('week', series) AS week_start
  FROM generate_series(
    date_trunc('week', now()) - (:weeks_ago || ' weeks')::interval,
    date_trunc('week', now()),
    '1 week'::interval
  ) series
),
staff_users AS (
  SELECT id
  FROM users
  WHERE admin = true OR moderator = true OR trust_level = 4
)

SELECT
  dr.week_start::date AS week_start,
  COALESCE(pa_non_staff.regular_likes_count, 0) AS regular_likes_count,
  COALESCE(pa_staff.staff_likes_count, 0) AS staff_likes_count,
  COALESCE(pa_non_staff.regular_likes_count, 0) + COALESCE(pa_staff.staff_likes_count, 0) AS total_likes,
  COALESCE(posts_count.posts_per_week, 0) AS posts_per_week
FROM
  date_range dr
LEFT JOIN (
  SELECT
    date_trunc('week', pa.created_at) AS action_week,
    COUNT(*) AS regular_likes_count
  FROM post_actions pa
  WHERE pa.post_action_type_id = 2
    AND pa.user_id NOT IN (SELECT id FROM staff_users)
    AND pa.created_at >= date_trunc('week', now()) - (:weeks_ago || ' weeks')::interval
    AND pa.created_at <= date_trunc('week', now())
  GROUP BY action_week
) pa_non_staff ON dr.week_start = pa_non_staff.action_week
LEFT JOIN (
  SELECT
    date_trunc('week', pa.created_at) AS action_week,
    COUNT(*) AS staff_likes_count
  FROM post_actions pa
  WHERE pa.post_action_type_id = 2
    AND pa.user_id IN (SELECT id FROM staff_users)
    AND pa.created_at >= date_trunc('week', now()) - (:weeks_ago || ' weeks')::interval
    AND pa.created_at <= date_trunc('week', now())
  GROUP BY action_week
) pa_staff ON dr.week_start = pa_staff.action_week
LEFT JOIN (
  SELECT
    date_trunc('week', p.created_at) AS post_week,
    COUNT(*) AS posts_per_week
  FROM posts p
  WHERE p.created_at >= date_trunc('week', now()) - (:weeks_ago || ' weeks')::interval
    AND p.created_at <= date_trunc('week', now())
  GROUP BY post_week
) posts_count ON dr.week_start = posts_count.post_week
ORDER BY dr.week_start

興味があれば、Saraのクエリを変更した最終的なプロンプトはこちらです:

2つの日付間の日次いいね数(likes_count)を報告するSQLクエリがありますが、最終的な出力を週単位で集計し、追加の詳細を含めるために、次の拡張を行う必要があります。

  1. スタッフグループの定義:
    • usersテーブルからstaff_usersグループを作成します。ユーザーは、次のいずれかの基準を満たす場合にスタッフと見なされます。
      • admin = true
      • moderator = true
      • trust_level = 4
  2. スタッフと非スタッフによるいいねの分離:
    • 2つの別々の列を追加します。
      • regular_likes_count: 非スタッフユーザーからのいいね数をカウントします。
      • staff_likes_count: スタッフユーザーからのいいね数をカウントします。
    • regular_likes_count列にスタッフユーザーによって生成されたいいね数が含まれないようにします。
  3. 合計いいね数の追加:
    • regular_likes_countstaff_likes_countを合計するtotal_likes列を含めます。
  4. 期間あたりの投稿数の追加:
    • 各週に作成された投稿数をカウントするposts_per_week列を追加します。
  5. 週単位での集計:
    • クエリを変更して、すべてのデータを日次ではなく週次間隔でグループ化します。
    • 各週の開始日を表すweek_start列を含めます。
  6. 過去N週間の制限:
    • :weeks_agoパラメータを導入して、結果を過去N週間に制限します。デフォルト値は52週間(1年)です。
  7. 順序と最終列:
    • 出力がweek_startで並べ替えられ、次の列がこの順序で含まれていることを確認します。
      1. week_start: 週の開始日。
      2. regular_likes_count: 非スタッフユーザーからのいいね数。
      3. staff_likes_count: スタッフユーザーからのいいね数。
      4. total_likes: regular_likes_countstaff_likes_countの合計。
      5. posts_per_week: 週に作成された投稿数。
「いいね!」 2