USTAWIENIA
Zacznijmy od założenia, że twoje tabele i dane są następujące. Zauważ, że zakładam, że dataset1
ma klucz podstawowy (może to być klucz złożony, ale dla uproszczenia ustawmy go jako liczbę całkowitą):
CREATE TABLE dataset1
(
id INTEGER PRIMARY KEY,
column4 TEXT
) ;
CREATE TABLE dataset2
(
column1 TEXT
) ;
Obie tabele wypełniamy przykładowymi danymi
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) ;
Kontrola zdrowia psychicznego:
SELECT count(DISTINCT column4) FROM dataset1 ;
| count | | ----: | | 20 |
Przypadek 1:liczba wierszy w zestawie danych1 <=wiersze w zestawie danych2
Wykonamy pełne tasowanie. Wartości z zestawu danych2 zostaną użyte raz i nie więcej niż raz.
WYJAŚNIENIE
Aby dokonać aktualizacji, która tasuje wszystkie wartości z column4
w sposób losowy potrzebujemy kilku kroków pośrednich.
Po pierwsze, dla dataset1
, musimy stworzyć listę (relację) krotek (id, rn)
, czyli po prostu:
(id_1, 1),
(id_2, 2),
(id_3, 3),
...
(id_20, 20)
Gdzie id_1
, ..., id_20
czy identyfikatory są obecne w dataset1
.Mogą być dowolnego typu, nie muszą być następujące po sobie i mogą być złożone.
Dla dataset2
, musimy utworzyć kolejną listę (column_1,rn)
, który wygląda tak:
(column1_1, 17),
(column1_2, 3),
(column1_3, 11),
...
(column1_20, 15)
W tym przypadku druga kolumna zawiera wszystkie wartości 1...20, ale przetasowane.
Gdy mamy już dwie relacje, JOIN
je ON ... rn
. W praktyce daje to kolejną listę krotek z (id, column1)
, gdzie parowanie zostało wykonane losowo. Używamy tych par do aktualizacji dataset1
.
PRAWDZIWE ZAPYTANIE
To wszystko można zrobić (oczywiście, mam nadzieję) za pomocą CTE (WITH
oświadczenie) do utrzymania relacji pośrednich:
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 ;
Pamiętaj, że sztuczka odbywa się za pomocą:
row_number() OVER (ORDER BY random()) AS rn
row_number()
funkcja okna
który daje tyle kolejnych liczb, ile jest wierszy, zaczynając od 1. Liczby te są losowo tasowane, ponieważ OVER
klauzula pobiera wszystkie dane i sortuje je losowo.
WERYFIKACJE
Możemy sprawdzić ponownie:
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
ALTERNATYWNE
Zauważ, że można to również zrobić za pomocą podzapytań, przez proste podstawienie zamiast CTE. W niektórych przypadkach może to poprawić wydajność:
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 ;
I znowu...
SELECT * FROM dataset1;
id | column4 --: | :------------- 101 | SOMETHING 1011 102 | SOMETHING 1018 103 | SOMETHING 1007 ... 118 | SOMETHING 1020 119 | SOMETHING 1002 120 | SOMETHING 1016
Całą konfigurację i eksperyment można sprawdzić na dbfiddle tutaj
UWAGA:jeśli robisz to z bardzo dużymi zestawami danych, nie oczekuj, że będzie to bardzo szybkie. Tasowanie bardzo dużej talii kart jest drogie.
Przypadek 2:liczba wierszy w zestawie danych1> wiersze w zestawie danych2
W tym przypadku wartości dla column4
można powtórzyć kilka razy.
Najłatwiejszą możliwością, jaką przychodzi mi do głowy (prawdopodobnie niezbyt wydajną, ale łatwą do zrozumienia) jest utworzenie funkcji random_column1
, oznaczony jako VOLATILE
:
CREATE FUNCTION random_column1()
RETURNS TEXT
VOLATILE -- important!
LANGUAGE SQL
AS
$$
SELECT
column1
FROM
dataset2
ORDER BY
random()
LIMIT
1 ;
$$ ;
I użyj go do aktualizacji:
UPDATE
dataset1
SET
column4 = random_column1();
W ten sposób niektóre wartości z dataset2
może w ogóle nie będą używane, podczas gdy inne będą być używany więcej niż raz.
dbfiddle tutaj