Założenia algorytmu:
- nowy PESEL dla danej osoby zachowuje informacje o płci i o dacie urodzenia
- numer ewidencyjny w obrębie daty urodzenia jest numerowany od stałej cn_DayIndex w dół
- zakłada się, że parametry są poprawne - parametr pc_PESEL nie jest walidowany
- na wejściu algorytmu powinien być zbiór SELECT DISTINCT PESEL FROM....
- przed wykonaniem anonimizacji należy wykonać blok:
PCKG_ANONYMIZE_DATA.PDB_CLEARINTERNALDATA;
END;
Jak to działa??
Poniższe zapytanie generuje nowy PESEL dla Jarosława K.
SELECT PCKG_ANONYMIZE_DATA.FDB_GenerateNewPESEL('49061804592') FROM DUAL
Otrzymamy 49061899990
Wywołajmy teraz funkcję 5 razy
SELECT PCKG_ANONYMIZE_DATA.FDB_GenerateNewPESEL('49061804592') FROM DUAL
CONNECT BY LEVEL <= 5
Jako wynik otrzymamy
49061899976
49061899952
49061899938
49061899914
49061899891
Czyli widzimy, że dla kolejnych nr PESELw obrębie daty urodzenia generowane są kolejne nowe PESELE, ale nie jest sprawdzana unikalność nr PESEL na wejściu..
Czy nowe nr PESEL zapewniają pełną pseudonimizację ze względu na ten nr. Nie.., Jeśli np wiemy , ze w bazie było dwóch klientów w wieku po 80 lat, to możemy z dużym prawdopodobieństwem ich zidentyfikować, jeśli maja różne daty urodzenia. Dla lat, w których jest dużo klientów w poszczególnych dniach będzie na podstawie tylko nr PESEL niemożliwe
CREATE OR REPLACE PACKAGE PCKG_ANONYMIZE_DATA IS
PROCEDURE PDB_ClearInternalData;
FUNCTION FDB_GenerateNewPESEL(pc_PESEL VARCHAR2) RETURN VARCHAR2 ;
PRAGMA RESTRICT_REFERENCES (FDB_GenerateNewPESEL, WNDS);
END;
/
CREATE OR REPLACE PACKAGE BODY PCKG_ANONYMIZE_DATA
IS
TYPE T_NUMBERS IS TABLE OF VARCHAR2 (4)
INDEX BY VARCHAR2 (6);
Vt_Numbers T_NUMBERS;
cn_DayIndex CONSTANT NUMBER (4) := 9999;
/************************************************************************/
PROCEDURE PDB_ClearInternalData
IS
BEGIN
Vt_Numbers.DELETE;
END;
/************************************************************************/
FUNCTION FDB_GenerateNewPESEL (pc_PESEL VARCHAR2 )
RETURN VARCHAR2
IS
TYPE t_Weights IS VARRAY (10) OF SIMPLE_INTEGER;
vt_Weights t_weights
:= t_weights (1,3,7,9,1,3,7,9,1,3);
vn_CheckSum SIMPLE_INTEGER := 0;
vc_PESEL VARCHAR2 (15);
vc_DayIndex NUMBER (4) := '0';
vn_Plec NUMBER (1);
BEGIN
vc_Pesel := SUBSTR (pc_PESEL, 1, 6);
vn_Plec := SUBSTR (pc_PESEL, 10, 1);
--------------------
IF vt_Numbers.exists(vc_PESEL ) THEN
vc_DayIndex := NVL (Vt_Numbers (vc_Pesel) - 1, cn_DayIndex);
ELSE
vc_DayIndex := cn_DayIndex;
END IF;
--------------------
IF MOD (vc_DayIndex, 2) <> MOD (vn_Plec, 2) THEN
vc_DayIndex := vc_DayIndex - 1;
END IF;
--------------------
Vt_Numbers (vc_Pesel) := vc_DayIndex;
vc_Pesel :=vc_Pesel || LPAD (TO_CHAR (vc_DayIndex), 4, '0');
--------------------
FOR i IN 1 .. vt_Weights.COUNT
LOOP
vn_CheckSum :=
vn_CheckSum
+ TO_NUMBER (SUBSTR (vc_PESEL, i, 1)) * vt_Weights (i);
END LOOP;
--------------------
IF MOD (vn_CheckSum, 10) = 0 THEN vc_PESEL := vc_PESEL || '0';
ELSE
vc_PESEL := vc_PESEL || (10 - MOD (vn_CheckSum, 10));
END IF;
--------------------
RETURN vc_Pesel;
/*EXCEPTION WHEN OTHERS THEN
RETURN NULL;*/
END;
/*******************************************************************/
BEGIN
Vt_Numbers.DELETE;
END;
/