В случае необходимости функция age позволит вам обойти високосные годы:
where age(cd.birthdate) - (extract(year from age(cd.birthdate)) || ' years')::interval = '0'::interval
В случае, если вам нужна производительность, вы можете фактически обернуть вышеупомянутое с произвольной начальной точкой (например, 'epoch'::date
) в функцию и использовать индекс для нее:
create or replace function day_of_birth(date)
returns interval
as $$
select age($1, 'epoch'::date)
- (extract(year from age($1, 'epoch'::date)) || ' years')::interval;
$$ language sql immutable strict;
create index on client_contacts(day_of_birth(birthdate));
...
where day_of_birth(cd.birthdate) = day_of_birth(current_date);
(Обратите внимание, что технически не является неизменным, поскольку даты зависят от часового пояса. Но неизменяемая часть необходима для создания индекса, и это безопасно, если вы не меняете часовой пояс во всем место.)
РЕДАКТИРОВАТЬ: Я только что немного протестировал вышеупомянутое, и предложение по индексу фактически не работает для 29 февраля. 29 февраля дает day_of_birth 1 мес 28 дней, которые, хотя и правильные, необходимо добавить к 1 января, чтобы получить действительную дату рождения в текущем году.
create or replace function birthdate(date)
returns date
as $$
select (date_trunc('year', now()::date)
+ age($1, 'epoch'::date)
- (extract(year from age($1, 'epoch'::date)) || ' years')::interval
)::date;
$$ language sql stable strict;
with dates as (
select d
from unnest('{
2004-02-28,2004-02-29,2004-03-01,
2005-02-28,2005-03-01
}'::date[]) d
)
select d,
day_of_birth(d),
birthdate(d)
from dates;
d | day_of_birth | birthdate
------------+---------------+------------
2004-02-28 | 1 mon 27 days | 2011-02-28
2004-02-29 | 1 mon 28 days | 2011-03-01
2004-03-01 | 2 mons | 2011-03-01
2005-02-28 | 1 mon 27 days | 2011-02-28
2005-03-01 | 2 mons | 2011-03-01
(5 rows)
И, таким образом:
where birthdate(cd.birthdate) = current_date