sql >> Databáze >  >> RDS >> PostgreSQL

Dotaz na počet různých hodnot v pohyblivém časovém období

Testovací případ:

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

Dotaz - vrací pouze dny, kdy v tbl existuje záznam :

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;

Nebo - vrátit všechny dny v zadaném rozsahu, i když pro daný den nejsou žádné řádky:

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<>zde hrajte

Výsledek:

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 znělo jako práce pro okenní funkce, ale nenašel jsem způsob, jak definovat vhodný okenní rám. Také podle dokumentace:

Agregační funkce okna, na rozdíl od normálních agregačních funkcí, neumožňují DISTINCT nebo ORDER BY k použití v seznamu argumentů funkcí.

Takže jsem to místo toho vyřešil korelovanými poddotazy. Myslím, že to je nejchytřejší způsob.

BTW, „mezi uvedeným datem a 3 dny“ by bylo období 4 dní. Vaše definice je v rozporu.

O něco kratší, ale na pár dní pomalejší:

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;

Související:

  • Generování časových řad mezi dvěma daty v PostgreSQL
  • Klouzavý počet řádků za časový interval


  1. Vícenásobné indexy vs vícesloupcové indexy

  2. Porovnání datových typů data a času na serveru SQL

  3. Nelze se přihlásit k SQL Server + SQL Server Authentication + Chyba:18456

  4. Jak vypočítat procento růstu po měsíci v MySQL