PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Eseguire una query per il conteggio di valori distinti in un intervallo di date in sequenza

Caso di prova:

CREATE TABLE tbl (date date, email text);
INSERT INTO tbl VALUES
  ('2012-01-01', '[email protected]')
, ('2012-01-01', '[email protected]')
, ('2012-01-01', '[email protected]')
, ('2012-01-02', '[email protected]')
, ('2012-01-02', '[email protected]')
, ('2012-01-03', '[email protected]')
, ('2012-01-04', '[email protected]')
, ('2012-01-05', '[email protected]')
, ('2012-01-05', '[email protected]')
, ('2012-01-06', '[email protected]')
, ('2012-01-06', '[email protected]')
, ('2012-01-06', '[email protected]`')
;

Query:restituisce solo i giorni in cui esiste una voce in tbl :

SELECT date
     ,(SELECT count(DISTINCT email)
       FROM   tbl
       WHERE  date BETWEEN t.date - 2 AND t.date -- period of 3 days
      ) AS dist_emails
FROM   tbl t
WHERE  date BETWEEN '2012-01-01' AND '2012-01-06'  
GROUP  BY 1
ORDER  BY 1;

Oppure - restituisci tutti i giorni nell'intervallo specificato, anche se non ci sono righe per il giorno:

SELECT date
     ,(SELECT count(DISTINCT email)
       FROM   tbl
       WHERE  date BETWEEN g.date - 2 AND g.date
      ) AS dist_emails
FROM  (SELECT generate_series(timestamp '2012-01-01'
                            , timestamp '2012-01-06'
                            , interval  '1 day')::date) AS g(date);

db<>gioca qui

Risultato:

day        | dist_emails
-----------+------------
2012-01-01 | 3
2012-01-02 | 3
2012-01-03 | 3
2012-01-04 | 3
2012-01-05 | 1
2012-01-06 | 2

Questo suonava come un lavoro per le funzioni della finestra all'inizio, ma non ho trovato un modo per definire la cornice della finestra adatta. Inoltre, per documentazione:

Le funzioni di aggregazione della finestra, a differenza delle normali funzioni di aggregazione, non consentono DISTINCT o ORDER BY da utilizzare nell'elenco degli argomenti della funzione.

Quindi l'ho risolto con subquery correlate. Immagino sia il modo più intelligente.

A proposito, "tra detta data e 3 giorni fa" sarebbe un periodo di 4 giorni. La tua definizione è contraddittoria lì.

Leggermente più breve, ma più lento per alcuni giorni:

SELECT g.date, count(DISTINCT email) AS dist_emails
FROM  (SELECT generate_series(timestamp '2012-01-01'
                            , timestamp '2012-01-06'
                            , interval  '1 day')::date) AS g(date)
LEFT   JOIN tbl t ON t.date BETWEEN g.date - 2 AND g.date
GROUP  BY 1
ORDER  BY 1;

Correlati:

  • Generazione di serie temporali tra due date in PostgreSQL
  • Numero progressivo di righe entro l'intervallo di tempo