10 ene 2021

Postgresql: carga de datos aleatorios

Últimamente he venido separándome algo de las tecnologías de Microsoft y ahora lidio con otras base de datos. Ya lo había experimentado a la inversa cuando al inicio de mi carrera cuando trabaje solamente con Oracle para luego pasarme a SQL Server, pero ahora le toca el turno a Postgresql (version 9.6 para ser preciso).

El problema en el que estoy trabajando es la carga random de datos en una tabla para propósitos de pruebas.

Estuve buscando alguna herramienta que ya lo hiciera (y las hay sólo que prácticamente todas son de pago) pero en realidad como lo que necesitaba era realmente simple decide hacerlo por medio de un sencillo script.

Primero necesitamos contar con una tabla:
CREATE TABLE foy.test
(
    test_id 		uuid 		DEFAULT uuid_generate_v4(),
    client_id		integer		NOT NULL,
    first_name		text,
    last_name		text,
    downloaded_at	timestamp,
    is_active		boolean		NOT NULL DEFAULT FALSE
);
Supongamos que en esta tabla se cargan clientes que se descargan de alguna parte. Ya con esta tabla previamente creada podemos ejecutar un sencillo script que la llene con algunos datos aleatorios.
INSERT INTO foy.test
SELECT 
	uuid_generate_v4(), -- test_id
	FLOOR(RANDOM()*(10000000- 100000 + 1)) + 100000, --client id
	(array['Peter', 'Kim', 'Lou', 'Jhon', 'Set','Carl','Karen', 'Marco','Tim','Marie'])[floor(random() * 10 + 1)] -- first name
	(array['Kent', 'Foster', 'Lee', 'Smith', 'Young','Clark','Hill', 'Brodt','Scott','Reed'])[floor(random() * 10 + 1)] -- last name
	clock_timestamp()  - (random() * INTERVAL '100 days' ), -- downloaded_at
	(case when 
    	random() > 0.8 then true
        	else false
	end) -- is_active
FROM generate_series(1,500000);

Expliquemos un poco cada línea:
uuid_generate_v4(), -- test_id
Si bien esta línea se puede omitir porque es parte del default value de la columna prefiero mantenerla para ser consciente de la cantidad de columnas de la tabla, es una función que simplemente genera UUIDs.
FLOOR(RANDOM()*(10000000- 100000 + 1)) + 100000, --client id
Esta función se encarga de generar números aleatorios enteros para el client id. En este caso estos números siempre estarán entre el numero 100,000 y el número 1,000,000.
(array['Peter', 'Kim', 'Lou', 'Jhon', 'Set','Carl','Karen', 'Marco','Tim','Marie'])[floor(random() * 10 + 1)] -- first name
(array['Kent', 'Foster', 'Lee', 'Smith', 'Young','Clark','Hill', 'Brodt','Scott','Reed'])[floor(random() * 10 + 1)] -- last name
Esta es una muy curiosa forma de generar nombres y apellidos, se toma aleatoriamente del array los valores, con esto nos ahorramos un cláusula case por ejemplo.
clock_timestamp()  - (random() * INTERVAL '100 days' ), -- downloaded_at
De esta forma podemos generar values de tipo timestamp, tomamos la función clock_timestamp() que nos da el timestamp actual y le restamos una cantidad de días aleatoria, siempre mayor a 100 en este caso, así nos aseguramos que el valor siempre será menor al momento actual.
	(case when 
    	random() > 0.8 then true
        	else false
	end) -- is_active
Con esta sentencia creamos valores "booleanos" y subiendo el valor numérico podemos tener mayor cantidad de "true" que de "false" como era lo que necesitaba en este caso.
FROM generate_series(1,500000);
Finalmente tenemos la última de las instrucciones que es de lo más conveniente. La función generate_series nos permite repetir la cláusula anterior las veces que queramos, en este caso desde 1 hasta 500,000 veces lo que resultará en 500 mil registros aleatorios en la tabla. 

Si bien puede que no sea la forma más eficiente de hacer un llenado de valores aleatorios en tablas, ésta al menos resulta práctica y rápida en escenarios simples.