替换Oracle中的循环内更新

时间:2016-04-22 14:07:22

标签: sql oracle loops oracle11g

如果有可能请帮帮我:)我想写一个更新语句(无循环):

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都与CARD_NO_MAP表中唯一的cd /home/*your_app_name*/app/programs/server/npm/mode_modules/meteor/npm-bcrypt npm install bcrypt 唯一一样唯一。 (不要问为什么会这样做......)

2 个答案:

答案 0 :(得分:1)

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

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;但是它可能会使用更多资源,因为第一个中间结果集可能很大。

答案 1 :(得分:0)

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()以执行此操作。

我觉得有一种更简单的方法可以做到这一点但是现在却让我不知所措......不得不再次点击地图表似乎没有效率。