週に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が説明したように。
関連:
- LATERALとPostgreSQLのサブクエリの違いは何ですか?
- GROUP BYクエリを最適化して、ユーザーごとに最新のレコードを取得します
- 最初に選択各GROUPBYグループの行?
- PostgreSQL:クエリの実行行数'分単位'