PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Zapytanie o liczbę różnych wartości w kroczącym zakresie dat

Przypadek testowy:

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

Zapytanie - zwraca tylko dni, w których wpis istnieje w 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;

Lub - zwróć wszystkie dni w określonym zakresie, nawet jeśli nie ma wierszy dla danego dnia:

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<>graj tutaj

Wynik:

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

To brzmiało jak praca dla funkcji okien na początku, ale nie znalazłem sposobu na zdefiniowanie odpowiedniej ramy okna. Ponadto, zgodnie z dokumentacją:

Funkcje okna agregującego, w przeciwieństwie do normalnych funkcji agregujących, nie pozwalają na DISTINCT lub ORDER BY do użycia na liście argumentów funkcji.

Więc rozwiązałem to za pomocą skorelowanych podzapytań. Myślę, że to najmądrzejszy sposób.

BTW, „między wspomnianą datą a 3 dniami temu” byłby okresem 4 dni. Twoja definicja jest tam sprzeczna.

Nieco krócej, ale wolniej przez kilka dni:

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;

Powiązane:

  • Generowanie szeregów czasowych między dwiema datami w PostgreSQL
  • Rocząca liczba wierszy w przedziale czasowym


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Aktualny stan zarządzania kopiami zapasowymi Open Source dla PostgreSQL

  2. Zoptymalizuj maksymalne zapytanie grupowe

  3. Pobieranie listy dat w zakresie w PostgreSQL

  4. Jaki jest najbardziej elegancki sposób przechowywania znacznika czasu za pomocą nanosec w postgresql?

  5. [Wideo] Ansible i PostgreSQL