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

Vælg distinkt brugergruppe efter tidsinterval

Tæl alle rækker

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
  • Dine kolonner week_beg og month_beg er 100 % redundante og kan nemt erstattes afdate_trunc('week', date + 1) - 1 og date_trunc('month', date) hhv.

  • Din uge ser ud til at starte på søndag (af én), derfor + 1 .. - 1 .

  • standardrammen for en vinduesfunktion med ORDER BY i OVER klausulens brug er RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW . Det er præcis, hvad du har brug for.

  • Brug UNION ALL , ikke UNION .

  • Dit uheldige valg til time_series (D, W, M) sorterer ikke godt, jeg omdøbte for at lave den endelige ORDER BY lettere.

  • Denne forespørgsel kan omhandle flere rækker pr. dag. Optællinger inkluderer alle jævnaldrende for en dag.

  • Mere om DISTINCT ON :

DISTINKTE brugere pr. dag

For kun at tælle hver bruger én gang om dagen, brug 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

SÆRLIGE brugere over en dynamisk periode

Du kan altid ty til korrelerede underforespørgsler . Har tendens til at være langsom med store borde!
Bygger på de tidligere forespørgsler:

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 for alle tre løsninger.

Hurtigere med dense_rank()

@Clodoaldo kom med en større forbedring:brug vinduefunktionen dense_rank() . Her er endnu en idé til en optimeret version. Det burde være endnu hurtigere at udelukke daglige dubletter med det samme. Ydelsesgevinsten vokser med antallet af rækker pr. dag.

Bygger på en forenklet og renset datamodel - uden de redundante kolonner- day som kolonnenavn i stedet for date

date er et reserveret ord i standard SQL og et grundlæggende typenavn i PostgreSQL og bør ikke bruges som identifikator.

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

Forbedret forespørgsel:

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 demonstrerer ydeevnen af ​​4 hurtigere varianter. Det afhænger af din datadistribution, hvilken der er hurtigst for dig.
De er alle omkring 10 gange så hurtige som versionen af ​​korrelerede underforespørgsler (hvilket ikke er dårligt for korrelerede underforespørgsler).



  1. Hvordan udtrækkes to på hinanden følgende cifre fra et tekstfelt i MySQL?

  2. Fremdriv:valg af kolonner fra aliasede jointabeller

  3. MySQL Cross Table Constraint

  4. En anden #1054:Ukendt kolonne i 'feltliste'-mysteriet