PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Popola dati casuali da un'altra tabella

CONFIGURAZIONE

Iniziamo assumendo che le tabelle e i dati siano i seguenti. Nota che presumo che dataset1 ha una chiave primaria (può essere composta, ma, per semplicità, rendiamola intera):

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

CREATE TABLE dataset2
(
    column1 TEXT
) ;
 

Riempiamo entrambe le tabelle con dati di esempio

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) ;
 

Controllo di integrità:

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

Caso 1:numero di righe nel set di dati1 <=righe nel set di dati2

Eseguiremo un rimescolamento completo. I valori del set di dati2 verranno utilizzati una volta e non più di una volta.

SPIEGAZIONE

Per effettuare un aggiornamento che rimescoli tutti i valori da column4 in modo casuale, abbiamo bisogno di alcuni passaggi intermedi.

Innanzitutto, per il dataset1 , dobbiamo creare un elenco (relazione) di tuple (id, rn) , sono solo:

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

Dove id_1 , ..., id_20 sono gli ID presenti su dataset1 .Possono essere di qualsiasi tipo, non devono essere necessariamente consecutive e possono essere composte.

Per il dataset2 , dobbiamo creare un altro elenco di (column_1,rn) , che assomiglia a:

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

In questo caso, la seconda colonna contiene tutti i valori 1 .. 20, ma mescolati.

Una volta che abbiamo le due relazioni, JOIN loro ON ... rn . Questo, in pratica, produce ancora un altro elenco di tuple con (id, column1) , dove l'accoppiamento è stato eseguito in modo casuale. Usiamo queste coppie per aggiornare dataset1 .

LA VERA DOMANDA

Tutto questo può essere fatto (chiaramente, spero) usando alcuni CTE (WITH dichiarazione) per mantenere le relazioni intermedie:

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 ;
 

Nota che il trucco viene eseguita per mezzo di:

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

Il row_number() funzione finestra che produce tanti numeri consecutivi quante sono le righe, a partire da 1. Questi numeri vengono mescolati casualmente perché OVER La clausola prende tutti i dati e li ordina casualmente.

CONTROLLI

Possiamo ricontrollare:

SELECT count(DISTINCT column4) FROM dataset1 ;
 
| contare || ----:|| 20 |
SELECT * FROM dataset1 ;
 
id | colonna 4 --:| :-------------101 | QUALCOSA 1016102 | QUALCOSA 1009103 | QUALCOSA 1003...118 | QUALCOSA 1012119 | QUALCOSA 1017120 | QUALCOSA 1011

ALTERNATIVA

Si noti che questo può essere fatto anche con le sottoquery, mediante semplice sostituzione, invece di CTE. Ciò potrebbe migliorare le prestazioni in alcune occasioni:

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 ancora...

SELECT * FROM dataset1;
 
id | colonna 4 --:| :-------------101 | QUALCOSA 1011102 | QUALCOSA 1018103 | QUALCOSA 1007...118 | QUALCOSA 1020119 | QUALCOSA 1002120 | QUALCOSA 1016

Puoi controllare l'intera configurazione e sperimentare su dbfiddle qui

NOTA:se lo fai con set di dati molto grandi, non aspettarti che sia estremamente veloce. Mischiare un mazzo di carte molto grande è costoso.

Caso 2:numero di righe nel set di dati1> righe nel set di dati2

In questo caso, valori per column4 può essere ripetuto più volte.

La possibilità più semplice che mi viene in mente (probabilmente non efficiente, ma facile da capire) è creare una funzione random_column1 , contrassegnato come VOLATILE :

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

E usalo per aggiornare:

UPDATE
    dataset1
SET
    column4 = random_column1();
 

In questo modo, alcuni valori da dataset2 potrebbe non essere utilizzato affatto, mentre altri lo faranno essere utilizzato più di una volta.

dbfiddle qui