sql >> データベース >  >> RDS >> PostgreSQL

週ごとに最近参加したレコードを集計する

    週に1つのデータ項目と目標が必要です(会社ごとのカウントを集計する前に)。それは単純な<​​code>CROSSJOIN generate_series()の間 および目標 。 (おそらく)高価な部分は、現在の状態を取得することです アップデートから それぞれのために。 @Paulがすでに提案している のように 、 LATERAL 参加は最高のツールのようです。 更新に対してのみ実行してください ただし、 LIMIT 1でより高速な手法を使用します 。

    また、 <を使用して、日付の処理を簡素化します。 code> date_trunc()

    SELECT w_start
         , g.company_id
         , count(*) FILTER (WHERE u.status = 'green') AS green_count
         , count(*) FILTER (WHERE u.status = 'amber') AS amber_count
         , count(*) FILTER (WHERE u.status = 'red')   AS red_count
    FROM   generate_series(date_trunc('week', NOW() - interval '2 months')
                         , date_trunc('week', NOW())
                         , interval '1 week') w_start
    CROSS  JOIN goals g
    LEFT   JOIN LATERAL (
       SELECT status
       FROM   updates
       WHERE  goal_id = g.id
       AND    created_at < w_start
       ORDER  BY created_at DESC
       LIMIT  1
       ) u ON true
    GROUP  BY w_start, g.company_id
    ORDER  BY w_start, g.company_id;
    

    これを高速にするには 複数列のインデックスが必要です :

    CREATE INDEX updates_special_idx ON updates (goal_id, created_at DESC, status);
    

    created_atの降順 最善ですが、厳密に必要というわけではありません。 Postgresは、ほぼ正確に同じ速度でインデックスを逆方向にスキャンできます。 (ただし、複数列の逆ソート順には適用されません。

    thatのインデックス列 注文。なぜですか?

    そして3番目の列のstatus 高速なインデックスのみのスキャン を可能にするためにのみ追加されます 更新 。関連事例:

    9週間の1,000の目標(2か月の間隔は少なくとも9週間と重複します)は、1,000行のみの2番目のテーブルに対して9kのインデックスルックアップのみを必要とします。このような小さなテーブルの場合、パフォーマンスはそれほど問題にはなりません。ただし、各テーブルに数千を追加すると、シーケンシャルスキャンでパフォーマンスが低下します。

    w_start 毎週の始まりを表します。したがって、カウントは週の初めのものです。 できます あなたが主張するならば、それでも年と週を抽出します(または他の詳細はあなたの週を表します):

       EXTRACT(isoyear from w_start) AS year
     , EXTRACT(week    from w_start) AS week
    

    ISOYEAR> 、@Paulが説明したように。

    SQLフィドル。

    関連:



    1. sqliteは2MBを超えるフィールドを取得します

    2. データベースルーターを使用したアプリDjango間での(mysql)データベースの共有

    3. MySQLの保存期間-データ型?

    4. Oracleで変数を宣言して表示する方法