从另一个表填充随机数据

Kar*_*hic 5 postgresql amazon-redshift

update dataset1.test
   set column4 = (select column1 
                 from dataset2
                 order by random()
                 limit 1
                 ) 
Run Code Online (Sandbox Code Playgroud)

我必须更新第4列的dataset1,每一行都更新数据集2列中的一个随机条目。但是到目前为止,在上述查询中,我在dataset1的所有行中都只有一个随机条目,而我希望它都相同是随机的。

joa*_*olo 5

设定

让我们开始假设您的表数据如下。请注意,我假设dataset1有一个主键(它可以是一个复合键,但是为了简单起见,让我们将其设为整数):

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

CREATE TABLE dataset2
(
    column1 TEXT
) ;
Run Code Online (Sandbox Code Playgroud)

我们在两个表中都填充了示例数据

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) ;
Run Code Online (Sandbox Code Playgroud)

完整性检查:

SELECT count(DISTINCT column4) FROM dataset1 ;
Run Code Online (Sandbox Code Playgroud)
| 计数|
| ----:|
| 20 |

情况1:数据集1中的行数<=数据集2中的行数

我们将进行完整的改组。来自dataset2的值将被使用一次,并且不超过一次。

说明

为了进行更新column4以随机方式随机整理所有值,我们需要一些中间步骤。

首先,对于dataset1,我们需要创建一个元组列表(关系)(id, rn),它们只是:

(id_1,   1),
(id_2,   2),
(id_3,   3),
...
(id_20, 20)
Run Code Online (Sandbox Code Playgroud)

id_1... id_20上的ID 在哪里dataset1。它们可以是任何类型,不需要连续,也可以是复合的。

对于dataset2,我们需要创建的另一个列表(column_1,rn),如下所示:

(column1_1,  17),
(column1_2,   3),
(column1_3,  11),
...
(column1_20, 15)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,第二列包含所有值1 .. 20,但被重新排序。

一旦我们建立了两种关系,我们就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 ;
Run Code Online (Sandbox Code Playgroud)

请注意,该技巧是通过以下方式执行的:

row_number() OVER (ORDER BY random()) AS rn
Run Code Online (Sandbox Code Playgroud)

row_number()窗函数,因为有行,从1开始这些号码被随机洗牌的,因为产生许多连续的数字OVER子句了所有的资料和随机进行排序。

检查

我们可以再次检查:

SELECT count(DISTINCT column4) FROM dataset1 ;
Run Code Online (Sandbox Code Playgroud)
| 计数|
| ----:|
| 20 |
SELECT * FROM dataset1 ;
Run Code Online (Sandbox Code Playgroud)
id | 专栏4       
-:| :-------------
101 | 东西1016
102 | 东西1009
103 | 东西1003
...
118 | 东西1012
119 | 东西1017
120 | 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 ;
Run Code Online (Sandbox Code Playgroud)

然后再次...

SELECT * FROM dataset1;
Run Code Online (Sandbox Code Playgroud)
id | 专栏4       
-:| :-------------
101 | 1011的东西
102 | 东西1018
103 | 东西1007
...
118 | 东西1020
119 | 东西1002
120 | 东西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 ;
$$ ;
Run Code Online (Sandbox Code Playgroud)

并使用它来更新:

UPDATE
    dataset1
SET
    column4 = random_column1();
Run Code Online (Sandbox Code Playgroud)

这样,dataset2 可能根本不会使用from中的某些值,而其他值 被多次使用。

dbfiddle 在这里