PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Aggregieren der zuletzt verbundenen Datensätze pro Woche

Sie benötigen ein Datenelement pro Woche und Ziel (vor der Aggregation der Zählungen pro Unternehmen). Das ist ein einfacher CROSS JOIN zwischen generate_series() und goals . Der (möglicherweise) teure Teil besteht darin, den aktuellen state zu erhalten von updates für jeden. Wie @Paul bereits vorgeschlagen , ein LATERAL join scheint das beste Werkzeug zu sein. Tun Sie dies nur für updates , und verwenden Sie eine schnellere Technik mit LIMIT 1 .

Und vereinfachen Sie die Datumsverwaltung mit 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;

Um dies schnell zu machen Sie benötigen einen mehrspaltigen Index :

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

Absteigende Reihenfolge für created_at ist am besten, aber nicht unbedingt erforderlich. Postgres kann Indizes fast genauso schnell rückwärts scannen. ( Gilt jedoch nicht für umgekehrte Sortierreihenfolge mehrerer Spalten. )

Indexspalten in dass bestellen. Warum?

Und die dritte Spalte status wird nur angehängt, um schnelle Nur-Index-Scans zu ermöglichen auf updates . Verwandter Fall:

1.000 Ziele für 9 Wochen (Ihr Intervall von 2 Monaten überschneidet sich mit mindestens 9 Wochen) erfordern nur 9.000 Indexsuchen für die 2. Tabelle mit nur 1.000 Zeilen. Bei kleinen Tabellen wie dieser sollte die Leistung kein großes Problem darstellen. Aber sobald Sie ein paar Tausend mehr in jeder Tabelle haben, verschlechtert sich die Leistung bei sequentiellen Scans.

w_start stellt den Beginn jeder Woche dar. Die Zählungen beziehen sich daher auf den Wochenstart. Sie können Extrahieren Sie trotzdem Jahr und Woche (oder andere Details, die Ihre Woche darstellen), wenn Sie darauf bestehen:

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

Am besten mit ISOYEAR , wie @Paul erklärt hat.

SQL-Fiddle.

Verwandte: