CONFIGURATION
Commençons par supposer que vos tables et vos données sont les suivantes. Notez que je suppose que dataset1
a une clé primaire (elle peut être composée, mais, par souci de simplicité, faisons-en un entier) :
CREATE TABLE dataset1
(
id INTEGER PRIMARY KEY,
column4 TEXT
) ;
CREATE TABLE dataset2
(
column1 TEXT
) ;
Nous remplissons les deux tableaux avec des exemples de données
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) ;
Contrôle d'intégrité :
SELECT count(DISTINCT column4) FROM dataset1 ;
| count | | ----: | | 20 |
Cas 1 :nombre de lignes dans le jeu de données 1 <= lignes dans le jeu de données2
Nous allons effectuer un remaniement complet. Les valeurs de l'ensemble de données 2 seront utilisées une seule fois, et pas plus d'une fois.
EXPLICATION
Afin de faire une mise à jour qui mélange toutes les valeurs de column4
de manière aléatoire, nous avons besoin de quelques étapes intermédiaires.
Tout d'abord, pour le dataset1
, nous devons créer une liste (relation) de tuples (id, rn)
, ce sont juste :
(id_1, 1),
(id_2, 2),
(id_3, 3),
...
(id_20, 20)
Où id_1
, ..., id_20
sont les identifiants présents sur dataset1
.Ils peuvent être de n'importe quel type, il n'est pas nécessaire qu'ils soient consécutifs et ils peuvent être composites.
Pour le dataset2
, nous devons créer une autre liste de (column_1,rn)
, qui ressemble à :
(column1_1, 17),
(column1_2, 3),
(column1_3, 11),
...
(column1_20, 15)
Dans ce cas, la deuxième colonne contient toutes les valeurs 1 .. 20, mais mélangées.
Une fois que nous avons les deux relations, nous JOIN
eux ON ... rn
. Ceci, en pratique, produit encore une autre liste de tuples avec (id, column1)
, où l'appariement a été fait de façon aléatoire. Nous utilisons ces paires pour mettre à jour dataset1
.
LA VRAIE REQUÊTE
Tout cela peut être fait (clairement, j'espère) en utilisant un CTE (WITH
instruction) pour contenir les relations intermédiaires :
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 ;
Notez que l'astuce s'effectue au moyen de :
row_number() OVER (ORDER BY random()) AS rn
Le row_number()
fonction fenêtre
qui produit autant de nombres consécutifs qu'il y a de lignes, en commençant par 1. Ces nombres sont mélangés aléatoirement car le OVER
La clause prend toutes les données et les trie au hasard.
VÉRIFICATIONS
Nous pouvons vérifier à nouveau :
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
ALTERNATIVE
Notez que cela peut également être fait avec des sous-requêtes, par simple substitution, au lieu de CTE. Cela peut améliorer les performances dans certains cas :
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 ;
Et encore...
SELECT * FROM dataset1;
id | column4 --: | :------------- 101 | SOMETHING 1011 102 | SOMETHING 1018 103 | SOMETHING 1007 ... 118 | SOMETHING 1020 119 | SOMETHING 1002 120 | SOMETHING 1016
Vous pouvez vérifier l'ensemble de la configuration et expérimenter sur dbfiddle ici
REMARQUE :si vous faites cela avec de très grands ensembles de données, ne vous attendez pas à ce que ce soit extrêmement rapide. Mélanger un très gros jeu de cartes coûte cher.
Cas 2 :nombre de lignes dans l'ensemble de données 1 > lignes dans l'ensemble de données 2
Dans ce cas, les valeurs de column4
peut être répété plusieurs fois.
La possibilité la plus simple à laquelle je peux penser (probablement pas efficace, mais facile à comprendre) est de créer une fonction random_column1
, marqué comme VOLATILE
:
CREATE FUNCTION random_column1()
RETURNS TEXT
VOLATILE -- important!
LANGUAGE SQL
AS
$$
SELECT
column1
FROM
dataset2
ORDER BY
random()
LIMIT
1 ;
$$ ;
Et utilisez-le pour mettre à jour :
UPDATE
dataset1
SET
column4 = random_column1();
De cette façon, certaines valeurs de dataset2
pourrait pas être utilisé du tout, alors que d'autres le feront être utilisé plus d'une fois.
dbfiddle ici