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

Seleziona gruppi di utenti distinti per intervallo di tempo

Conta tutti righe

SELECT date, '1_D' AS time_series,  count(DISTINCT user_id) AS cnt
FROM   uniques
GROUP  BY 1

UNION  ALL
SELECT DISTINCT ON (1)
       date, '2_W', count(*) OVER (PARTITION BY week_beg ORDER BY date)
FROM   uniques

UNION  ALL
SELECT DISTINCT ON (1)
       date, '3_M', count(*) OVER (PARTITION BY month_beg ORDER BY date)
FROM   uniques
ORDER  BY 1, time_series
  • Le tue colonne week_beg e month_beg sono ridondanti al 100% e possono essere facilmente sostituiti dadate_trunc('week', date + 1) - 1 e date_trunc('month', date) rispettivamente.

  • La tua settimana sembra iniziare di domenica (in meno di uno), quindi il + 1 .. - 1 .

  • Il frame predefinito di una funzione finestra con ORDER BY nel OVER la clausola utilizza è RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW . È esattamente ciò di cui hai bisogno.

  • Usa UNION ALL , non UNION .

  • La tua sfortunata scelta per time_series (D, W, M) non ordina bene, l'ho rinominato per rendere l'ultimo ORDER BY più facile.

  • Questa query può gestire più righe al giorno. I conteggi includono tutti i peer per un giorno.

  • Ulteriori informazioni su DISTINCT ON :

Utenti DISTINTI al giorno

Per contare ogni utente solo una volta al giorno, utilizza un CTE con DISTINCT ON :

WITH x AS (SELECT DISTINCT ON (1,2) date, user_id FROM uniques)
SELECT date, '1_D' AS time_series,  count(user_id) AS cnt
FROM   x
GROUP  BY 1

UNION ALL
SELECT DISTINCT ON (1)
       date, '2_W'
      ,count(*) OVER (PARTITION BY (date_trunc('week', date + 1)::date - 1)
                      ORDER BY date)
FROM   x

UNION ALL
SELECT DISTINCT ON (1)
       date, '3_M'
      ,count(*) OVER (PARTITION BY date_trunc('month', date) ORDER BY date)
FROM   x
ORDER BY 1, 2

Distingue gli utenti in un periodo di tempo dinamico

Puoi sempre ricorrere a sottoquery correlate . Tendono ad essere lenti con i tavoli grandi!
Sulla base delle query precedenti:

WITH du AS (SELECT date, user_id FROM uniques GROUP BY 1,2)
    ,d  AS (
    SELECT date
          ,(date_trunc('week', date + 1)::date - 1) AS week_beg
          ,date_trunc('month', date)::date AS month_beg
    FROM   uniques
    GROUP  BY 1
    )
SELECT date, '1_D' AS time_series,  count(user_id) AS cnt
FROM   du
GROUP  BY 1

UNION ALL
SELECT date, '2_W', (SELECT count(DISTINCT user_id) FROM du
                     WHERE  du.date BETWEEN d.week_beg AND d.date )
FROM   d
GROUP  BY date, week_beg

UNION ALL
SELECT date, '3_M', (SELECT count(DISTINCT user_id) FROM du
                     WHERE  du.date BETWEEN d.month_beg AND d.date)
FROM   d
GROUP  BY date, month_beg
ORDER  BY 1,2;

SQL Fiddle per tutte e tre le soluzioni.

Più veloce con dense_rank()

@Clodoaldo ha apportato un notevole miglioramento:utilizzare la funzione finestra dense_rank() . Ecco un'altra idea per una versione ottimizzata. Dovrebbe essere ancora più veloce escludere immediatamente i duplicati giornalieri. Il guadagno in termini di prestazioni aumenta con il numero di righe al giorno.

Basandosi su un modello di dati semplificato e sanificato - senza le colonne ridondanti- day come nome della colonna invece di date

date è una parola riservata in SQL standard e un nome di tipo di base in PostgreSQL e non dovrebbe essere usato come identificatore.

CREATE TABLE uniques(
   day date     -- instead of "date"
  ,user_id int
);

Query migliorata:

WITH du AS (
   SELECT DISTINCT ON (1, 2)
          day, user_id 
         ,date_trunc('week',  day + 1)::date - 1 AS week_beg
         ,date_trunc('month', day)::date         AS month_beg
   FROM   uniques
   )
SELECT day, count(user_id) AS d, max(w) AS w, max(m) AS m
FROM  (
    SELECT user_id, day
          ,dense_rank() OVER(PARTITION BY week_beg  ORDER BY user_id) AS w
          ,dense_rank() OVER(PARTITION BY month_beg ORDER BY user_id) AS m
    FROM   du
    ) s
GROUP  BY day
ORDER  BY day;

SQL Fiddle dimostrando le prestazioni di 4 varianti più veloci. Dipende dalla distribuzione dei dati che è più veloce per te.
Tutti sono circa 10 volte più veloci della versione delle sottoquery correlate (il che non è male per le sottoquery correlate).