sql >> データベース >  >> RDS >> PostgreSQL

別のテーブルからランダムデータを入力します

    セットアップ

    まず、テーブルのデータが次のデータであると想定します。dataset1と想定していることに注意してください。 主キーがあります(複合キーにすることもできますが、簡単にするために整数にします):

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

    両方のテーブルにサンプルデータを入力します

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

    健全性チェック:

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

    ケース1:データセット1の行数<=データセット2の行数

    完全なシャッフルを実行します。データセット2の値は、1回だけ使用され、1回だけ使用されます。

    説明

    column4のすべての値をシャッフルする更新を行うため ランダムな方法で、いくつかの中間ステップが必要です。

    まず、dataset1の場合 、タプルのリスト(関係)を作成する必要があります(id, rn) 、それはただ:

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

    id_1 、...、id_20 dataset1に存在するIDです 。それらは任意のタイプにすることができ、連続している必要はなく、複合することもできます。

    dataset2の場合 、(column_1,rn)の別のリストを作成する必要があります 、次のようになります:

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

    この場合、2番目の列にはすべての値1 .. 20が含まれていますが、シャッフルされています。

    2つの関係ができたら、JOIN それらはON ... rn 。これにより、実際には、(id, column1)を持つタプルのさらに別のリストが生成されます。 、ペアリングがランダムに行われた場合。これらのペアを使用して、dataset1を更新します 。

    本当の質問

    これはすべて、CTE(WITH)を使用して行うことができます(明らかに、私は願っています)。 ステートメント)中間関係を保持する:

    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 ;
    

    トリックに注意してください によって実行されます:

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

    row_number() ウィンドウ関数 OVERのため、これらの番号はランダムにシャッフルされます。 句はすべてのデータを取得し、ランダムに並べ替えます。

    チェック

    もう一度確認できます:

    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
    

    代替

    これは、CTEの代わりに、単純な置換によってサブクエリを使用して実行することもできることに注意してください。これにより、場合によってはパフォーマンスが向上する可能性があります。

    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 ;
    

    そしてまた...

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

    セットアップ全体と実験は、 dbfiddle こちら で確認できます。

    注:非常に大きなデータセットでこれを行う場合は、極端に高速になるとは思わないでください。非常に大きなカードのデッキをシャッフルするのは費用がかかります。

    ケース2:データセット1の行数>データセット2の行

    この場合、column4の値 数回繰り返すことができます。

    私が考えることができる最も簡単な可能性(おそらく、効率的なものではありませんが、理解しやすい)は、関数random_column1を作成することです。 、VOLATILEとしてマークされています :

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

    そしてそれを使用して更新します:

    UPDATE
        dataset1
    SET
        column4 = random_column1();
    

    このように、dataset2からのいくつかの値 かもしれない まったく使用されませんが、他の人は 複数回使用する。

    dbfiddle こちら



    1. SQLRecoverableException:I / O例外:接続のリセット

    2. MySQL-JOINとWHEREを使用して平均時間を決定する

    3. 誤った結果のクエリを更新する

    4. 事前定義された値リストをSQLテーブルとして使用するSQLステートメント