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