Community Wiki because it is borrowing heavily from other answers.
The answer by Farhad Jabiyev (now deleted — see Is it OK to unaccept an accepted answer after weeks) was:
SELECT STATE_CODE, SUM(COLA) SUM_COLA, SUM(COLB) SUM_COLB
FROM TABLE_NAME
WHERE to_date(ExtractDay(sysdate) || MONTH || YEAR, 'ddMMyyyy')
between add_months(sysdate,-12) AND add_months(sysdate,-24)
GROUP BY STATE_CODE;
This answer runs into problems when the current date is at the end of the month for some intervals (with the -24..-12 month range, usually only on a Leap Day, but for other ranges of months, it can run into problems from the 29th of the month onwards).
However, it is firmly on the right track and only needs (relatively) trivial fixing. The requirement is to get the year/month combination for the current month minus 12 months and the current month minus 24 months.
- Change the 'day of month' from
ExtractDay(sysdate)
to'01'
of month (since the data stored only has one month granularity and every month has a first of the month, but not every month has a 29th, 30th or 31st).
If you use dates, then you need to get the first day of the current month (twice) which is verbose:
SELECT STATE_CODE, SUM(COLA) SUM_COLA, SUM(COLB) SUM_COLB
FROM TABLE_NAME
WHERE to_date('01' || MONTH || YEAR, 'ddMMyyyy')
BETWEEN to_date('01' || ExtractMonth(add_months(sysdate, -24)) ||
ExtractYear(add_months(sysdate, -24)), 'ddMMyyyy')
AND to_date('01' || ExtractMonth(add_months(sysdate, -12)) ||
ExtractYear(add_months(sysdate, -12)), 'ddMMyyyy')
GROUP BY STATE_CODE;
Alternatively, and probably better (if only because simpler) is to convert the year/month combination as suggested by Gordon Linoff in his answer:
SELECT STATE_CODE, SUM(COLA) SUM_COLA, SUM(COLB) SUM_COLB
FROM TABLE_NAME
WHERE (MONTH + 100 * YEAR)
BETWEEN (ExtractMonth(add_months(sysdate, -24)) + 100 *
ExtractYear(add_months(sysdate, -24)))
AND (ExtractMonth(add_months(sysdate, -12)) + 100 *
ExtractYear(add_months(sysdate, -12))
GROUP BY STATE_CODE;
Some residual issues to note:
- The standard behaviour of x BETWEEN y AND z requires that y is less than or equal to z. That is,
x BETWEEN y AND z
is equivalent tox >= y AND x <= z
. (Is there any DBMS that treats it as equivalent tox >= MIN(y, z) AND z <= MAX(y, z)
?) Hence the code above switches -12 and -24 so the the range is correct. - The bounds of x BETWEEN y AND z are inclusive, so the -24 to -12 range covers 13 months, not 12 months. As long as that's what you want, it's fine. Otherwise, you probably need -24..-13 or maybe -23..-12.
- You may be able to use functions
YEAR()
andMONTH()
in lieu ofExtractYear()
andExtractMonth()
. You might need to worry about ambiguity between column namesYear
andMonth
and functions of the same name. Whether this matters will depend on the DBMS.
This question does highlight that date arithmetic is tricky stuff, and leap years and ends of months always have to be kept in mind when subtracting months from dates. For example, what date corresponds to 2 months before 2014-04-29, 2014-04-30, 2014-08-31, 2016-04-29, 2016-04-30?
Warning: untested SQL: syntax errors are possible.