如何在保持数据集不变的同时调整列值

hwazgwia  于 2022-10-15  发布在  PostgreSQL
关注(0)|答案(2)|浏览(217)

我想从Postgres数据库的表中洗牌一些列。我有两百万排。我需要通过另一个更新所有非空值。
我需要保留相同的数据集。不可能有两次相同的值。不可能用NEXT值交换数据,因为如果我对另一列执行相同的过程,我将保留相同的链接。这是为了匿名我的数据库。只需对数据进行混洗并保留数据集。
示例(更改名字和姓氏):
Id|Firstname|姓
-|-|
1|Albert|Einsten
2|艾萨克|牛顿
3||居里夫人
4|Alexandre|格雷厄姆·贝尔
5|托马斯|爱迪生
无序排列名字列:
Id|Firstname|姓
-|-|
1|艾萨克|格雷厄姆·贝尔
2|阿尔伯特|爱迪生
3||Einsten
4|托马斯|牛顿
5|Alexandre|居里夫人
如何以快速的过程做到这一点?

6qfn3psc

6qfn3psc1#

这将以完全随机的方式调整列firstname中的值:

UPDATE test t0
SET    firstname = t2.firstname
FROM  (SELECT row_number() OVER (ORDER BY random()) AS rn, id        FROM test WHERE firstname IS NOT NULL) t1
JOIN  (SELECT row_number() OVER (ORDER BY random()) AS rn, firstname FROM test WHERE firstname IS NOT NULL) t2 USING (rn)
WHERE  t0.id = t1.id
AND    t0.firstname IS NOT NULL;

“完全随机”包括某些列可能保留其原始值。(排得越多,机会就越小。)这实际上是数据匿名化的最佳选择。那么这些值就是真正的随机的。如果我们强制切换,读取器将获得与给定ID关联的不同值的最低信息。
它还遵守了您令人惊讶的规则,即只对非空值进行混洗。
对需要洗牌的每一列重复上述步骤。
在不排除NULL值的情况下,对于任意数量的列,此单个查询的运行成本更低:

UPDATE test t0
SET    firstname = t2.firstname
     , lastname  = t3.lastname
FROM  (SELECT row_number() OVER (ORDER BY random()) AS rn, id        FROM test) t1
JOIN  (SELECT row_number() OVER (ORDER BY random()) AS rn, firstname FROM test) t2 USING (rn)
JOIN  (SELECT row_number() OVER (ORDER BY random()) AS rn, lastname  FROM test) t3 USING (rn)
WHERE  t0.id = t1.id;

如果id是一个无间隙序列,我们可以把t1从方程中去掉,然后把t0.idt2.rn结合起来。(排除空值时不起作用。)
fiddle

tf7tbtn2

tf7tbtn22#

考虑到更新后的需求,使用Erwin Brandstetter的解决方案这样的策略可能会更好,该解决方案可以轻松地应用于任意数量的列,但是我将把我最初的答案留给第二篇专栏的更新。

原答案(要求洗一栏):

鉴于您对洗牌顺序的要求非常笼统,我不确定这实际上会有多大帮助,但我认为它回答了您的问题:

update test
SET firstname = t2.firstname
FROM
(
 SELECT id, COALESCE(LAG(firstname, 1) OVER (ORDER BY id RANGE UNBOUNDED PRECEDING), LAST_VALUE(firstname) OVER (ORDER BY id RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)) firstname
 FROM test t2
 WHERE firstname IS NOT NULL
) t2
WHERE test.id = t2.id

这里的想法是,内部查询将值移位1(忽略空值)。使用COALESCE是因为第一个没有进行中的条目,所以它依赖于某个LAST_VALUE逻辑来获得最后一个值(即,它的行为就像移位循环一样)。
周围的UPDATE语句将测试连接到子查询,以实际更新数据。
你可以看到它在工作。

已更新(要求同时洗牌第二列):

考虑到还需要对第二个字段进行改组的更新要求,您可以在其中应用不同的逻辑:

update test
SET firstname = t2.firstname,
  lastname = t2.lastname
FROM
(
 SELECT id, 
  COALESCE(LAG(firstname, 1) OVER (ORDER BY id), LAST_VALUE(firstname) OVER (ORDER BY id RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)) firstname,
  COALESCE(LEAD(lastname, 1) OVER (ORDER BY id), FIRST_VALUE(lastname) OVER (ORDER BY id RANGE UNBOUNDED PRECEDING)) lastname
 FROM test t2
 WHERE firstname IS NOT NULL AND lastname IS NOT NULL
) t2
WHERE test.id = t2.id

这只是在相反方向上移动lastname,因此从前一个非空行获取firstname(环绕),从下一个非空行获取lastname(环绕)。这两列都将更改。
这是一个fiddle of it working

相关问题