1

I am currently programming an SQL view which should provide a count of a populated field for a particular month.

This is how I would like the view to be constructed:

Country   | (Current Month - 12) Eg Feb 2011 | (Current Month - 11) | (Current Month - 10)
----------|----------------------------------|----------------------|---------------------
UK        | 10                               | 11                   | 23

The number under the month should be a count of all populated fields for a particular country. The field is named eldate and is a date (cast as a char) of format 10-12-2011. I want the count to only count dates which match the month.

So column "Current Month - 12" should only include a count of dates which fall within the month which is 12 months before now. Eg Current Month - 12 for UK should include a count of dates which fall within February-2011.

I would like the column headings to actually reflect the month it is looking at so:

Country | Feb 2011 | March 2011 | April 2011
--------|----------|------------|------------
UK      | 4        | 12         | 0

So something like:

SELECT c.country_name,
        (SELECT COUNT("C1".eldate) FROM "C1" WHERE "C1".eldate LIKE %NOW()-12 Months% AS NOW() - 12 Months
        (SELECT COUNT("C1".eldate) FROM "C1" WHERE "C1".eldate LIKE %NOW()-11 Months% AS NOW() - 11 Months
FROM country AS c
INNER JOIN "site" AS s using (country_id)
INNER JOIN "subject_C1" AS "C1" ON "s"."site_id" = "C1"."site_id"

Obviously this doesn't work but just to give you an idea of what I am getting at.

Any ideas?

Thank you for your help, any more queries please ask.

2
  • 1
    I don't know if you can achieve this with a view but you could use a function. Notice in PostgreSQL you can call a function like this: SELECT * FROM funName() Commented Feb 27, 2012 at 15:12
  • Yeah I thought it would probably be the way, however, I am a novice and not sure how I would go about constructing a function to do this Commented Feb 27, 2012 at 15:17

2 Answers 2

2

My first inclination is to produce this table:

+---------+-------+--------+
| Country | Month | Amount |
+---------+-------+--------+
| UK      | Jan   | 4      |
+---------+-------+--------+
| UK      | Feb   | 12     |
+---------+-------+--------+

etc. and pivot it. So you'd start with (for example):

SELECT 
  c.country, 
  EXTRACT(MONTH FROM s.eldate) AS month, 
  COUNT(*) AS amount
FROM country AS c
JOIN site AS s ON s.country_id = c.id
WHERE 
  s.eldate > NOW() - INTERVAL '1 year'
GROUP BY c.country, EXTRACT(MONTH FROM s.eldate);

You could then plug that into one the crosstab functions from the tablefunc module to achieve the pivot, doing something like this:

SELECT * 
FROM crosstab('<query from above goes here>') 
  AS ct(country varchar, january integer, february integer, ... december integer);
Sign up to request clarification or add additional context in comments.

2 Comments

I'm not sure I understand what you mean but thanks for the help
@AydinHassan what's the hangup?
1

You could truncate the dates to make the comparable:

WHERE date_trunc('month', eldate) = date_trunc('month', now()) - interval '12 months'

UPDATE

This kind of replacement for your query:

(SELECT COUNT("C1".eldate) FROM "C1" WHERE date_trunc('month', "C1".eldate) =
    date_trunc('month', now()) - interval '12 months') AS TWELVE_MONTHS_AGO

But that would involve a scan of the table for each month, so you could do a single scan with something more along these lines:

SELECT SUM( CASE WHEN date_trunc('month', "C1".eldate) =  date_trunc('month', now()) - interval '12 months' THEN 1 ELSE 0 END ) AS TWELVE_MONTHS_AGO
      ,SUM( CASE WHEN date_trunc('month', "C1".eldate) =  date_trunc('month', now()) - interval '11 months' THEN 1 ELSE 0 END ) AS ELEVEN_MONTHS_AGO
...

or do a join with a table of months as others are showing.

UPDATE2

Further to the comment on fixing the columns from Jan to Dec, I was thinking something like this: filter on the last years worth of records, then sum on the appropriate month. Perhaps like this:

SELECT SUM( CASE WHEN EXTRACT(MONTH FROM "C1".eldate) = 1 THEN 1 ELSE 0 END ) AS JAN
      ,SUM( CASE WHEN EXTRACT(MONTH FROM "C1".eldate) = 2 THEN 1 ELSE 0 END ) AS FEB
      ...

  WHERE date_trunc('month', "C1".eldate) <  date_trunc('month', now())
    AND date_trunc('month', "C1".eldate) >= date_trunc('month', now()) - interval '12 months'

3 Comments

Can you explain further please?
Okay this works good thanks, but is there no way to actually dynamically name the columns?
@AydinHassan Not that I am aware of. In this case I would lean to fixing the columns (JAN - DEC), then do a bit of math involving now() - jan, now() - feb, etc rather than the hard-coded -12, -11, ... There would need to be some handling of year boundaries, but that is probably some min/max stuff.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.