sql >> Databasteknik >  >> RDS >> PostgreSQL

Fråga efter antalet distinkta värden i ett rullande datumintervall

Testfall:

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]`')
;

Fråga - returnerar endast dagar där en post finns i 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;

Eller - returnera alla dagar i det angivna intervallet, även om det inte finns några rader för dagen:

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<>spela här

Resultat:

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

Detta lät som ett jobb för fönsterfunktioner till en början, men jag hittade inte ett sätt att definiera den lämpliga fönsterramen. Dessutom, per dokumentation:

Aggregerade fönsterfunktioner tillåter, till skillnad från vanliga aggregerade funktioner, inte DISTINCT eller ORDER BY som ska användas i listan över funktionsargument.

Så jag löste det med korrelerade underfrågor istället. Jag antar att det är det smartaste sättet.

BTW, "mellan nämnda datum och 3 dagar sedan" skulle vara en period på 4 dagar. Din definition är motsägelsefull där.

Något kortare, men långsammare i några dagar:

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;

Relaterat:

  • Genererar tidsserier mellan två datum i PostgreSQL
  • Rullande antal rader inom tidsintervallet


  1. Anslutningsproblem med SQLAlchemy och flera processer

  2. Hur man skapar en ny användare och beviljar behörigheter i MySQL

  3. 2 sätt att ersätta en delsträng i MariaDB

  4. Postgres-trigger efter infogning åtkomst till NEW