[英]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_NO
在CARD_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.