sql >> Base de Datos >  >> RDS >> PostgreSQL

Rellenar datos aleatorios de otra tabla

CONFIGURACIÓN

Comencemos asumiendo que sus tablas y datos son los siguientes. Tenga en cuenta que asumo que dataset1 tiene una clave principal (puede ser una compuesta, pero, por simplicidad, hagámosla un número entero):

CREATE TABLE dataset1
(
     id INTEGER PRIMARY KEY,
     column4 TEXT
) ;

CREATE TABLE dataset2
(
    column1 TEXT
) ;

Llenamos ambas tablas con datos de muestra

INSERT INTO dataset1
    (id, column4)
SELECT
    i, 'column 4 for id ' || i
FROM
    generate_series(101, 120) AS s(i);

INSERT INTO dataset2
    (column1)
SELECT
    'SOMETHING ' || i
FROM 
    generate_series (1001, 1020) AS s(i) ;

Control de cordura:

SELECT count(DISTINCT column4) FROM dataset1 ;
| count |
| ----: |
|    20 |

Caso 1:número de filas en el conjunto de datos1 <=filas en el conjunto de datos2

Realizaremos un barajado completo. Los valores de dataset2 se usarán una vez y no más de una vez.

EXPLICACIÓN

Para hacer una actualización que baraje todos los valores de column4 de forma aleatoria, necesitamos algunos pasos intermedios.

Primero, para el dataset1 , necesitamos crear una lista (relación) de tuplas (id, rn) , que son solo:

(id_1,   1),
(id_2,   2),
(id_3,   3),
...
(id_20, 20)

Donde id_1 , ..., id_20 son los identificadores presentes en dataset1 .Pueden ser de cualquier tipo, no es necesario que sean consecutivos y pueden ser compuestos.

Para el dataset2 , necesitamos crear otra lista de (column_1,rn) , que se parece a:

(column1_1,  17),
(column1_2,   3),
(column1_3,  11),
...
(column1_20, 15)

En este caso, la segunda columna contiene todos los valores 1 .. 20, pero barajados.

Una vez que tenemos las dos relaciones, JOIN ellos ON ... rn . Esto, en la práctica, produce otra lista de tuplas con (id, column1) , donde el emparejamiento se ha realizado de forma aleatoria. Usamos estos pares para actualizar dataset1 .

LA CONSULTA REAL

Todo esto se puede hacer (claramente, espero) usando algún CTE (WITH instrucción) para mantener las relaciones intermedias:

WITH original_keys AS
(
    -- This creates tuples (id, rn), 
    -- where rn increases from 1 to number or rows
    SELECT 
        id, 
        row_number() OVER  () AS rn
    FROM 
        dataset1
)
, shuffled_data AS
(
    -- This creates tuples (column1, rn)
    -- where rn moves between 1 and number of rows, but is randomly shuffled
    SELECT 
        column1,
        -- The next statement is what *shuffles* all the data
        row_number() OVER  (ORDER BY random()) AS rn
    FROM 
        dataset2
)
-- You update your dataset1
-- with the shuffled data, linking back to the original keys
UPDATE
    dataset1
SET
    column4 = shuffled_data.column1
FROM
    shuffled_data
    JOIN original_keys ON original_keys.rn = shuffled_data.rn
WHERE
    dataset1.id = original_keys.id ;

Tenga en cuenta que el truco se realiza mediante:

row_number() OVER (ORDER BY random()) AS rn

El row_number() función de ventana que produce tantos números consecutivos como filas, comenzando desde 1. Estos números se barajan aleatoriamente porque OVER cláusula toma todos los datos y los ordena aleatoriamente.

CHEQUES

Podemos comprobar de nuevo:

SELECT count(DISTINCT column4) FROM dataset1 ;
| count |
| ----: |
|    20 |
SELECT * FROM dataset1;
 id | column4       
--: | :-------------
101 | SOMETHING 1016
102 | SOMETHING 1009
103 | SOMETHING 1003
...
118 | SOMETHING 1012
119 | SOMETHING 1017
120 | SOMETHING 1011

ALTERNATIVA

Tenga en cuenta que esto también se puede hacer con subconsultas, por simple sustitución, en lugar de CTE. Eso podría mejorar el rendimiento en algunas ocasiones:

UPDATE
    dataset1
SET
    column4 = shuffled_data.column1
FROM
    (SELECT 
        column1,
        row_number() OVER  (ORDER BY random()) AS rn
    FROM 
        dataset2
    ) AS shuffled_data
    JOIN 
    (SELECT 
        id, 
        row_number() OVER  () AS rn
    FROM 
        dataset1
    ) AS original_keys ON original_keys.rn = shuffled_data.rn
WHERE
    dataset1.id = original_keys.id ;

Y otra vez...

SELECT * FROM dataset1;
 id | column4       
--: | :-------------
101 | SOMETHING 1011
102 | SOMETHING 1018
103 | SOMETHING 1007
...
118 | SOMETHING 1020
119 | SOMETHING 1002
120 | SOMETHING 1016

Puede consultar toda la configuración y el experimento en dbfiddle aquí

NOTA:si hace esto con conjuntos de datos muy grandes, no espere que sea extremadamente rápido. Barajar una gran baraja de cartas es caro.

Caso 2:número de filas en el conjunto de datos1> filas en el conjunto de datos2

En este caso, valores para column4 se puede repetir varias veces.

La posibilidad más fácil que se me ocurre (probablemente, no eficiente, pero fácil de entender) es crear una función random_column1 , marcado como VOLATILE :

CREATE FUNCTION random_column1() 
    RETURNS TEXT
    VOLATILE      -- important!
    LANGUAGE SQL
AS
$$
    SELECT
        column1
    FROM
        dataset2
    ORDER BY
        random()
    LIMIT
        1 ;
$$ ;

Y úsalo para actualizar:

UPDATE
    dataset1
SET
    column4 = random_column1();

De esta forma, algunos valores de dataset2 podría no se usará en absoluto, mientras que otros lo harán utilizarse más de una vez.

dbfiddle aquí