调用Oracle存储过程报ORA-01422错误 重复ID写入cmb_reject表问题
ORA-01422报错根因
报错的直接原因是你写的select e_id into lv_count from cmb_staging;语句返回了多行数据:INTO语法要求查询结果必须唯一且只有1行,而你的cmb_staging表有多条记录,这条查询会返回所有行的e_id,自然触发多行返回错误。同时这条查询的逻辑本身也不符合你的需求,你原本想要统计重复e_id的数量,实际写的是查询全表e_id值,逻辑完全不匹配。
此外你原代码还有多个逻辑漏洞:
- 没有对重复
e_id的记录做校验失败标记,仅做了空值、长度超限的校验 - IF分支逻辑完全错误,哪怕计数逻辑改对也无法实现你要的「重复记录进reject表、合法记录进目标表」的需求
- 直接用未去重的数据源跑MERGE,必然会触发ORA-30926(你提到的ORA-30296属于笔误),因为同一个目标行匹配到多个源行时,Oracle无法确定用哪个源数据更新。
修复后的完整存储过程
调整逻辑为:先做全量校验(含空值、长度超限、e_id重复三种规则),校验失败的记录统一进reject表,校验通过的唯一e_id记录走MERGE入目标表,避免MERGE报错。
create or replace procedure sp_dup_rec(ov_err_msg OUT varchar2) is lv_succ_rec number(30); lv_fail_rec number(30); begin lv_succ_rec := 0; lv_fail_rec := 0; -- 第一步:基础校验:e_id为空/长度超过4,标记为失败 UPDATE cmb_staging SET validation_status = 'Fail', validation_result = CASE WHEN e_id IS NULL THEN 'Id is not present' ELSE 'Id is longer than expected' END WHERE e_id is null OR LENGTH(e_id) > 4; -- 第二步:重复e_id校验:所有出现次数>1的e_id对应的记录,标记为失败 UPDATE cmb_staging s SET validation_status = 'Fail', validation_result = 'e_id重复' WHERE validation_status IS NULL -- 已经标记失败的不用再判断 AND EXISTS ( SELECT 1 FROM cmb_staging s2 WHERE s2.e_id = s.e_id GROUP BY s2.e_id HAVING COUNT(*) > 1 ); -- 第三步:所有校验失败的记录插入reject表 INSERT INTO cmb_reject SELECT s.* FROM cmb_staging s WHERE validation_status = 'Fail'; lv_fail_rec := SQL%ROWCOUNT; -- 第四步:校验通过的记录(e_id合法且唯一)MERGE到目标表,不会再触发MERGE重复匹配错误 MERGE INTO cmb_target t USING ( SELECT e_id, e_name, e_loc FROM cmb_staging WHERE validation_status IS NULL ) S ON (t.e_id = S.e_id) WHEN MATCHED THEN UPDATE SET t.e_name = s.e_name, t.e_loc = s.e_loc WHEN NOT MATCHED THEN INSERT (t.e_id,t.e_name,t.e_loc) VALUES (s.e_id,s.e_name,s.e_loc); lv_succ_rec := SQL%ROWCOUNT; -- 第五步:写入汇总表 dbms_output.put_line('Inserting into Summary table'); INSERT INTO summary_tab( tot_records, success_records, failed_records, process_status ) values ( lv_succ_rec + lv_fail_rec, lv_succ_rec, lv_fail_rec, 'COMPLETED' ); COMMIT; ov_err_msg := 'Procedure completed succesfully'; EXCEPTION WHEN OTHERS THEN ov_err_msg := 'Procedure end up with errors:'|| sqlerrm; ROLLBACK; END sp_dup_rec;
修复后验证
你可以直接用原来的调用语句执行,你的测试数据中e_id=1、e_id=2都是重复的,会被标记为失败进reject表,只有e_id=3的唯一合法记录会进入目标表,不会再出现ORA-01422和MERGE相关报错。
内容的提问来源于stack exchange,提问作者Vicky
相关产品推荐
相关产品推荐

