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

Forespørgsel efter optælling af distinkte værdier i et rullende datointerval

Testtilfælde:

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

Forespørgsel - returnerer kun dage, hvor der findes en post 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 - returner alle dage i det angivne område, selvom der ikke er nogen rækker for 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<>spil her

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

Dette lød som et job til vinduesfunktioner i starten, men jeg fandt ikke en måde at definere den passende vinduesramme på. Også pr. dokumentation:

Aggregerede vinduesfunktioner tillader i modsætning til normale aggregerede funktioner ikke DISTINCT eller ORDER BY skal bruges i funktionsargumentlisten.

Så jeg løste det med korrelerede underforespørgsler i stedet for. Det er vist den smarteste måde.

BTW, "mellem nævnte dato og 3 dage siden" ville være en periode på 4 dage. Der er din definition selvmodsigende.

Lidt kortere, men langsommere i nogle dage:

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;

Relateret:

  • Generering af tidsserier mellem to datoer i PostgreSQL
  • Rullende antal rækker inden for tidsinterval


  1. SQL Server, den vildledende XLOCK &optimeringer

  2. Tjek om databasen findes i PostgreSQL ved hjælp af shell

  3. SQL kommandoer

  4. Android SQLite-opdateringsrækken virker ikke