This article describes a function that will return random data based on a given value. The function intends to determine the data type and return data that is absolutely irrelevant to the original data but the same length and the same type of data.
Why?
The joy of working with the plethora of applications out there result in me using MySQL, Oracle PL/SQL and Transact SQL in my day job. The equivalent functions have to be written up all in the name of "seamless integration"...
How?
Previously I had tried to write a smart function which could determine the datatype automatically but as you may know, Oracle has a funny way of working with dates which made this need to be changed. The following function now requires a second parameter where you specify the data type:
CREATE OR REPLACE FUNCTION UFN_DATASCRAMBLE
(
p_original_value IN VARCHAR2,
p_original_datatype IN VARCHAR2,
p_original_range_1 IN VARCHAR2 DEFAULT NULL, -- no effect on strings
p_original_range_2 IN VARCHAR2 DEFAULT NULL -- no effect on strings
)
RETURN VARCHAR2
IS
v_char_at NUMBER := 0; -- v_char_at : internal variable for string indexing
v_rand_param1 VARCHAR2(50) := ''; -- v_rand_param1 : parameter 1 for dbms_random function
v_rand_param2 VARCHAR2(50) := ''; -- v_rand_param2 : parameter 2 for dbms_random function
v_returnedval VARCHAR2(255) := ''; -- v_returnedval : value to be returned
BEGIN
IF p_original_value IS NULL THEN
v_returnedval := NULL;
ELSIF p_original_datatype='INT' THEN
-- Check Parameters and assign proper default values
IF p_original_range_1 IS NULL THEN
v_rand_param1 := POWER(10, (LENGTH(p_original_value)-1)); -- should yield eg. 100000
ELSE
v_rand_param1 := p_original_range_1;
END IF;
IF p_original_range_2 IS NULL THEN
v_rand_param2 := POWER(10, LENGTH(p_original_value))-1; -- should yield eg. 999999
ELSE
v_rand_param2 := p_original_range_2;
END IF;
-- Determined submitted value as 'INTEGER';
v_returnedval := TRUNC(DBMS_RANDOM.VALUE(v_rand_param1, v_rand_param2));
v_returnedval := SUBSTR(v_returnedval,1,LENGTH(p_original_value));
ELSIF p_original_datatype='FLOAT' THEN
-- Determined submitted value as 'FLOAT';
v_char_at := (INSTR(p_original_value, '.')-1);
v_rand_param1 := POWER(10, (LENGTH(p_original_value) - v_char_at)); -- should yield eg. 10
v_rand_param2 := POWER(10, (LENGTH(p_original_value) - v_char_at+1)-1); -- should yield eg. 99
v_returnedval := TRUNC(DBMS_RANDOM.VALUE(v_rand_param1, v_rand_param2));
v_rand_param1 := POWER(10, (v_char_at - 1)); -- should yield eg. 100000
v_rand_param2 := POWER(10, v_char_at) - 1; -- should yield eg. 999999
v_returnedval := TRUNC(DBMS_RANDOM.VALUE(v_rand_param1, v_rand_param2))||'.'||v_returnedval;
v_returnedval := SUBSTR(v_returnedval,1,LENGTH(p_original_value));
ELSIF p_original_datatype='VARCHAR' THEN
IF LENGTH(TRIM(TRANSLATE(p_original_value, 'abcdefghijklmnopqrstuvwxyz ', ' '))) IS NULL THEN
-- Determined submitted value as 'ALPHALOWER';
v_rand_param1 := 'L';
v_rand_param2 := LENGTH(p_original_value);
v_returnedval := DBMS_RANDOM.STRING(v_rand_param1, v_rand_param2);
ELSIF LENGTH(TRIM(TRANSLATE(p_original_value, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ ', ' '))) IS NULL THEN
-- Determined submitted value as 'ALPHAUPPER';
v_rand_param1 := 'U';
v_rand_param2 := LENGTH(p_original_value);
v_returnedval := DBMS_RANDOM.STRING(v_rand_param1, v_rand_param2);
ELSIF LENGTH(TRIM(TRANSLATE(p_original_value, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ', ' '))) IS NULL THEN
-- Determined submitted value as 'ALPHABOTH';
v_rand_param1 := 'A';
v_rand_param2 := LENGTH(p_original_value);
v_returnedval := DBMS_RANDOM.STRING(v_rand_param1, v_rand_param2);
ELSIF LENGTH(TRIM(TRANSLATE(LOWER(p_original_value), 'abcdefghijklmnopqrstuvwxyz0123456789 ', ' '))) IS NULL THEN
-- Determined submitted value as 'ALPHANUMERIC';
v_rand_param1 := 'X';
v_rand_param2 := LENGTH(p_original_value);
v_returnedval := DBMS_RANDOM.STRING(v_rand_param1, v_rand_param2);
ELSE
-- Determined submitted value as 'STRING';
v_rand_param1 := 'P';
v_returnedval := DBMS_RANDOM.STRING(v_rand_param1, LENGTH(p_original_value));
END IF;
v_returnedval := SUBSTR(v_returnedval,1,LENGTH(p_original_value));
ELSIF p_original_datatype='DATE' THEN
-- Check Parameters and assign proper default values
IF p_original_range_1 IS NULL THEN
v_rand_param1 := '01/01/1901 00:00:00';
ELSE
v_rand_param1 := p_original_range_1;
END IF;
IF p_original_range_2 IS NULL THEN
v_rand_param2 := '01/01/2001 23:59:59';
ELSE
v_rand_param2 := p_original_range_2;
END IF;
-- Determined submitted value as 'DATE';
v_rand_param1 := TO_CHAR(TO_DATE(v_rand_param1,'dd/mm/yyyy hh24:mi:ss'),'J'); -- eg. from 01/01/1901 (Julian Days)
v_rand_param2 := TO_CHAR(TO_DATE(v_rand_param2,'dd/mm/yyyy hh24:mi:ss'),'J'); -- eg. to 01/01/2001 (Julian Days)
v_returnedval := TO_DATE(TRUNC(DBMS_RANDOM.VALUE(v_rand_param1,v_rand_param2)),'J');
END IF;
RETURN v_returnedval;
EXCEPTION
WHEN no_data_found THEN
RETURN ('Error in submitted value');
WHEN others THEN
RETURN ('Unable to generate random value');
END UFN_DATASCRAMBLE;
Usage
SELECT
-- UFN_DATASCRAMBLE(DATE_CREATED, 'DATE') scramble_column, -- testing inputted column
UFN_DATASCRAMBLE(0123456789012345, 'INT') random_integer, -- testing 16 digit number
UFN_DATASCRAMBLE(0123456789012345, 'INT',1,9) random_integer_range, -- testing integer with range
UFN_DATASCRAMBLE(1234.567, 'FLOAT') random_float, -- testing decimal
UFN_DATASCRAMBLE('ABCDEFGH', 'VARCHAR') random_upper, -- testing uppercase
UFN_DATASCRAMBLE('abcdefgh', 'VARCHAR') random_lower, -- testing lowercase
UFN_DATASCRAMBLE('ABCDefgh', 'VARCHAR') random_both, -- testing upper and lowercase
UFN_DATASCRAMBLE('ABCD1234', 'VARCHAR') random_alphanum, -- testing alphanumeric
UFN_DATASCRAMBLE('ABcd12.!', 'VARCHAR') random_other, -- testing string (any char)
UFN_DATASCRAMBLE(TO_DATE('01:00:00','HH:MI:SS'), 'DATE') random_time, -- testing time
UFN_DATASCRAMBLE(TO_DATE('12/31/2012','mm/dd/yyyy'), 'DATE') random_date, -- testing date
UFN_DATASCRAMBLE(TO_DATE('2012-01-01 05:31:18','YYYY-MM-DD HH24:MI:SS'), 'DATE') random_timestamp, -- testing timestamp
UFN_DATASCRAMBLE(TO_DATE('2012-01-01','yyyy-mm-dd'), 'DATE','01/01/1000','31/12/1002') random_daterange -- testing date range
FROM
UFN_STUDENT_ACCOUNTS
WHERE
ROWNUM<2;
-- yields
-- RANDOM_INTEGER RANDOM_INTEGER_RANGE RANDOM_FLOAT RANDOM_UPPER
-- 8127711439178713 8 8328.917 JEQOQEYY
-- RANDOM_LOWER RANDOM_BOTH RANDOM_ALPHANUM RANDOM_OTHER
-- fckkfrtv fTxyqDxR 69V0FSLY sGajQ$*'
-- RANDOM_TIME RANDOM_DATE RANDOM_TIMESTAMP RANDOM_DATERANGE
-- 08-AUG-41 05-JAN-91 19-MAY-92 11-NOV-01
Additional
Note the return values for the dates and times, in Oracle you would need to specify how you want the return date specified. In the case where the above is used to update a set of data in a table (hopefully a copy of the original one :)), the database will set the format automatically:
UPDATE my_table SET my_column=UFN_DATASCRAMBLE(my_column, my_datatype);Also note the date format being a mix of european or US american format. This is more dependent on how your database is setup but as shown in the usage examples above, you can specify the format as well.
Discussion
Comments
Questions, corrections and useful additions are reviewed before appearing here.
No comments yet. Start the discussion.