sql >> Databáze >  >> RDS >> PostgreSQL

Vyplňte náhodná data z jiné tabulky

NASTAVENÍ

Začněme tím, že předpokládejme, že vaše tabulky a data jsou následující. Všimněte si, že předpokládám, že dataset1 má primární klíč (může být složený, ale pro zjednodušení z něj udělejme celé číslo):

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

CREATE TABLE dataset2
(
    column1 TEXT
) ;

Obě tabulky naplníme vzorovými údaji

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 příčetnosti:

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

Případ 1:počet řádků v datové sadě1 <=řádků v datové sadě2

Provedeme kompletní promíchání. Hodnoty z datové sady2 budou použity jednou a ne více než jednou.

VYSVĚTLENÍ

Chcete-li provést aktualizaci, která zamíchá všechny hodnoty ze column4 náhodně potřebujeme nějaké mezikroky.

Nejprve pro dataset1 , musíme vytvořit seznam (relaci) n-tic (id, rn) , to jsou jen:

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

Kde id_1 , ..., id_20 jsou ID přítomná v dataset1 .Mohou být libovolného typu, nemusí být po sobě jdoucí a mohou být složené.

Pro dataset2 , musíme vytvořit další seznam (column_1,rn) , to vypadá takto:

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

V tomto případě druhý sloupec obsahuje všechny hodnoty 1 .. 20, ale zamíchané.

Jakmile máme oba vztahy, JOIN ON ... rn . To v praxi vytváří další seznam n-tic s (id, column1) , kde bylo párování provedeno náhodně. Tyto páry používáme k aktualizaci dataset1 .

SKUTEČNÝ DOTAZ

To vše lze provést (jasně, doufám) pomocí nějakého CTE (WITH příkaz) k udržení mezilehlých vztahů:

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 ;

Všimněte si, že trik se provádí pomocí:

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

row_number() funkce okna který vytvoří tolik po sobě jdoucích čísel, kolik je řádků, počínaje 1. Tato čísla jsou náhodně zamíchána, protože OVER klauzule vezme všechna data a náhodně je seřadí.

KONTROLY

Můžeme znovu zkontrolovat:

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

ALTERNATIVNÍ

Všimněte si, že to lze také provést pomocí poddotazů, jednoduchou substitucí, namísto CTE. To by mohlo v některých případech zlepšit výkon:

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 ;

A znovu...

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

Celé nastavení a experiment můžete zkontrolovat na dbfiddle zde

POZNÁMKA:Pokud to uděláte s velmi velkými datovými sadami, neočekávejte, že to bude extrémně rychlé. Míchání velmi velkého balíčku karet je drahé.

Případ 2:počet řádků v datové sadě1> řádků v datové sadě2

V tomto případě hodnoty pro column4 lze několikrát opakovat.

Nejjednodušší možnost, kterou si dokážu představit (pravděpodobně ne efektivní, ale snadno pochopitelná), je vytvořit funkci random_column1 , označené jako VOLATILE :

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

A použijte jej k aktualizaci:

UPDATE
    dataset1
SET
    column4 = random_column1();

Tímto způsobem některé hodnoty z dataset2 možná nebudou použity vůbec, zatímco ostatní budou použít více než jednou.

dbfiddle zde



  1. Čekání na localhost, navždy!

  2. PHP PDO vybírá sloupce s jednoduchými uvozovkami v jejich názvu

  3. MYSQL jak odemknout tabulku, pokud jsem použil příkaz LOCK tabulka název_tabulky WRITE;

  4. java.sql.SQLException:ORA-00932:nekonzistentní datové typy:očekávaný NUMBER získal BINARY