A interpretação do ano de dois dígitos e seu século implícito parece ser baseada tanto em seu valor quanto no PIN. Os intervalos para essa sobreposição, mas o ano inteiro é restrito; então parece que você pode usar uma expressão case que verifica ambos:
-- CTE for dummy data
with t42 (ssn) as (
select '12104900000' from dual
union all select '12105099999' from dual
union all select '01010000001' from dual
union all select '02029949902' from dual
union all select '03035450003' from dual
union all select '04049974904' from dual
union all select '05050050005' from dual
union all select '06063999906' from dual
union all select '07074090007' from dual
union all select '08089999908' from dual
)
select ssn, to_date(substr(ssn, 1, 4)
|| case
when to_number(substr(ssn, 7, 3)) between 0 and 499
and to_number(substr(ssn, 5, 2)) between 0 and 99 then '19'
when to_number(substr(ssn, 7, 3)) between 500 and 749
and to_number(substr(ssn, 5, 2)) between 54 and 99 then '18'
when to_number(substr(ssn, 7, 3)) between 500 and 999
and to_number(substr(ssn, 5, 2)) between 0 and 39 then '20'
when to_number(substr(ssn, 7, 3)) between 900 and 999
and to_number(substr(ssn, 5, 2)) between 40 and 99 then '19'
end
|| substr(ssn, 5, 2), 'DDMMYYYY') as dob
from t42;
que para esses dados, com base em seus dois exemplos e os intervalos envolvidos, fornece:
SSN DOB
----------- ----------
12104900000 1949-10-12
12105099999 1950-10-12
01010000001 1900-01-01
02029949902 1999-02-02
03035450003 1854-03-03
04049974904 1899-04-04
05050050005 2000-05-05
06063999906 2039-06-06
07074090007 1940-07-07
08089999908 1999-08-08
O caso escolhe um valor de século de dois dígitos com base no PIN e depois - porque eles se sobrepõem - no intervalo de dois dígitos do ano.
Se o design de dados for alterado para que as sobreposições não sejam mais exclusivas com base no ano de dois dígitos, você terá mais problemas. Será interessante ver o que acontecerá quando chegarmos a 2040...
E se você tiver um SSN que não corresponda aos intervalos mostrados, diga
12105050000
(com PIN 500, mas ano de dois dígitos não no intervalo 00-39 ou 54-99), a expressão case retornará nulo e o ano de dois dígitos será interpretado como 0050. Você pode cometer um erro alterando o modelo de formato - depende se isso pode acontecer e como você deseja lidar com isso, se isso acontecer. Você provavelmente pode descobrir isso de qualquer maneira, mas para lidar com o cenário dia+40 mencionado nos comentários, você pode usar outra expressão de caso para ajustar o número do dia:
select ssn, to_date(
case
when substr(ssn, 1, 2) > 31 then to_char(to_number(substr(ssn, 1, 2)) - 40, 'FM99')
else substr(ssn, 1, 2)
end
|| substr(ssn, 3, 2)
|| case
when to_number(substr(ssn, 7, 3)) between 0 and 499
...