The challenge is to get the number of months between now and the next birthday interval. The MONTHS_BETWEEN()
function combined with dividing by 12 gets us close, but we have to deal with the decimal dust and convert the remainder to something useful. I can think of two ways to deal with that: MOD()
and CEIL()
.
Using CEIL
we would take the difference between the age-to-be on the next birthday and the present age and use that in the predicate. In this case the result is in decimal years, so we need to compare to a quarter year (3/12):
where (ceil(months_between(sysdate, b.birthday)/12) -
months_between(sysdate, b.birthday)/12) <= 3/12;
That's a bit clunky. Alternatively, MOD
would give us the remainder of months to go, so subtracting MOD
from 12 gives us the months left to go:
where (12 - mod(months_between(sysdate, b.birthday),12)) <= 3;
Here it is in action with sample data:
with b as
(select date '1990-05-10' birthday from dual union all
select date '1970-09-23' from dual union all
select date '2000-04-02' from dual union all
select date '1948-11-12' from dual)
select
b.birthday,
months_between(sysdate, b.birthday) months,
months_between(sysdate, b.birthday)/12 years,
ceil(months_between(sysdate, b.birthday)/12) age_to_be,
ceil(months_between(sysdate, b.birthday)/12) - months_between(sysdate, b.birthday)/12 period_to_birthday,
mod(months_between(sysdate, b.birthday),12) mod_months,
12 - mod(months_between(sysdate, b.birthday),12) months_diff
from b
where (12 - mod(months_between(sysdate, b.birthday),12)) <= 3;
-- Or
--where (ceil(months_between(sysdate, b.birthday)/12) -
-- months_between(sysdate, b.birthday)/12) <= 3/12;
EDIT: I'll leave my old and incorrect answer here for reference and I'll just look the other way.
Just use ADD_MONTHS()
and LAST_DAY()
to find all rows where the birthday is inside of a calculated value. No need to worry about the year component, you are not working with string literals.
This example casts sysdate
as a timestamp
, then gets the last day of the current month and adds three months to that:
select add_months(last_day(cast(sysdate as timestamp)),3) from dual;
So using that logic, you can:
select *
from your_table
where birthday <= add_months(last_day(cast(sysdate as timestamp)),3);
Or just:
select *
from your_table
where birthday <= add_months(last_day(sysdate),3);
Oracle nicely handles the data type conversion for you.