PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Wypełnij losowe dane z innej tabeli

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



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Rekurencyjny postgres JSONB

  2. flyway nie może połączyć się z kontenerem postgres w skrypcie docker-entrypoint-initdb.d

  3. postgresql - zamień wszystkie wystąpienia ciągu w polu tekstowym

  4. Czy przypadek ma znaczenie, gdy „auto” ładuje dane z S3 do tabeli Redshift?

  5. Błąd składni Postgresa przy lub w pobliżu IF