sql >> Base de Datos >  >> RDS >> PostgreSQL

Agregar los registros unidos más recientes por semana

Necesita un elemento de datos por semana y objetivo (antes de agregar recuentos por empresa). Eso es un simple CROSS JOIN entre generate_series() y goals . La parte (posiblemente) costosa es obtener el state actual de updates para cada. Me gusta @Paul ya sugirió , un LATERAL join parece la mejor herramienta. Hazlo solo para updates , sin embargo, y use una técnica más rápida con LIMIT 1 .

Y simplifique el manejo de fechas con 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;

Para hacer esto rápido necesitas un índice de varias columnas :

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

Orden descendente para created_at es lo mejor, pero no estrictamente necesario. Postgres puede escanear índices hacia atrás casi exactamente igual de rápido. ( Sin embargo, no se aplica al orden invertido de varias columnas. )

Índice de columnas en eso ordenar. ¿Por qué?

Y la tercera columna status solo se agrega para permitir rápidos escaneos de solo índice en updates . Caso relacionado:

Los objetivos de 1k durante 9 semanas (su intervalo de 2 meses se superpone con al menos 9 semanas) solo requieren búsquedas de índice de 9k para la segunda tabla de solo 1k filas. Para mesas pequeñas como esta, el rendimiento no debería ser un gran problema. Pero una vez que tenga un par de miles más en cada tabla, el rendimiento se deteriorará con los análisis secuenciales.

w_start representa el comienzo de cada semana. En consecuencia, los conteos son para el inicio de la semana. Tu puedes aún extraiga el año y la semana (o cualquier otro detalle que represente su semana), si insiste:

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

Mejor con ISOYEAR , como explicó @Paul.

SQL Fiddle.

Relacionado: