3

I have a table as follow:

CREATE TABLE counts
(
    T TIMESTAMP NOT NULL,
    C INTEGER NOT NULL
);

I create the following views from it:

CREATE VIEW micounts AS 
SELECT DATE_TRUNC('minute',t) AS t,SUM(c) AS c FROM counts GROUP BY 1;

CREATE VIEW hrcounts AS
SELECT DATE_TRUNC('hour',t) AS t,SUM(c) AS c,SUM(c)/60 AS a
FROM micounts GROUP BY 1;

CREATE VIEW dycounts AS
SELECT DATE_TRUNC('day',t) AS t,SUM(c) AS c,SUM(c)/24 AS a
FROM hrcounts GROUP BY 1;

The problem now comes in when I want to create the monthly counts to know what to divide the daily sums by to get the average column a i.e. the number of days in the specific month.

I know to get the days in PostgreSQL you can do:

SELECT DATE_PART('days',DATE_TRUNC('month',now())+'1 MONTH'::INTERVAL-DATE_TRUNC('month',now()))

But I can't use now(), I have to somehow let it know what the month is when the grouping gets done. Any suggestions i.e. what should replace ??? in this view:

CREATE VIEW mocounts AS
SELECT DATE_TRUNC('month',t) AS t,SUM(c) AS c,SUM(c)/(???) AS a
FROM dycounts
GROUP BY 1;
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
Waslap
  • 572
  • 3
  • 23

1 Answers1

1

A bit shorter and faster and you get the number of days instead of an interval:

SELECT EXTRACT(day FROM date_trunc('month', now()) + interval '1 month'
                                                   - interval '1 day')

It's possible to combine multiple units in a single interval value . So we can use '1 mon - 1 day':

SELECT EXTRACT(day FROM date_trunc('month', now()) + interval '1 mon - 1 day')

(mon, month or months work all the same for month units.)

To divide the daily sum by the number of days in the current month (orig. question):

SELECT t::date AS the_date
     , SUM(c)  AS c
     , SUM(c) / EXTRACT(day FROM date_trunc('month', t::date)
                               + interval '1 mon - 1 day') AS a
FROM   dycounts
GROUP  BY 1;

To divide monthly sum by the number of days in the current month (updated question):

SELECT DATE_TRUNC('month', t)::date AS t
      ,SUM(c) AS c
      ,SUM(c) / EXTRACT(day FROM date_trunc('month', t)::date
                               + interval '1 mon - 1 day') AS a
FROM   dycounts
GROUP  BY 1;

You have to repeat the GROUP BY expression if you want to use a single query level.

Or use a subquery:

SELECT *, c / EXTRACT(day FROM t + interval '1 mon - 1 day') AS a
FROM  (
   SELECT date_trunc('month', t)::date AS t, SUM(c) AS c
   FROM   dycounts
   GROUP  BY 1
   ) sub;
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
  • Thanks a lot for taking time to answer. I had a typo in my original question (date_trunc in mocount was day instead of month) which i believe has a material affect on your answer. t::date as day was the same as date_trunc('day'...) and i can therefore understand why you proposed it but after my corrected question, your answer is not entirely correct and i can't get your answer to work either by tinkering with my apparently limited knowledge. Would you mind re-evaluating your answer ? – Waslap Oct 08 '13 at 09:21
  • 1
    CREATE VIEW mocounts AS SELECT DATE_TRUNC('month',t)::date AS t,SUM(c) AS c,SUM(c)/(EXTRACT(day FROM (DATE_TRUNC('month', DATE_TRUNC('month',t)::date + interval '1 month'))::date - 1)) AS a FROM dycounts GROUP BY 1; – Waslap Oct 08 '13 at 09:26
  • @Waslap: Exactly. I found another improvement, though. Consider updated answer. – Erwin Brandstetter Oct 08 '13 at 15:18