sql >> Databasteknik >  >> RDS >> PostgreSQL

Aggregerar de senaste sammanslagna posterna per vecka

Du behöver en datapost per vecka och mål (innan du sammanställer antal per företag). Det är en vanlig CROSS JOIN mellan generate_series() och goals . Den (möjligen) dyra delen är att få det nuvarande state från updates för varje. Som @Paul redan föreslagit , en LATERAL gå med verkar vara det bästa verktyget. Gör det bara för updates , dock, och använd en snabbare teknik med LIMIT 1 .

Och förenkla datumhanteringen med 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;

För att göra detta snabbt du behöver ett flerkolumnindex :

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

Fallande ordning för created_at är bäst, men inte absolut nödvändigt. Postgres kan skanna index bakåt nästan exakt lika snabbt. ( Inte tillämpligt för inverterad sorteringsordning för flera kolumner, dock. )

Indexera kolumner i det ordning. Varför?

Och den tredje kolumnen status är endast tillagd för att tillåta snabba endast indexsökningarupdates . Relaterat fall:

1 000 mål under 9 veckor (ditt intervall på 2 månader överlappar med minst 9 veckor) kräver bara 9 000 indexuppslag för den andra tabellen med endast 1 000 rader. För små bord som detta borde prestanda inte vara några större problem. Men när du väl har ett par tusen fler i varje tabell kommer prestandan att försämras med sekventiella skanningar.

w_start representerar början av varje vecka. Följaktligen gäller räkningar för början av veckan. Du kan extrahera fortfarande år och vecka (eller andra detaljer representerar din vecka), om du insisterar:

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

Bäst med ISOYEAR , som @Paul förklarade.

SQL-fiol.

Relaterat:



  1. Lista alla nullbara kolumner i en SQL Server-databas

  2. Skapar DBMS_SCHEDULER-jobb för Oracle

  3. timeoutperioden har gått. alla poolade anslutningar användes och maximal poolstorlek nåddes

  4. 2 sätt att lista tabellerna i en SQLite-databas