PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Remplir des données aléatoires d'une autre table

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)

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