簡體   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