L'interpretazione dell'anno a due cifre e del suo secolo implicito sembra basarsi sia sul suo valore che sul PIN. Gli intervalli per tale sovrapposizione, ma l'intero anno viene quindi limitato; quindi sembra che tu possa usare un'espressione case che controlla entrambi:
-- 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;
che per quei dati, in base ai tuoi due esempi e agli intervalli coinvolti, fornisce:
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
Il caso seleziona un valore del secolo a due cifre in base al PIN e quindi, poiché si sovrappongono, all'intervallo di anni a due cifre.
Se la progettazione dei dati cambia in modo che le sovrapposizioni non siano più univoche in base all'anno a due cifre, si verificano ulteriori problemi. Sarà interessante vedere cosa accadrà quando raggiungeremo il 2040...
E se avevi un SSN che non corrispondeva agli intervalli che hai mostrato, dì 12105050000
(con PIN 500, ma anno a due cifre non compreso nell'intervallo 00-39 o 54-99), l'espressione del caso restituirà null e l'anno a due cifre verrà quindi interpretato come 0050. È possibile invece commettere un errore modificando il modello di formato - dipende se può accadere e come vuoi gestirlo se lo fa.
Probabilmente puoi comunque capire questo bit, ma per gestire lo scenario day+40 menzionato nei commenti, puoi usare un'altra espressione maiuscola per regolare il numero del giorno:
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
...