sql >> Database >  >> RDS >> PostgreSQL

Query voor het tellen van verschillende waarden in een voortschrijdend datumbereik

Testgeval:

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

Query - retourneert alleen dagen waarop een item bestaat in 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;

Of - retourneer alle dagen binnen het opgegeven bereik, zelfs als er geen rijen zijn voor de dag:

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<>viool hier

Resultaat:

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

Dit klonk als een baan voor raamfuncties in het begin, maar ik vond geen manier om het geschikte raamkozijn te definiëren. Ook, per documentatie:

Geaggregeerde vensterfuncties, in tegenstelling tot normale aggregatiefuncties, staan ​​DISTINCT niet toe of ORDER BY te gebruiken in de lijst met functieargumenten.

Dus heb ik het in plaats daarvan opgelost met gecorreleerde subquery's. Ik denk dat dat de slimste manier is.

Trouwens, "tussen genoemde datum en 3 dagen geleden" zou een periode zijn van 4 dagen. Je definitie is daar tegenstrijdig.

Iets korter, maar een paar dagen langzamer:

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;

Gerelateerd:

  • Tijdreeksen genereren tussen twee datums in PostgreSQL
  • Rollend aantal rijen binnen tijdsinterval


  1. Voordelen en nadelen van het gebruik van opgeslagen procedures

  2. Hoe kom je aan het einde van een dag?

  3. ASP Classic SQL Query-foutbericht, juiste syntaxis alstublieft

  4. Gebruik IDENT_CURRENT() om de huidige identiteitswaarde op een identiteitskolom in SQL Server te retourneren