【问题标题】:Replacing values with random values from other table. Oracle用其他表中的随机值替换值。甲骨文
【发布时间】:2016-01-28 16:56:54
【问题描述】:

我想用另一个表中的随机名称更新表中的名称。表用户中有真实的用户名。表 tmp_users 中有组成的名称。我想用 tmp_users 表中的随机名称更新用户表中的所有名称。这个想法是用假客户匿名化真实的生产数据。 tmp_users 表中的条目较少,因此我认为我无法关联 id。

我遇到的问题是所有用户都设置为相同的名称。

一些样本数据:

create table users
(
  name varchar2(50)
);

create table tmp_users
(
  name varchar2(50)
);

insert into users values ('Cora');
insert into users values ('Rayna');
insert into users values ('Heidi');
insert into users values ('Gilda');
insert into users values ('Dorothy');
insert into users values ('Elena');
insert into users values ('Providencia');
insert into users values ('Louetta');
insert into users values ('Portia');
insert into users values ('Rodrick');
insert into users values ('Rocco');
insert into users values ('Nelson');
insert into users values ('Derrick');
insert into users values ('Everett');
insert into users values ('Nisha');
insert into users values ('Amy');
insert into users values ('Hyun');
insert into users values ('Brendon');
insert into users values ('Gabriela');
insert into users values ('Melina');

insert into tmp_users values ('Snow White');
insert into tmp_users values ('Cinderella');
insert into tmp_users values ('Aurora');
insert into tmp_users values ('Ariel');
insert into tmp_users values ('Belle');
insert into tmp_users values ('Jasmine');
insert into tmp_users values ('Pocahontas');
insert into tmp_users values ('Mulan');
insert into tmp_users values ('Tinker Bell');
insert into tmp_users values ('Anna');
insert into tmp_users values ('Elsa');

--Wrong, sets all users to the same random name
update users set name = (select name from (select name from tmp_users order by sys_guid()) where rownum = 1);

--Wrong, sets all users to the same random name
update users set name = (select name from (select name from tmp_users order by dbms_random.value) where rownum = 1);

当这样做时:

select * from users;

我得到的结果是这样的,我不想要。

Cinderella
Cinderella
Cinderella
Cinderella
Cinderella
...

我想为用户表中的每一行分配一个随机名称。所有行的名称不同。我想要这样的东西:

Mulan
Cinderella
Belle
Elsa
Jasmine
Tinker Bell
...

知道如何做到这一点吗?我正在使用 Oracle Database 11g Express Edition 11.2.0.2.0。使用光标很容易做到,但我试图弄清楚如何通过设置操作来做到这一点。

更新:

我现在已经在两个不同的 Oracle 版本上进行了测试。相关子查询解决方案不适用于 Oracle Database 11g Express Edition 11.2.0.2.0。 但有时它确实适用于 Oracle Database 11g 企业版 11.2.0.4.0。在一张桌子上它一直有效,而在另一张桌子上它从不工作。

【问题讨论】:

    标签: sql oracle


    【解决方案1】:

    使用 11.2.0.4 进行测试,如下工作,类似于 @VR46 的建议:

    SQL> UPDATE users u
      2     SET name = (SELECT name
      3                   FROM (SELECT NAME,
      4                                row_number() over(ORDER BY dbms_random.value) rn
      5                           FROM tmp_users) tu
      6                    WHERE u.name IS NOT NULL
      7                         AND rn = 1);
    20 rows updated
    
    SQL> select * from users;
    NAME
    --------------------------------------------------
    Ariel
    Aurora
    Belle
    Ariel
    Anna
    Mulan
    Aurora
    Ariel
    Mulan
    Tinker Bell
    Mulan
    Ariel
    Aurora
    Pocahontas
    Pocahontas
    Aurora
    Snow White
    Mulan
    Aurora
    Anna
    20 rows selected
    

    【讨论】:

    • 我的 Oracle Database 11g Express Edition 11.2.0.2.0 出现错误行为。用户表中的所有名称都具有相同的名称。当我自己运行内部选择时,它可以正常工作,但是当它用作相关子查询时却不能。也许这只是在我的版本中被破坏了,因为@Alex Poole 给出了一些关于 MOS 注释 420779.1 的提示。我自己无法获得 Oracle 支持,所以我不知道它到底在说什么。
    • 我得到与@johanrex 相同的结果。更新后的所有值都一样。
    【解决方案2】:

    我认为关联子查询将从子查询中提取随机名称

    UPDATE users 
    SET    name = (SELECT name 
                   FROM   (SELECT name 
                           FROM   tmp_users tu 
                           ORDER  BY Sys_guid()) 
                   WHERE  ROWNUM = 1
                     AND users.name <> name ); 
    

    【讨论】:

    • 在哪里(users.name = 'A' OR 1 = 1)?
    • 我不太了解相关逻辑,但它给出了以下错误:“SQL 错误:ORA-00904:“USERS”。“NAME”:无效标识符”
    • @user1073476 - 不确定如何在 Oracle 中执行此操作。但想法是将子查询与更新表相关联
    • @user1073476 - 尝试将条件移至外部查询
    • @VR46 你更新你的查询了吗?无论如何,我在外部查询中尝试了相关性,它的行为相同。
    【解决方案3】:

    您需要一些相关性,正如@VR46 建议的那样;但是 sys_guid() 对此不起作用(无论如何在 11gR2 中;我认为优化器可能出于某种原因在这种情况下只评估一次);你可以使用 dbms_random.value:

    update users u set name = (
      select name from (
        select name from tmp_users order by dbms_random.value
      )
      where rownum = 1 and u.name is not null
    );
    
    NAME                                             
    --------------------------------------------------
    Jasmine                                           
    Tinker Bell                                       
    Ariel                                             
    Elsa                                              
    Elsa                                              
    Elsa                                              
    Belle                                             
    Snow White                                        
    ...
    

    如果您不想要 aubquery,您可以改用保持密集排名:

    update users u set name = (
      select max(name) keep (dense_rank first order by dbms_random.value)
      from tmp_users
      where u.name is not null
    );
    
    NAME                                             
    --------------------------------------------------
    Mulan                                             
    Anna                                              
    Snow White                                        
    Elsa                                              
    Tinker Bell                                       
    Belle                                             
    Belle                                             
    Elsa                                              
    ...
    

    相关性必须是真实的;如果您的用户表中有 null 值,这会将它们更新为 null,如果这是一个问题,您可以使用不同的条件。

    从 cmets 看来,您在 11.2.0.2 中使用 dbms_random 时遇到的问题与在 11.2.0.3 和 11.2.0.4 中使用 sys_guid 时遇到的问题相同。如果您的用户表还有一个数字唯一/主键,例如 ID,您可以将其用于关联并将其传递给 value 函数,这可能会有所不同,但我没有没有合适的实例来测试:

    update users u set name = (
      select max(name) keep (dense_rank first order by dbms_random.value(0, u.id))
      from tmp_users
    );
    

    MOS 注释 420779.1 包括“在子查询中包含 DBMS_RANDOM.VALUE 可能会或可能不会工作,具体取决于选择的优化和执行代码路径”,这似乎是这里的问题。

    您也可以尝试使用合并的变体,例如(再次假设您可以使用一个 ID):

    merge into users u
    using (
      select u.id, max(tu.name) keep (dense_rank first 
        order by dbms_random.value(0, u.id)) as name
      from users u
      cross join tmp_users tu
      group by u.id
    ) tu
    on (tu.id = u.id)
    when matched then update set u.name = tu.name;
    

    但是,交叉连接可能会使这不切实际,具体取决于您在每个表中实际拥有的行数。

    【讨论】:

    • 你试过这些例子吗?据我所知,两者都会产生相同的错误结果。
    • @user1073476 - 我做到了。两者都在 11.2.0.4.7 中产生了随机值。哪个版本不支持?
    • 我使用的是 Oracle Database 11g Express Edition 11.2.0.2.0。当我执行“从用户中选择 *”时,所有用户都具有相同的名称。
    • @user1073476 - 我没有那个版本可以检查;也适用于 11.2.0.3,这对您没有帮助。
    • @user1073476 - 不确定它是否仍然有效,但您可以尝试在真实表上使用唯一/主键作为value 的参数,看看这是否会使优化器以不同的方式处理它.很遗憾,我无法测试。
    【解决方案4】:

    这是另一种方法,它使用 rowid 和模数:

    MERGE into users_tab u
    USING (
        select 
            actual.row_id as actual_rowid,
            actual.rnum actual_rnum, 
            actual.name actual_name, 
            fake.rnum fake_rnum, 
            fake.name fake_name, 
            mod(actual.rnum, fake_count.cnt) modulus
        from
        (
        select rownum rnum, name, rowid as row_id
        from users_tab
        ) actual,
        (
        select rownum-1 rnum, name
        from (select distinct name from tmp_users_tab)
        ) fake,
        (select count(distinct name) cnt from tmp_users_tab) fake_count
        where mod(actual.rnum, fake_count.cnt) = fake.rnum
    ) x
    ON (x.actual_rowid = u.rowid)
    WHEN MATCHED THEN UPDATE
        set name = x.fake_name;
    

    但不确定这将如何在非常大的用户表上执行。它不是随机的,而是遵循一系列假名。因此,如果您有 10 个假名,则用户中的记录 1->10 将被分配假名 1->10,而用户 11 将以假名 #1 重新开始。

    USING 查询有额外的测试字段。

    【讨论】:

      猜你喜欢
      • 2017-09-05
      • 2018-05-21
      • 2018-04-03
      • 2022-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-20
      • 1970-01-01
      相关资源
      最近更新 更多