sql >> Databasteknik >  >> RDS >> PostgreSQL

Välj distinkt användargrupp efter tidsintervall

Räkna alla rader

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
  • Dina kolumner week_beg och month_beg är 100 % redundanta och kan enkelt ersättas meddate_trunc('week', date + 1) - 1 och date_trunc('month', date) respektive.

  • Din vecka verkar börja på söndag (av ett), därför + 1 .. - 1 .

  • standardramen för en fönsterfunktion med ORDER BY i OVER satsanvändning är RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW . Det är precis vad du behöver.

  • Använd UNION ALL , inte UNION .

  • Ditt olyckliga val för time_series (D, W, M) sorterar inte bra, jag bytte namn för att göra den sista ORDER BY lättare.

  • Den här frågan kan hantera flera rader per dag. Räknar inkluderar alla kamrater för en dag.

  • Mer om DISTINCT ON :

DISTINKT användare per dag

För att bara räkna varje användare en gång per dag, använd en CTE med 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

STYRKA användare över en dynamisk tidsperiod

Du kan alltid använda korrelerade underfrågor . Brukar vara långsam med stora bord!
Bygg på de tidigare frågorna:

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 för alla tre lösningarna.

Snabbare med dense_rank()

@Clodoaldo kom med en stor förbättring:använd fönsterfunktionen dense_rank() . Här är en annan idé för en optimerad version. Det borde gå ännu snabbare att utesluta dagliga dubbletter direkt. Prestandaökningen växer med antalet rader per dag.

Bygger på en förenklad och sanerad datamodell - utan de redundanta kolumnerna- day som kolumnnamn istället för date

date är ett reserverat ord i standard SQL och ett grundläggande typnamn i PostgreSQL och bör inte användas som identifierare.

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

Förbättrad fråga:

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 demonstrerar prestandan hos 4 snabbare varianter. Det beror på din datadistribution vilken som är snabbast för dig.
Alla är ungefär 10 gånger så snabba som versionen av korrelerade underfrågor (vilket inte är dåligt för korrelerade underfrågor).



  1. Hur man beställer efter skiftlägesokänslig ASC eller DESC, med DISTINCT och UNION

  2. Uppdatera delsträng för en kolumn

  3. Hämta varbinary(MAX) från SQL Server till byte[] i C#

  4. Hur kan sanitet som undkommer enstaka citattecken besegras av SQL-injektion i SQL Server?