获取 ORA-01422: 调用过程时精确提取 returns 多于请求的行数
getting ORA-01422: exact fetch returns more than requested number of rows while calling a procedure
CREATE TABLE cmb_staging (
e_id NUMBER(10),
e_name VARCHAR2(30),
e_loc VARCHAR2(30),
validation_status VARCHAR2(30),
validation_result VARCHAR2(30)
);
insert into cmb_staging values(1,'A','AA',null,null);
insert into cmb_staging values(1,'B','BB',null,null);
insert into cmb_staging values(2,'C','CC',null,null);
insert into cmb_staging values(2,'D','DD',null,null);
insert into cmb_staging values(3,'A','AA',null,null);
CREATE TABLE cmb_target (
e_id NUMBER(10),
e_name VARCHAR2(30),
e_loc VARCHAR2(30)
);
CREATE TABLE cmb_reject (
e_id NUMBER(10),
e_name VARCHAR2(30),
e_loc VARCHAR2(30),
validation_status VARCHAR2(30),
validation_result VARCHAR2(30)
);
CREATE TABLE SUMMARY_TAB
( TOT_RECORDS NUMBER(10,0),
SUCCESS_RECORDS NUMBER(10,0),
FAILED_RECORDS NUMBER(10,0),
PROCESS_STATUS VARCHAR2(30)
);
程序:
create or replace procedure sp_dup_rec(ov_err_msg OUT varchar2)
is
lv_succ_rec number(30);
lv_fail_rec number(30);
lv_count number(30);
begin
lv_succ_rec := 0;
lv_fail_rec := 0;
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;
--If there are duplicates id then it should go into cmb_reject table
select e_id into lv_count from cmb_staging;
if lv_count < 1 then
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;
else
insert into cmb_reject
select s.*
from cmb_staging s
WHERE validation_status = 'Fail';
lv_fail_rec := SQL%ROWCOUNT;
end if;
dbms_output.put_line('Inserting into Summary table');
insert into summary_tab(
tot_records,
success_records,
failed_records
) values (
lv_succ_rec + lv_fail_rec,
lv_succ_rec,
lv_fail_rec
);
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;
调用程序:
set serveroutput on;
declare
v_err_msg varchar2(100);
begin
sp_dup_rec(v_err_msg);
dbms_output.put_line(v_err_msg);
end;
您好团队,
调用过程时出现 ora-01422 错误。基本上,我想将重复记录插入 cmb_reject 选项卡,因为合并语句将失败,如果我只使用合并,我将得到 ora - 30296 错误。所以,我写了 if 条件,其中它将获取计数,如果计数更多,则将插入 cmb_reject tab
这是一个问题:
--If there are duplicates id then it should go into cmb_reject table
select e_id into lv_count from cmb_staging;
由于没有 WHERE
子句限制返回的行数,如果 cmb_staging
包含 2(或更多)行,查询将失败,因为您不能将那么多行放入标量 lv_count
变量。
看来你真的是想说
select count(*) into lv_count from cmb_staging;
正如下一行所说
if lv_count < 1 then
错误堆栈应该告诉您抛出错误的行。我的猜测是这条线是
select e_id
into lv_count
from cmb_staging;
因为该查询没有意义。如果 cmb_staging
中有多行,查询将抛出 ORA-01422 错误,因为您不能将多行放在一个标量变量中。您似乎极有可能希望您的查询至少执行 count
。并且很可能带有某种谓词。像
select count(*)
into lv_count
from cmb_staging;
会避免错误。但这可能没有意义,因为您在评论中说“它应该进入 cmb_reject table”,这意味着存在一个单一的对象。每当 cmb_staging
中有任何行时,此更改都会导致 lv_count > 0
,并且您似乎不太可能希望始终拒绝行。
我的粗略猜测是,当您说“重复”时,您的意思是“两行具有相同 e_id”。如果是这样
select e_id, count(*)
from cmb_staging
group by e_id
having count(*) > 1
将显示重复的 e_id
值。然后你可以
insert into cmb_reject
select s.*
from cmb_staging s
where e_id in (select s2.e_id
from cmb_staging s2
group by s2.e_id
having count(*) > 1);
如果您并不真正关心性能(根据您提出的问题,我假设您只是在学习 PL/SQL 而不是试图管理多 TB数据仓库负载)并且只想循环处理记录
for stg in (select s.*,
count(*) over (partition by s.e_id) cnt
from cmb_staging s)
loop
if( stg.cnt > 1 )
then
insert into cmb_reject( e_id, e_name, e_loc )
values( stg.e_id, stg.e_name, stg.e_loc );
l_fail_cnt := l_fail_cnt + 1;
else
merge into cmb_target tgt
using( select stg.e_id e_id,
stg.e_name e_name,
stg.e_loc e_loc
from dual ) src
on( src.e_id = tgt.e_id )
when not matched
then
insert( e_id, e_name, e_loc )
values( src.e_id, src.e_name, src.e_loc )
when matched
then
update set e_name = src.e_name,
e_loc = src.e_loc;
l_success_cnt := l_success_cnt + 1;
end if;
end loop;
--If there are duplicates id then it should go into cmb_reject table
select e_id into lv_count from cmb_staging;
当您尝试将所有 5 个 e_id
值插入到单个变量中时,这会引发异常并引发 TOO_MANY_ROWS
异常。 (除了它不识别重复项之外。)
您可以在原始 UPDATE
中进行所有处理(在本例中,将其转换为 MERGE
语句),而不是尝试将重复项识别为流程的单独部分:
create or replace procedure sp_dup_rec(ov_err_msg OUT varchar2)
is
lv_succ_rec number(30);
lv_fail_rec number(30);
lv_count number(30);
begin
MERGE INTO cmb_staging dst
USING (
SELECT ROWID AS rid,
CASE
WHEN e_id IS NULL
THEN 'Id is not present'
WHEN LENGTH(e_id) > 4
THEN 'Id is longer than expected'
WHEN num_e_id > 1
THEN 'Duplicate IDs'
END AS failure_reason
FROM (
SELECT e_id,
COUNT(*) OVER (PARTITION BY e_id) AS num_e_id
FROM cmb_staging
)
WHERE e_id IS NULL
OR LENGTH(e_id) > 4
OR num_e_id > 1
) src
ON (src.rid = dst.ROWID)
WHEN MATCHED THEN
UPDATE
SET validation_status = 'Fail',
validation_result = failure_reason;
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;
insert into cmb_reject
select s.*
from cmb_staging s
WHERE validation_status = 'Fail';
lv_fail_rec := SQL%ROWCOUNT;
dbms_output.put_line('Inserting into Summary table');
insert into summary_tab(
tot_records,
success_records,
failed_records
) values (
lv_succ_rec + lv_fail_rec,
lv_succ_rec,
lv_fail_rec
);
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;
/
db<>fiddle here
CREATE TABLE cmb_staging (
e_id NUMBER(10),
e_name VARCHAR2(30),
e_loc VARCHAR2(30),
validation_status VARCHAR2(30),
validation_result VARCHAR2(30)
);
insert into cmb_staging values(1,'A','AA',null,null);
insert into cmb_staging values(1,'B','BB',null,null);
insert into cmb_staging values(2,'C','CC',null,null);
insert into cmb_staging values(2,'D','DD',null,null);
insert into cmb_staging values(3,'A','AA',null,null);
CREATE TABLE cmb_target (
e_id NUMBER(10),
e_name VARCHAR2(30),
e_loc VARCHAR2(30)
);
CREATE TABLE cmb_reject (
e_id NUMBER(10),
e_name VARCHAR2(30),
e_loc VARCHAR2(30),
validation_status VARCHAR2(30),
validation_result VARCHAR2(30)
);
CREATE TABLE SUMMARY_TAB
( TOT_RECORDS NUMBER(10,0),
SUCCESS_RECORDS NUMBER(10,0),
FAILED_RECORDS NUMBER(10,0),
PROCESS_STATUS VARCHAR2(30)
);
程序:
create or replace procedure sp_dup_rec(ov_err_msg OUT varchar2)
is
lv_succ_rec number(30);
lv_fail_rec number(30);
lv_count number(30);
begin
lv_succ_rec := 0;
lv_fail_rec := 0;
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;
--If there are duplicates id then it should go into cmb_reject table
select e_id into lv_count from cmb_staging;
if lv_count < 1 then
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;
else
insert into cmb_reject
select s.*
from cmb_staging s
WHERE validation_status = 'Fail';
lv_fail_rec := SQL%ROWCOUNT;
end if;
dbms_output.put_line('Inserting into Summary table');
insert into summary_tab(
tot_records,
success_records,
failed_records
) values (
lv_succ_rec + lv_fail_rec,
lv_succ_rec,
lv_fail_rec
);
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;
调用程序:
set serveroutput on;
declare
v_err_msg varchar2(100);
begin
sp_dup_rec(v_err_msg);
dbms_output.put_line(v_err_msg);
end;
您好团队, 调用过程时出现 ora-01422 错误。基本上,我想将重复记录插入 cmb_reject 选项卡,因为合并语句将失败,如果我只使用合并,我将得到 ora - 30296 错误。所以,我写了 if 条件,其中它将获取计数,如果计数更多,则将插入 cmb_reject tab
这是一个问题:
--If there are duplicates id then it should go into cmb_reject table
select e_id into lv_count from cmb_staging;
由于没有 WHERE
子句限制返回的行数,如果 cmb_staging
包含 2(或更多)行,查询将失败,因为您不能将那么多行放入标量 lv_count
变量。
看来你真的是想说
select count(*) into lv_count from cmb_staging;
正如下一行所说
if lv_count < 1 then
错误堆栈应该告诉您抛出错误的行。我的猜测是这条线是
select e_id
into lv_count
from cmb_staging;
因为该查询没有意义。如果 cmb_staging
中有多行,查询将抛出 ORA-01422 错误,因为您不能将多行放在一个标量变量中。您似乎极有可能希望您的查询至少执行 count
。并且很可能带有某种谓词。像
select count(*)
into lv_count
from cmb_staging;
会避免错误。但这可能没有意义,因为您在评论中说“它应该进入 cmb_reject table”,这意味着存在一个单一的对象。每当 cmb_staging
中有任何行时,此更改都会导致 lv_count > 0
,并且您似乎不太可能希望始终拒绝行。
我的粗略猜测是,当您说“重复”时,您的意思是“两行具有相同 e_id”。如果是这样
select e_id, count(*)
from cmb_staging
group by e_id
having count(*) > 1
将显示重复的 e_id
值。然后你可以
insert into cmb_reject
select s.*
from cmb_staging s
where e_id in (select s2.e_id
from cmb_staging s2
group by s2.e_id
having count(*) > 1);
如果您并不真正关心性能(根据您提出的问题,我假设您只是在学习 PL/SQL 而不是试图管理多 TB数据仓库负载)并且只想循环处理记录
for stg in (select s.*,
count(*) over (partition by s.e_id) cnt
from cmb_staging s)
loop
if( stg.cnt > 1 )
then
insert into cmb_reject( e_id, e_name, e_loc )
values( stg.e_id, stg.e_name, stg.e_loc );
l_fail_cnt := l_fail_cnt + 1;
else
merge into cmb_target tgt
using( select stg.e_id e_id,
stg.e_name e_name,
stg.e_loc e_loc
from dual ) src
on( src.e_id = tgt.e_id )
when not matched
then
insert( e_id, e_name, e_loc )
values( src.e_id, src.e_name, src.e_loc )
when matched
then
update set e_name = src.e_name,
e_loc = src.e_loc;
l_success_cnt := l_success_cnt + 1;
end if;
end loop;
--If there are duplicates id then it should go into cmb_reject table
select e_id into lv_count from cmb_staging;
当您尝试将所有 5 个 e_id
值插入到单个变量中时,这会引发异常并引发 TOO_MANY_ROWS
异常。 (除了它不识别重复项之外。)
您可以在原始 UPDATE
中进行所有处理(在本例中,将其转换为 MERGE
语句),而不是尝试将重复项识别为流程的单独部分:
create or replace procedure sp_dup_rec(ov_err_msg OUT varchar2)
is
lv_succ_rec number(30);
lv_fail_rec number(30);
lv_count number(30);
begin
MERGE INTO cmb_staging dst
USING (
SELECT ROWID AS rid,
CASE
WHEN e_id IS NULL
THEN 'Id is not present'
WHEN LENGTH(e_id) > 4
THEN 'Id is longer than expected'
WHEN num_e_id > 1
THEN 'Duplicate IDs'
END AS failure_reason
FROM (
SELECT e_id,
COUNT(*) OVER (PARTITION BY e_id) AS num_e_id
FROM cmb_staging
)
WHERE e_id IS NULL
OR LENGTH(e_id) > 4
OR num_e_id > 1
) src
ON (src.rid = dst.ROWID)
WHEN MATCHED THEN
UPDATE
SET validation_status = 'Fail',
validation_result = failure_reason;
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;
insert into cmb_reject
select s.*
from cmb_staging s
WHERE validation_status = 'Fail';
lv_fail_rec := SQL%ROWCOUNT;
dbms_output.put_line('Inserting into Summary table');
insert into summary_tab(
tot_records,
success_records,
failed_records
) values (
lv_succ_rec + lv_fail_rec,
lv_succ_rec,
lv_fail_rec
);
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;
/
db<>fiddle here