Interpretacja dwucyfrowego roku i związanego z nim wieku wydaje się opierać zarówno na jego wartości, jak i numerze PIN. Zakresy dla tego nakładania się, ale cały rok jest wtedy ograniczony; więc wygląda na to, że możesz użyć wyrażenia wielkości liter, które sprawdza oba:
-- 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;
co dla tych danych, na podstawie twoich dwóch przykładów i związanych z nimi zakresów, daje:
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
Sprawa wybiera dwucyfrową wartość wieku na podstawie kodu PIN, a następnie – ponieważ się pokrywają – dwucyfrowy zakres roku.
Jeśli projekt danych zmieni się tak, że nakładanie się nie będzie już unikatowe w oparciu o dwucyfrowy rok, masz dalsze problemy. Ciekawie będzie zobaczyć, co się stanie, gdy dotrzemy do 2040 roku...
A jeśli masz numer SSN, który nie pasuje do pokazanych zakresów, powiedz 12105050000
(z kodem PIN 500, ale dwucyfrowy rok nie mieści się w żadnym zakresie 00-39 lub 54-99) wtedy wyrażenie wielkości liter zwróci null, a dwucyfrowy rok zostanie zinterpretowany jako 0050. Zamiast tego można popełnić błąd, zmieniając model formatu - zależy od tego, czy może się to zdarzyć i jak chcesz sobie z tym poradzić, jeśli tak się stanie.
Prawdopodobnie i tak możesz to rozgryźć, ale aby poradzić sobie ze scenariuszem dnia +40, o którym mowa w komentarzach, możesz użyć innego wyrażenia przypadku, aby dostosować numer dnia:
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
...