sql >> Database >  >> RDS >> PostgreSQL

Willekeurige gegevens uit een andere tabel invullen

INSTELLEN

Laten we beginnen met aan te nemen dat uw tabellen en gegevens de volgende zijn. Merk op dat ik aanneem dat dataset1 heeft een primaire sleutel (het kan een samengestelde sleutel zijn, maar laten we er voor de eenvoud een geheel getal van maken):

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

CREATE TABLE dataset2
(
    column1 TEXT
) ;

We vullen beide tabellen met voorbeeldgegevens

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

Gezondheidscontrole:

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

Geval 1:aantal rijen in dataset1 <=rijen in dataset2

We voeren een volledige shuffle uit. Waarden uit dataset2 worden één keer gebruikt, en niet meer dan één keer.

UITLEG

Om een ​​update uit te voeren die alle waarden van column4 door elkaar schudt willekeurig hebben we enkele tussenstappen nodig.

Ten eerste, voor de dataset1 , we moeten een lijst (relatie) van tuples maken (id, rn) , dat zijn gewoon:

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

Waar id_1 , ..., id_20 zijn de id's aanwezig op dataset1 .Ze kunnen van elk type zijn, ze hoeven niet opeenvolgend te zijn en ze kunnen samengesteld zijn.

Voor de dataset2 , moeten we een andere lijst maken van (column_1,rn) , dat lijkt op:

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

In dit geval bevat de tweede kolom alle waarden 1 .. 20, maar geschud.

Zodra we de twee relaties hebben, JOIN ze ON ... rn . Dit levert in de praktijk nog een andere lijst met tuples op met (id, column1) , waarbij de koppeling willekeurig is uitgevoerd. We gebruiken deze paren om dataset1 bij te werken .

DE ECHTE VRAAG

Dit kan allemaal worden gedaan (duidelijk hoop ik) door een aantal CTE te gebruiken (WITH statement) om de tussenliggende relaties vast te houden:

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 ;

Merk op dat de truc wordt uitgevoerd door middel van:

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

De row_number() vensterfunctie die evenveel opeenvolgende getallen oplevert als er rijen zijn, beginnend bij 1. Deze getallen worden willekeurig geschud omdat de OVER clausule neemt alle gegevens en sorteert deze willekeurig.

CONTROLES

We kunnen het opnieuw controleren:

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

ALTERNATIEF

Merk op dat dit ook kan worden gedaan met subquery's, door eenvoudige vervanging, in plaats van CTE's. Dat kan in sommige gevallen de prestaties verbeteren:

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 ;

En nogmaals...

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

Je kunt de hele setup en het experiment bekijken op dbfiddle hier

LET OP:als je dit met zeer grote datasets doet, verwacht dan niet dat het extreem snel gaat. Het schudden van een heel groot pak kaarten is duur.

Geval 2:aantal rijen in dataset1> rijen in dataset2

In dit geval, waarden voor column4 kan meerdere keren worden herhaald.

De gemakkelijkste mogelijkheid die ik kan bedenken (waarschijnlijk niet efficiënt, maar gemakkelijk te begrijpen) is om een ​​functie random_column1 te maken , gemarkeerd als VOLATILE :

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

En gebruik het om te updaten:

UPDATE
    dataset1
SET
    column4 = random_column1();

Op deze manier kunnen enkele waarden uit dataset2 misschien helemaal niet worden gebruikt, terwijl andere wel meer dan eens worden gebruikt.

dbfiddle hier



  1. kan soundex worden gebruikt op een deel van een kolom in mysql?

  2. waarde doorgeven in url href in php

  3. Hoe dubbele rijen in SQL te elimineren

  4. Filteren op AANTAL(*)?