【问题标题】:Oracle Multi Row Update using Subquery使用子查询的 Oracle 多行更新
【发布时间】:2018-04-03 15:55:08
【问题描述】:

我正在尝试“洗牌”名称表中的 last_names 值。我想使用一个子选择查询来随机化名称的顺序并相应地更新它们。我想这样做是出于混淆的原因,但希望它仍然看起来像一个真实的数据集。

以下语句返回“ORA-01427:单行子查询返回多行”

我怎样才能做到这一点?

UPDATE schema.names set last_name = (
      SELECT * 
        FROM (
         SELECT last_name
         FROM schema.names
         ORDER BY DBMS_RANDOM.RANDOM))

【问题讨论】:

  • where rownum = 1添加到外部SELECT
  • @GordonLinoff 这会将所有姓氏设置为一个随机姓氏,对吗?我希望所有的名字都被打乱但独一无二。

标签: sql oracle sql-update


【解决方案1】:

这是一个打乱名称的查询:

select n.*, n2.name as new_name
from (select n.*, row_number() over (order by dbms_random.random) as seqnum
      from schema.names n
     ) n join
     (select n.*, row_number() over (order by dbms_random.random) as seqnum
      from schema.names n
     ) n2 
     on n.seqnum = n2.seqnum;

假设您有一个主键,您可以将其合并到 merge 中:

merge into schema.names n
    using (select n.*, n2.name as new_name
           from (select n.*, row_number() over (order by dbms_random.random) as seqnum
                 from schema.names n
                ) n join
                (select n.*, row_number() over (order by dbms_random.random) as seqnum
                 from schema.names n
                ) n2 
                on n.seqnum = n2.seqnum
          ) nn
    on n.? = nn.?
when matched then update
    set n.name = nn.new_name;

【讨论】:

  • 不适用于改组名称的查询,“ORA-24804:在另一个 OCI LOB 读/写流正在进行时调用 Lob 读/写函数”还需要是 DBMS_RANDOM.RANDOM 而不是 dbs .random.
【解决方案2】:

编辑:以下 PL/SQL 块使用 Gordon 的答案中的查询来驱动循环。

设置:

create table demo
( name_original varchar2(10)
, name_new      varchar2(10) );

-- Two columns initially the same so we can check the results:
insert into demo
select column_value, column_value
from table(sys.dbms_debug_vc2coll('Jim','James','Joe','Jenny','Jane','Jacky'));

代码:

begin
    for r in (
        select n.rowid as rwd
             , n.name_original
             , n2.name_new as name_shuffled
        from   ( select n.*, row_number() over(order by dbms_random.value) as seqnum
                 from   demo n ) n
               join  
               ( select n.*, row_number() over(order by dbms_random.value) as seqnum
                 from   demo n ) n2
               on n.seqnum = n2.seqnum
    )
    loop
        update demo set name_new = r.name_shuffled
        where  rowid = r.rwd;
    end loop;
end;

我的原始答案如下,对 OP 不是特别有帮助,但无论如何我认为它在技术上很有趣:


优化器意识到一个不相关的单行子查询只需要执行一次,因此您会得到一个值查找并应用于所有行。可能有一个提示可以防止这种情况发生,但我无法快速查看(no_mergeno_unnestrule 没有效果)。

以下方法有效(Oracle 12.1),但仅通过施加新旧名称必须不同的附加规则,使其成为优化器必须为每一行评估的相关子查询。 (它也会产生重复,因为每个随机查找都是独立的,所以它可能对你没有用。)

update demo d set name_new =
       ( select name_new
         from   demo d2
         where  d2.name_new <> d.name_new
         order by dbms_random.random
         fetch first row only );

select * from demo;

NAME_ORIGINAL NAME_NEW
------------- ----------
Jim           Jenny
James         Jane
Joe           Jacky
Jenny         Jane
Jane          Jacky
Jacky         Jim

冒着偏离主题的风险,请注意添加谓词 where d2.name_new &lt;&gt; d.name_new 如何改变执行计划:

Plan hash value: 1813657616

-----------------------------------------------------------------------------------------------
| Id  | Operation                    | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT             |      |      1 |        |      0 |00:00:00.01 |      27 |
|   1 |  UPDATE                      | DEMO |      1 |        |      0 |00:00:00.01 |      27 |
|   2 |   TABLE ACCESS STORAGE FULL  | DEMO |      1 |     82 |      6 |00:00:00.01 |       7 |
|*  3 |   VIEW                       |      |      1 |      1 |      1 |00:00:00.01 |       7 |
|*  4 |    WINDOW SORT PUSHED RANK   |      |      1 |     82 |      1 |00:00:00.01 |       7 |
|   5 |     TABLE ACCESS STORAGE FULL| DEMO |      1 |     82 |      6 |00:00:00.01 |       7 |
-----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   3 - filter("from$_subquery$_003"."rowlimit_$$_rownumber"<=1)
   4 - filter(ROW_NUMBER() OVER ( ORDER BY "DBMS_RANDOM"."RANDOM"())<=1)

到这里:

Plan hash value: 1813657616
--------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
--------------------------------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT             |      |      1 |        |      0 |00:00:00.01 |      62 |       |       |          |
|   1 |  UPDATE                      | DEMO |      1 |        |      0 |00:00:00.01 |      62 |       |       |          |
|   2 |   TABLE ACCESS STORAGE FULL  | DEMO |      1 |     82 |      6 |00:00:00.01 |       7 |  1025K|  1025K|          |
|*  3 |   VIEW                       |      |      6 |      1 |      6 |00:00:00.01 |      42 |       |       |          |
|*  4 |    WINDOW SORT PUSHED RANK   |      |      6 |      4 |      6 |00:00:00.01 |      42 |  2048 |  2048 | 2048  (0)|
|*  5 |     TABLE ACCESS STORAGE FULL| DEMO |      6 |      4 |     30 |00:00:00.01 |      42 |  1025K|  1025K|          |
--------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   3 - filter("from$_subquery$_003"."rowlimit_$$_rownumber"<=1)
   4 - filter(ROW_NUMBER() OVER ( ORDER BY "DBMS_RANDOM"."RANDOM"())<=1)
   5 - filter("D2"."NAME_NEW"<>:B1)

这是同一执行计划(计划哈希值 1813657616)做两件完全不同的事情的一个很好的例子,如果你想要其中之一的话。

(如果有一个提示做同样的事情,它将成为一个改变结果的提示的简洁示例。)

【讨论】:

    猜你喜欢
    • 2011-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-01
    • 2013-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多