【问题标题】:Populate random data from another table从另一个表填充随机数据
【发布时间】:2018-01-12 15:18:14
【问题描述】:
update dataset1.test
   set column4 = (select column1 
                 from dataset2
                 order by random()
                 limit 1
                 ) 

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

【问题讨论】:

标签: postgresql amazon-redshift


【解决方案1】:

设置

让我们首先假设您的表和数据如下。 请注意,我假设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 ;
|计数 | | ----: | | 20 |

案例 1:dataset1 中的行数

我们将执行一次完整的洗牌。 dataset2 中的值将被使用一次,并且不会超过一次。

解释

为了进行更新,将column4 中的所有值打乱 随机方式,我们需要一些中间步骤。

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

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

其中id_1, ..., id_20dataset1 上的ID。 它们可以是任何类型,不必连续,也可以是复合的。

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

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

在这种情况下,第二列包含所有值 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 ;

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

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

row_number() window function 产生与行数一样多的连续数字,从 1 开始。 这些数字是随机打乱的,因为OVER 子句获取所有数据并随机排序。

检查

我们可以再检查一下:

SELECT count(DISTINCT column4) FROM dataset1 ;
|计数 | | ----: | | 20 |
SELECT * FROM dataset1 ;
编号 |第 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 ;

又一次……

SELECT * FROM dataset1;
编号 |第 4 列 --: | :------------- 101 |东西 1011 102 |东西 1018 103 |东西 1007 ... 118 |东西 1020 119 |东西 1002 120 |东西 1016

您可以在 dbfiddle here

查看整个设置和实验

注意:如果您使用非常大的数据集执行此操作,请不要期望它非常快。洗一副非常大的牌很昂贵。


案例 2:dataset1 中的行数 > dataset2 中的行数

在这种情况下,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 here

【讨论】:

  • 嘿,你的查询似乎是合法的..但是我忘了提到 dataset1 大于 dataset2 ..所以例如 dataset2 可以有 50 个数据,如果 dataset1 有 100 个数据。 -> dataset1 应该使用来自 dataset2 的随机值更新,即使该值出现 10 次
  • 您应该编辑您的问题并添加这条信息。任何可能的答案都会受到很大影响。
  • 请参阅我的回答中的 案例 2
【解决方案2】:

更好的是从子查询中引用外部表。然后必须为每一行评估子查询:

update dataset1.test
   set column4 = (select
        case when dataset1.test.column4 = dataset1.test.column4
             then column1 end
        from dataset2
        order by random()
        limit 1
   )

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-06-07
    • 1970-01-01
    • 2011-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-02
    • 1970-01-01
    相关资源
    最近更新 更多