Ну, определенная пользователем функция - самое ясное, если не самое правильное решение. Однако без пользовательской функции вы можете сделать это с помощью регулярного выражения. Следующее предлагает наиболее общее регулярное выражение для дат проверки, однако оно неверно помечает 29-февраль как недействительный в високосные годы (проверка того, что с помощью регулярного выражения начинает усложняться), но в противном случае проверяет даты. Это регулярное выражение:
'^(0[0-9]{2}[1-9]|[1-9][0-9]{3})-((0[13578]|10|12)-(0[1-9]|[12][0-9]|3[01])|02-(0[1-9]|1[0-9]|2[0-8])|(0[469]|11)-(0[1-9]|[12][0-9]|30))$')
Разбивается следующим образом:
^ ==> Начиная с начала строки.
(0 [0-9] {2} [1-9] | [1-9] [0-9] {3}) ==> год в диапазоне 0001 - 9999 (Postgres может на самом деле поддерживать годы далеко кроме этого, но этого должно быть достаточно).
- - ==> буквальный символ
- (0 [13578] | 10 | 12) ==> В течение нескольких месяцев январь, март, май , Июль, август, октябрь, декабрь
- - ==> литерал
- (0 [1-9] | [12] [0-9] | 3 [01]) ==> день находится в диапазоне от 01 до 31
- 02 для февраля
- - ==> литерал
- (0 [1-9] | 1 [0 -9] | 2 [0-8]) ==> день в диапазоне от 01 до 28
- (0 [469] | 11) ==> Для апреля, июня, сентября, ноября
- - ==> литерал
- (0 [1-9] | [12] [0-9] | 30) ==> день находится в диапазоне от 01 до 30
- $ ==> конец строки
Следующий тест каждый день для 2020 года и несколько правильных и недействительных дат, добавленных в:
with date_validator(pattrn) as
( select '^(0[0-9]{2}[1-9]|[1-9][0-9]{3})-((0[13578]|10|12)-(0[1-9]|[12][0-9]|3[01])|02-(0[1-9]|1[0-9]|2[0-8])|(0[469]|11)-(0[1-9]|[12][0-9]|30))$')
, extras (dt) as
(select * from ( values ('2020/02/15'), ('abcd'),('00-01-01'),('1215-06-15'),('1776-07-04'),('bad_date')) as t(dt))
, test_data as
(select ('2020-01-01'::timestamp + lev * interval '1 day')::date::text dt
from generate_series(0,365) lev
union all
select * from extras
)
select dt, dt ~ pattrn "Is Valid Date"
from test_data
cross join date_validator;
И, наконец, с ваши тестовые данные и возвращая 1 для действительной даты s и 0 для недопустимых:
with emp(eid,ename,dob,doj) as
( values (1, 'abc', '1900-01-19', '2019-02-20' )
, (2, 'dx', '1900-29-02', '2019-04-22' )
, (3, 'xy', '1989-10-26', '2018-27-02' )
, (4, 'ji', '2000-23-01', '2019-29-04' )
, (5, 'jj', '1980/04/09', '1999/08/20' )
)
, date_validator(pattrn) as
( select '^(0[0-9]{2}[1-9]|[1-9][0-9]{3})-((0[13578]|10|12)-(0[1-9]|[12][0-9]|3[01])|02-(0[1-9]|1[0-9]|2[0-8])|(0[469]|11)-(0[1-9]|[12][0-9]|30))$')
select eid
, case when dob ~ pattrn::text then 1 else 0 end "dob date format status"
, case when doj ~ pattrn::text then 1 else 0 end "doj date format status"
from emp cross join date_validator ;
Конечно, если бы мне пришлось это реализовать, я бы, конечно, скрыл это пользовательскую функцию. Я, конечно, не доверял бы попытаться напечатать это каждый раз. Но, как другие отметили, сохраните ваши даты как дату (или отметку времени), и у вас не возникнет этой проблемы.