问题描述
今天跑批过程报错了,查看了日志,报错代码为:ORA-30926: 无法在源表中获得一组稳定的行,发现是使用merge更新数据导致的。
为了解决这个问题,得先弄明白merge的逻辑。merge into是insert与update语句的结合,可以同时实现update和insert的功能,语法大致如下
merge into 目标表 a
using (
源表
) b
on (源表与目标表的关联条件) --关联字段用目标表的主键
when matched then update --当源表通过关联字段能匹配到目标表时,执行更新操作
set 用源表字段值更新目标表字段值
when not matched then insert --当源表通过关联字段不能匹配到目标表时,执行插入操作
(
目标表字段
)
values
(
源表对应的字段
)
;
commit;
merge into会将源表的每一条记录和目标表按关联字段匹配,目标表被匹配到的记录会更新数据,匹配不到的记录话就会把源表这些数据插入目标表,匹配的前提是关联字段要是目标表与源表的主键。
所以,如果源表的关联字段有重复值的话,程序就不知道该选择哪条去更新目标表,因此就会抛出如上所述之错误。为了解决这个问题,我们先来复现一下这个错误。
--drop table merge_target_test;
--创建目标表
create table merge_target_test(
D_DATA_DT date --数据日期
,V_TRADE_NO varchar2(10) --交易编号
,V_SEC_NO varchar2(10) --证券代码
,N_PV numeric(24,6) --估值结果
);
--drop table merge_source_test;
--创建源表
create table merge_source_test(
D_DATA_DT date --数据日期
,V_TRADE_NO varchar2(10) --交易编号
,V_SEC_NO varchar2(10) --证券代码
,N_PV numeric(24,6) --估值结果
);
--目标表插入一条数据
insert into merge_target_test(D_DATA_DT,V_TRADE_NO,V_SEC_NO,N_PV)
values(TO_DATE('20210101', 'YYYY-MM-DD'),'No_Test01','000905.SH',100.00);commit;
--目标表插入两条交易编号一样的数据(制造关联字段主键重复)
insert into merge_source_test(D_DATA_DT,V_TRADE_NO,V_SEC_NO,N_PV)
values(TO_DATE('20210101', 'YYYY-MM-DD'),'No_Test01','000905.SH',200.00);commit;
insert into merge_source_test(D_DATA_DT,V_TRADE_NO,V_SEC_NO,N_PV)
values(TO_DATE('20210101', 'YYYY-MM-DD'),'No_Test01','000905.SH',300.00);commit;
--执行merge操作
merge into merge_target_test a
using (
select *
from merge_source_test
) b
on (a.d_data_dt=b.d_data_dt and a.v_trade_no = b.v_trade_no) --关联字段用目标表的主键
when matched then update
set --更新字段值(注释掉关联字段)
--a.D_DATA_DT = b.D_DATA_DT
--,a.V_TRADE_NO = b.V_TRADE_NO
a.V_SEC_NO = b.V_SEC_NO
,a.N_PV = b.N_PV
when not matched then insert
(
a.D_DATA_DT
,a.V_TRADE_NO
,a.V_SEC_NO
,a.N_PV
)
values
(
b.D_DATA_DT
,b.V_TRADE_NO
,b.V_SEC_NO
,b.N_PV
)
;
commit;
执行完上面merge代码就会抛出ORA-30926错误。
解决方案
这个问题解决办法是删除源表里面主键重复的数据,或者对于关联字段重新给一组主键,以使源表数据按主键字段不会出现重复即可。
如下代码可以正常执行,并且更新了目标表已有数据,插入了没有的数据。
--清空目标表与源表
truncate table merge_target_test;
truncate table merge_source_test;
--目标表插入一条数据
insert into merge_target_test(D_DATA_DT,V_TRADE_NO,V_SEC_NO,N_PV)
values(TO_DATE('20210101', 'YYYY-MM-DD'),'No_Test01','000905.SH',100.00);commit;
--目标表插入交易编号不重复
insert into merge_source_test(D_DATA_DT,V_TRADE_NO,V_SEC_NO,N_PV)
values(TO_DATE('20210101', 'YYYY-MM-DD'),'No_Test01','000905.SH',200.00);commit;
insert into merge_source_test(D_DATA_DT,V_TRADE_NO,V_SEC_NO,N_PV)
values(TO_DATE('20210101', 'YYYY-MM-DD'),'No_Test02','000905.SH',300.00);commit;
--执行merge操作
merge into merge_target_test a
using (
select *
from merge_source_test
) b
on (a.d_data_dt=b.d_data_dt and a.v_trade_no = b.v_trade_no) --关联字段用目标表的主键
when matched then update
set --更新字段值(注释掉关联字段)
--a.D_DATA_DT = b.D_DATA_DT
--,a.V_TRADE_NO = b.V_TRADE_NO
a.V_SEC_NO = b.V_SEC_NO
,a.N_PV = b.N_PV
when not matched then insert
(
a.D_DATA_DT
,a.V_TRADE_NO
,a.V_SEC_NO
,a.N_PV
)
values
(
b.D_DATA_DT
,b.V_TRADE_NO
,b.V_SEC_NO
,b.N_PV
)
;
commit;