繁体   English   中英

替换 Oracle 中的循环更新

[英]Substitute in-loop update in Oracle

如果可能,请帮助我:) 我想从这里编写一个更新语句(无循环):

BEGIN 

  FOR CRD IN ( SELECT CARD_NO
               FROM CARDS )
  LOOP

    UPDATE CARD_NO_MAP SET CARD_NO_MAP.NEW_CARD_NO = CRD.CARD_NO
      WHERE ( CARD_NO_MAP.NEW_CARD_NO IS NULL )
        AND ( CARD_NO_MAP.PREFIX = CASE WHEN ( CRD.CARD_NO LIKE '123%' ) THEN 555
                                        WHEN ( CRD.CARD_NO LIKE '456%' ) THEN 666
                                                                         ELSE -1 
                                   END )
        AND ( ROWNUM = 1 );

  END LOOP;

END;

如果它不够清楚它的作用,我会尝试解释。


编辑:

一开始我们有这样的事情:

CARD_NO_MAP:
PREFIX  CARD_NO NEW_CARD_NO
---------------------------
555     000000  NULL
555     111111  NULL
555     222222  NULL
555     333333  NULL
555     444444  NULL
555     555555  NULL
555     666666  NULL
666     111111  NULL
666     222222  NULL
666     333333  NULL
666     444444  NULL

CARDS:
CARD_NO
-----------
1231263
1234566
1236547
1236549
4564566
4560001
4561234

以及更新后的CARD_NO_MAP

PREFIX  CARD_NO NEW_CARD_NO
---------------------------
555     000000  1231263
555     111111  1234566
555     222222  1236547
555     333333  1236549
555     444444  NULL
555     555555  NULL
555     666666  NULL
666     111111  4564566
666     222222  4560001
666     333333  4561234
666     444444  NULL

每个CARDS.CARD_NO和所有PREFIX || CARD_NO一样都是独一无二的 PREFIX || CARD_NOCARD_NO_MAP表中是唯一的。 (不要问为什么分开...)

由于不同且可以说更简单,方法是使用合并。 您无法合并到视图中,但可以通过连接两个表并对结果应用密集秩分析函数来创建映射:

select cnm.prefix, cnm.card_no, c.card_no as new_card_no,
  dense_rank() over (partition by cnm.prefix order by c.card_no) rnk1,
  dense_rank() over (partition by c.card_no order by cnm.prefix, cnm.card_no) rnk2
from card_no_map cnm
join cards c
on case when c.card_no like '123%' then 555
        when c.card_no like '456%' then 666
        else -1 end = cnm.prefix
where cnm.new_card_no is null;

    PREFIX CARD_NO          NEW_CARD_NO            RNK1       RNK2
---------- ---------------- ---------------- ---------- ----------
       555 444444           1231263                   1          5
       555 333333           1231263                   1          4
       555 222222           1231263                   1          3
       555 111111           1231263                   1          2
       555 000000           1231263                   1          1
       555 555555           1231263                   1          6
       555 666666           1231263                   1          7
       555 000000           1234566                   2          1
       555 111111           1234566                   2          2
       555 222222           1234566                   2          3
       555 333333           1234566                   2          4
...
       666 222222           4564566                   3          3
       666 333333           4564566                   3          4

 40 rows selected 

... 相同日期生成 40 行,因为每个新卡号根据前缀计算映射到每个旧卡号; 但是分析列具有独特的组合,因此您可以进行过滤而不是查找匹配的结果:

select prefix, card_no, new_card_no
from (
  select cnm.prefix, cnm.card_no, c.card_no as new_card_no,
    dense_rank() over (partition by cnm.prefix order by c.card_no) rnk1,
    dense_rank() over (partition by c.card_no order by cnm.prefix, cnm.card_no) rnk2
  from card_no_map cnm
  join cards c
  on case when c.card_no like '123%' then 555
          when c.card_no like '456%' then 666
          else -1 end = cnm.prefix
  where cnm.new_card_no is null
)
where rnk1 = rnk2;

    PREFIX CARD_NO          NEW_CARD_NO    
---------- ---------------- ----------------
       555 000000           1231263         
       555 111111           1234566         
       555 222222           1236547         
       555 333333           1236549         
       666 000000           4560001         
       666 111111           4561234         
       666 222222           4564566         

 7 rows selected 

...看起来更有希望。 然后可以在合并中用作using子句:

merge into card_no_map cnm
using (
  select prefix, card_no, new_card_no
  from (
    select cnm.prefix, cnm.card_no, c.card_no as new_card_no,
      dense_rank() over (partition by cnm.prefix order by c.card_no) rnk1,
      dense_rank() over (partition by c.card_no order by cnm.prefix, cnm.card_no) rnk2
    from card_no_map cnm
    join cards c
    on case when c.card_no like '123%' then 555
            when c.card_no like '456%' then 666
            else -1 end = cnm.prefix
    where cnm.new_card_no is null
  )
  where rnk1 = rnk2
) tmp
on (tmp.prefix = cnm.prefix and tmp.card_no = cnm.card_no)
when matched then update set cnm.new_card_no = tmp.new_card_no;

7 rows merged.

select * from card_no_map;

    PREFIX CARD_NO          NEW_CARD_NO    
---------- ---------------- ----------------
       555 000000           1231263         
       555 111111           1234566         
       555 222222           1236547         
       555 333333           1236549         
       555 444444                           
       555 555555                           
       555 666666                           
       666 000000           4560001         
       666 111111           4561234         
       666 222222           4564566         
       666 333333                           

或者您可以使用相同的子查询进行直接更新:

update card_no_map cnm
set new_card_no = (
  select new_card_no
  from (
    select cnm.prefix, cnm.card_no, c.card_no as new_card_no,
      dense_rank() over (partition by cnm.prefix order by c.card_no) rnk1,
      dense_rank() over (partition by c.card_no order by cnm.prefix, cnm.card_no) rnk2
    from card_no_map cnm
    join cards c
    on case when c.card_no like '123%' then 555
            when c.card_no like '456%' then 666
            else -1 end = cnm.prefix
    where cnm.new_card_no is null
  ) t
  where t.rnk1 = t.rnk2
  and t.prefix = cnm.prefix
  and t.card_no = cnm.card_no
)
where cnm.new_card_no is null;

我的其他答案正在使用类似的分析方法,但结果代码(带有合并或更新)可以说更简单一些,因为它没有 CTE; 但是它可能会使用更多资源,因为第一个中间结果集可能很大。

cards的唯一值分配给card_no_map每个空行会使相关更新变得有点混乱。 您无法更新或合并到视图中,但您可以让相关性计算出行之间的匹配。

您可以根据其前缀为每个地图分配一个名义行号:

select rowid as rid,
  prefix,
  row_number() over (partition by prefix order by rowid) as rn
from card_no_map
where new_card_no is null

您可以根据要匹配的前缀为每张卡分配一个名义行号:

select card_no,
  case when card_no like '123%' then 555
    when card_no like '456%' then 666
    else -1 end  as prefix,
  row_number() over (partition by case when card_no like '123%' then 555
    when card_no like '456%' then 666
    else -1 end order by card_no) as rn
from cards

然后,您可以在更新子查询中使用这两个查询作为 CTE(或内联视图,如果您愿意),将它们连接到前缀和名义行号,然后将结果与通过其 rowid 更新的行相关联:

update card_no_map cnm
set cnm.new_card_no = (
  with t1 as (
    select rowid as rid,
      prefix,
      row_number() over (partition by prefix order by card_no) as rn
    from card_no_map
    where new_card_no is null
  ),
  t2 as (
    select card_no,
      case when card_no like '123%' then 555
        when card_no like '456%' then 666
        else -1 end  as prefix,
      row_number() over (partition by case when card_no like '123%' then 555
        when card_no like '456%' then 666
        else -1 end order by card_no) as rn
    from cards
  )
  select t2.card_no
  from t1 join t2 on t2.prefix = t1.prefix and t2.rn = t1.rn
  where t1.rid = cnm.rowid
)
where cnm.new_card_no is null;

使用您添加到问题中的示例数据,结果为:

select * from card_no_map;

    PREFIX CARD_NO          NEW_CARD_NO    
---------- ---------------- ----------------
       555 000000           1231263         
       555 111111           1234566         
       555 222222           1236547         
       555 333333           1236549         
       555 444444                           
       555 555555                           
       555 666666                           
       666 000000           4560001         
       666 111111           4561234         
       666 222222           4564566         
       666 333333                           

分配卡号的顺序与您的示例输出不匹配,但我只是根据rowid进行排序; 如果您有更好的订购方式,请更改row_number()以执行此操作。

我觉得有一种更简单的方法可以做到这一点,但目前我无法做到……不得不再次点击地图表似乎效率不高。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM