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