PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Preencher dados aleatórios de outra tabela


CONFIGURAÇÃO

Vamos começar assumindo que suas tabelas e dados são os seguintes. Observe que eu suponho que dataset1 tem uma chave primária (pode ser composta, mas, para simplificar, vamos torná-la um inteiro):
CREATE TABLE dataset1
(
     id INTEGER PRIMARY KEY,
     column4 TEXT
) ;

CREATE TABLE dataset2
(
    column1 TEXT
) ;

Preenchemos ambas as tabelas com dados de amostra
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) ;

Verificação de sanidade:
SELECT count(DISTINCT column4) FROM dataset1 ;
| count |
| ----: |
|    20 |

Caso 1:número de linhas no conjunto de dados1 <=linhas no conjunto de dados2


Faremos um embaralhamento completo. Os valores do dataset2 serão usados ​​uma vez e não mais de uma vez.

EXPLICAÇÃO

Para fazer uma atualização que embaralhe todos os valores de column4 de forma aleatória, precisamos de alguns passos intermediários.

Primeiro, para o dataset1 , precisamos criar uma lista (relação) de tuplas (id, rn) , que são apenas:
(id_1,   1),
(id_2,   2),
(id_3,   3),
...
(id_20, 20)

Onde id_1 , ..., id_20 são os ids presentes em dataset1 .Podem ser de qualquer tipo, não precisam ser consecutivos e podem ser compostos.

Para o dataset2 , precisamos criar outra lista de (column_1,rn) , que se parece com:
(column1_1,  17),
(column1_2,   3),
(column1_3,  11),
...
(column1_20, 15)

Neste caso, a segunda coluna contém todos os valores 1 .. 20, mas embaralhados.

Assim que tivermos as duas relações, JOIN eles ON ... rn . Isso, na prática, produz mais uma lista de tuplas com (id, column1) , onde o emparelhamento foi feito aleatoriamente. Usamos esses pares para atualizar dataset1 .

A VERDADEIRA CONSULTA

Isso tudo pode ser feito (claramente, espero) usando algum CTE (WITH declaração) para manter as relações intermediárias:
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 ;

Observe que o truque é realizado por meio de:
row_number() OVER (ORDER BY random()) AS rn

O row_number() função de janela que produz tantos números consecutivos quantas linhas, começando em 1. Esses números são embaralhados aleatoriamente porque o OVER cláusula pega todos os dados e os classifica aleatoriamente.

CHEQUES

Podemos verificar novamente:
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

Observe que isso também pode ser feito com subconsultas, por simples substituição, em vez de CTEs. Isso pode melhorar o desempenho em algumas ocasiões:
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 ;

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

Você pode verificar toda a configuração e experimentar em dbfiddle aqui

NOTA:se você fizer isso com conjuntos de dados muito grandes, não espere que seja extremamente rápido. Embaralhar um baralho muito grande é caro.

Caso 2:número de linhas no conjunto de dados1> linhas no conjunto de dados2


Nesse caso, os valores para column4 pode ser repetido várias vezes.

A possibilidade mais fácil que posso pensar (provavelmente, não é eficiente, mas fácil de entender) é criar uma função 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 ;
$$ ;

E use-o para atualizar:
UPDATE
    dataset1
SET
    column4 = random_column1();

Dessa forma, alguns valores de dataset2 pode não serão usados, enquanto outros serão ser usado mais de uma vez.

dbfiddle aqui