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