在游标内使用函数(通过使用变量)
Using function inside a cursor (by using a variable)
我想确认以下内容的正确使用:
1) 使用全局变量从函数中获取 return 值一次
(因为我的函数将 returning 一些序列值)
2) 在游标内多次使用该变量
3) 所有这些都将在一个过程中
下面显示了一个示例。
CREATE OR REPLACE Procedure insert_myTable is
--declare variables for insert
v_firstNO VARCHAR2(10);
v_secondNO VARCHAR2(6);
--declare variable to store the sequence number
var_ASeqno varchar2(6);
-- Validation
v_check VARCHAR2 (10 Byte);
v_table_name varchar2(50):='myTable';
cursor c1 is
select distinct firstNO,
secondNO
from (SELECT hdr.someNum firstNO,
-- using variable to assign the sequence no
var_ASeqno secondNO
FROM someOtherTable hdr
WHERE -- some condition
union
SELECT hdr.someNum firstNO,
-- using variable to assign the sequence no
var_ASeqno secondNO
FROM someOtherTable hdr
WHERE -- some other conditions
union
SELECT hdr.someNum firstNO,
-- using variable to assign the sequence no
var_ASeqno secondNO
FROM someOtherTable hdr
WHERE -- some other other conditions
begin
if c1%isopen then
close c1;
end if;
v_check:=null;
FOR i IN c1 LOOP
--assign variables for insert
v_firstNO := i.firstNO ;
v_secondNO := i.secondNO ;
begin
-- calling the Function aSeqNoFunc and assign the
--Sequence Number into the variable var_ASeqno
var_ASeqno := aSeqNoFunc();
select firstNO
into v_check
from myTable a
where firstNO = i.firstNO
and secondNO =i.secondNO;
exception
when no_data_found then
--insert into target table
INSERT INTO myTable (firstNO, secondNO)
values (v_firstNO, v_secondNO);
end ;
end loop;
end;
可以看出,函数'aSeqNoFunc'在Insert near the end之前被调用。这些值被分配给变量 'var_ApmSeqno' ,该变量又在游标内使用了三次。
谢谢。
几点建议:
您在 cursor c1
的声明之后有一个 END;
语句,它与任何内容都不匹配。您应该从您的代码中删除它。
进入程序时无需检查光标是否打开。不会的。更好的是,不要使用显式游标声明 - 使用游标 FOR 循环。
除非您知道两者之间的区别,否则请使用 UNION ALL 而不是 UNION。 (And go read up on that。99.9% 的时间你想要 UNION ALL...)。
但是,由于看起来所有行都是从同一个 table 中选择的,因此您可以完全消除 UNION,如下所示。
在函数开头将 NULL 赋值给变量没有任何好处。如果没有给出其他明确的初始化值,变量将被初始化为 NULL。
在我看来,使用 return 序列中的下一个值的函数没有任何好处。这只会让理解代码变得更加困难。摆脱 FUNCTION aSeqNoFunc
并在适当的地方调用 SOME_SEQUENCE.NEXTVAL
- 所以在上面我建议你使用 var_ASeqno := SOME_SEQUENCE.NEXTVAL
.
您需要在游标c1
打开之前将值赋给var_ASeqno
。如上所述,var_ASeqno
在游标打开时将为空,因此游标可能不会 return 您所期望的。但更重要的是,我看不出有任何理由让光标 return 成为 var_ASeqno
的值。只需在您的 INSERT 语句或其他任何需要它们的地方使用 var_ASeqno
的值。
如果数据尚不存在,请使用 MERGE 语句插入数据。这避免了笨拙的 "SELECT...catch the NO_DATA_FOUND exception...INSERT in the exception handler" 逻辑。
正如@boneist 在她的评论中指出的那样,当我们走到这一步时,光标真的毫无意义。您也可以只使用 MERGE 语句来执行 INSERT,而不使用游标。
所以我会尝试将此过程重写为:
CREATE OR REPLACE Procedure insert_myTable is
begin
MERGE INTO MYTABLE m
USING (SELECT FIRSTNO,
SOME_SEQUENCE.NEXTVAL AS SECONDNO
FROM (SELECT DISTINCT hdr.someNum AS FIRSTNO
FROM someOtherTable hdr
WHERE (/* some condition */)
OR (/* some other conditions */)
OR (/* some other other conditions */))) d
ON d.FIRSTNO = m.FIRSTNO AND
d.SECONDNO = m.SECONDNO
WHEN NOT MATCHED THEN INSERT (FIRSTNO, SECONDNO)
VALUES (d.FIRSTNO, d.SECONDNO);
end INSERT_MYTABLE;
考虑到您希望插入的所有行都分配有相同的序列号,我认为您可以将过程重写为:
create or replace procedure insert_mytable
is
v_seq_no number;
begin
v_seq_no := somesequence.nextval;
merge into mytable tgt
using (select firstno,
v_seq_no secondno
from (select hdr.somenum firstno
from someothertable1 hdr
where -- some condition
union
select hdr.somenum firstno
from someothertable2 hdr
where -- some other conditions
union
select hdr.somenum firstno
from someothertable3 hdr
where -- some other other conditions
)
) src
on (tgt.firstno = src.firstno and tgt.secondno = src.secondno)
when not matched then
insert (tgt.firstno, tgt.secondno)
values (src.firstno, src.secondno);
end insert_mytable;
/
如果这与您尝试执行的操作不符,请编辑您的问题以提供有关程序目的的更多信息。示例输入和输出数据将不胜感激,以便我们更好地了解您的需求(因为我们看不到您的 table 结构、数据等)。
预计到达时间:有关基于集合的方法和逐行方法之间的性能注意事项的信息。
这是一个简单的脚本,用于插入一百万行,包括逐行插入和单个插入语句:
create table test (col1 number,
col2 number);
set timing on;
-- row-by-row (aka slow-by-slow) approach
begin
for rec in (select level col1, level * 10 col2
from dual
connect by level <= 1000000)
loop
insert into test (col1, col2)
values (rec.col1, rec.col2);
end loop;
end;
/
commit;
truncate table test;
-- set based approach (keeping in an anonymous block for comparison purposes)
begin
insert into test (col1, col2)
select level, level*10
from dual
connect by level <= 1000000;
end;
/
commit;
drop table test;
这是我在 Toad 中 运行 以上内容时得到的输出:
Table created.
PL/SQL procedure successfully completed.
Elapsed: 00:00:21.87
Commit complete.
Elapsed: 00:00:01.03
Table truncated.
Elapsed: 00:00:00.22
PL/SQL procedure successfully completed.
Elapsed: 00:00:01.96
Commit complete.
Elapsed: 00:00:00.03
Table dropped.
Elapsed: 00:00:00.18
您是否看到逐行方法耗时 21 秒,基于集合的方法耗时 2 秒?性能上的巨大差异,你不同意吗?如果这不是考虑在第一个实例中编写基于集合的代码的理由,那么我不知道还有什么能说服 you/your 老板!
我想确认以下内容的正确使用:
1) 使用全局变量从函数中获取 return 值一次 (因为我的函数将 returning 一些序列值)
2) 在游标内多次使用该变量
3) 所有这些都将在一个过程中
下面显示了一个示例。
CREATE OR REPLACE Procedure insert_myTable is
--declare variables for insert
v_firstNO VARCHAR2(10);
v_secondNO VARCHAR2(6);
--declare variable to store the sequence number
var_ASeqno varchar2(6);
-- Validation
v_check VARCHAR2 (10 Byte);
v_table_name varchar2(50):='myTable';
cursor c1 is
select distinct firstNO,
secondNO
from (SELECT hdr.someNum firstNO,
-- using variable to assign the sequence no
var_ASeqno secondNO
FROM someOtherTable hdr
WHERE -- some condition
union
SELECT hdr.someNum firstNO,
-- using variable to assign the sequence no
var_ASeqno secondNO
FROM someOtherTable hdr
WHERE -- some other conditions
union
SELECT hdr.someNum firstNO,
-- using variable to assign the sequence no
var_ASeqno secondNO
FROM someOtherTable hdr
WHERE -- some other other conditions
begin
if c1%isopen then
close c1;
end if;
v_check:=null;
FOR i IN c1 LOOP
--assign variables for insert
v_firstNO := i.firstNO ;
v_secondNO := i.secondNO ;
begin
-- calling the Function aSeqNoFunc and assign the
--Sequence Number into the variable var_ASeqno
var_ASeqno := aSeqNoFunc();
select firstNO
into v_check
from myTable a
where firstNO = i.firstNO
and secondNO =i.secondNO;
exception
when no_data_found then
--insert into target table
INSERT INTO myTable (firstNO, secondNO)
values (v_firstNO, v_secondNO);
end ;
end loop;
end;
可以看出,函数'aSeqNoFunc'在Insert near the end之前被调用。这些值被分配给变量 'var_ApmSeqno' ,该变量又在游标内使用了三次。
谢谢。
几点建议:
您在
cursor c1
的声明之后有一个END;
语句,它与任何内容都不匹配。您应该从您的代码中删除它。进入程序时无需检查光标是否打开。不会的。更好的是,不要使用显式游标声明 - 使用游标 FOR 循环。
除非您知道两者之间的区别,否则请使用 UNION ALL 而不是 UNION。 (And go read up on that。99.9% 的时间你想要 UNION ALL...)。
但是,由于看起来所有行都是从同一个 table 中选择的,因此您可以完全消除 UNION,如下所示。
在函数开头将 NULL 赋值给变量没有任何好处。如果没有给出其他明确的初始化值,变量将被初始化为 NULL。
在我看来,使用 return 序列中的下一个值的函数没有任何好处。这只会让理解代码变得更加困难。摆脱
FUNCTION aSeqNoFunc
并在适当的地方调用SOME_SEQUENCE.NEXTVAL
- 所以在上面我建议你使用var_ASeqno := SOME_SEQUENCE.NEXTVAL
.您需要在游标
c1
打开之前将值赋给var_ASeqno
。如上所述,var_ASeqno
在游标打开时将为空,因此游标可能不会 return 您所期望的。但更重要的是,我看不出有任何理由让光标 return 成为var_ASeqno
的值。只需在您的 INSERT 语句或其他任何需要它们的地方使用var_ASeqno
的值。如果数据尚不存在,请使用 MERGE 语句插入数据。这避免了笨拙的 "SELECT...catch the NO_DATA_FOUND exception...INSERT in the exception handler" 逻辑。
正如@boneist 在她的评论中指出的那样,当我们走到这一步时,光标真的毫无意义。您也可以只使用 MERGE 语句来执行 INSERT,而不使用游标。
所以我会尝试将此过程重写为:
CREATE OR REPLACE Procedure insert_myTable is
begin
MERGE INTO MYTABLE m
USING (SELECT FIRSTNO,
SOME_SEQUENCE.NEXTVAL AS SECONDNO
FROM (SELECT DISTINCT hdr.someNum AS FIRSTNO
FROM someOtherTable hdr
WHERE (/* some condition */)
OR (/* some other conditions */)
OR (/* some other other conditions */))) d
ON d.FIRSTNO = m.FIRSTNO AND
d.SECONDNO = m.SECONDNO
WHEN NOT MATCHED THEN INSERT (FIRSTNO, SECONDNO)
VALUES (d.FIRSTNO, d.SECONDNO);
end INSERT_MYTABLE;
考虑到您希望插入的所有行都分配有相同的序列号,我认为您可以将过程重写为:
create or replace procedure insert_mytable
is
v_seq_no number;
begin
v_seq_no := somesequence.nextval;
merge into mytable tgt
using (select firstno,
v_seq_no secondno
from (select hdr.somenum firstno
from someothertable1 hdr
where -- some condition
union
select hdr.somenum firstno
from someothertable2 hdr
where -- some other conditions
union
select hdr.somenum firstno
from someothertable3 hdr
where -- some other other conditions
)
) src
on (tgt.firstno = src.firstno and tgt.secondno = src.secondno)
when not matched then
insert (tgt.firstno, tgt.secondno)
values (src.firstno, src.secondno);
end insert_mytable;
/
如果这与您尝试执行的操作不符,请编辑您的问题以提供有关程序目的的更多信息。示例输入和输出数据将不胜感激,以便我们更好地了解您的需求(因为我们看不到您的 table 结构、数据等)。
预计到达时间:有关基于集合的方法和逐行方法之间的性能注意事项的信息。
这是一个简单的脚本,用于插入一百万行,包括逐行插入和单个插入语句:
create table test (col1 number,
col2 number);
set timing on;
-- row-by-row (aka slow-by-slow) approach
begin
for rec in (select level col1, level * 10 col2
from dual
connect by level <= 1000000)
loop
insert into test (col1, col2)
values (rec.col1, rec.col2);
end loop;
end;
/
commit;
truncate table test;
-- set based approach (keeping in an anonymous block for comparison purposes)
begin
insert into test (col1, col2)
select level, level*10
from dual
connect by level <= 1000000;
end;
/
commit;
drop table test;
这是我在 Toad 中 运行 以上内容时得到的输出:
Table created.
PL/SQL procedure successfully completed.
Elapsed: 00:00:21.87
Commit complete.
Elapsed: 00:00:01.03
Table truncated.
Elapsed: 00:00:00.22
PL/SQL procedure successfully completed.
Elapsed: 00:00:01.96
Commit complete.
Elapsed: 00:00:00.03
Table dropped.
Elapsed: 00:00:00.18
您是否看到逐行方法耗时 21 秒,基于集合的方法耗时 2 秒?性能上的巨大差异,你不同意吗?如果这不是考虑在第一个实例中编写基于集合的代码的理由,那么我不知道还有什么能说服 you/your 老板!