如何在 Oracle PL/SQL 过程的开始部分之后声明游标
Posted
技术标签:
【中文标题】如何在 Oracle PL/SQL 过程的开始部分之后声明游标【英文标题】:How to declare a cursor after the begin section of an Oracle PL/SQL procedure 【发布时间】:2016-11-08 15:00:45 【问题描述】:我是 Oracle PL/SQL 的新手。我试图编写一个程序,首先从表中的列中获取数据并分配给一个变量。然后对于结果中的每一行/值,我执行另一个查询并循环通过这个新查询的结果并执行各种插入和更新。之后我会回到外循环并继续这个序列。我的尝试如下:
CREATE OR REPLACE PROCEDURE CMSADMIN.Proc_RFC_UPD_NEW_MRSP
IS
ecode NUMBER;
emesg VARCHAR2(200);
cursor y IS (select distinct cod_unicom FCODE from RFC_UPD_NEW_MRSP_POOL);
BEGIN
for t in y loop
cursor X IS (
SELECT DISTINCT s.NIF PREMISE,a.COD_UNICOM READING, A.COD_LECT_AREA AREA
FROM SUMCON s, LECT_AREAS a
WHERE a.COD_UNICOM = t.FCODE
AND s.NUM_SUM IN (select num_sum from RFC_UPD_NEW_MRSP_POOL where cod_unicom = t.FCODE)
);
for met in x loop
/* Store record trace*/
INSERT into RFC_UPD_NEW_MRSP(NIF, COD_UNICOM,COD_LECT_AREA, USUARIO, F_ACTUAL, PROGRAMA)
values (met.PREMISE, met.READING, met.AREA, USER, SYSDATE,'RFC_MRSP_FPL');
UPDATE fincas_per_lect fp
SET
FP.NUM_MRSP = MET.READING,
fp.AOL_FIN=0,
fp.NUM_ITIN =0,
fp.USUARIO = user, fp.PROGRAMA = 'RFC_MRSP_FPL', fp.F_ACTUAL = sysdate
WHERE Fp.NIF=met.PREMISE;
UPDATE apmedida_ap fp
SET
FP.NUM_MRSP = MET.READING,
fp.USUARIO = user, fp.PROGRAMA = 'RFC_MRSP_FPL', fp.F_ACTUAL = sysdate
WHERE Fp.NIF_apa = met.PREMISE;
UPDATE FINCAS fp
SET AREA_LECT = MET.AREA,
fp.USUARIO = user, fp.PROGRAMA = 'RFC_MRSP_FPL', fp.F_ACTUAL = sysdate
WHERE NIF = met.PREMISE;
end loop;
end loop;
COMMIT;
dbms_output.put_line('The procedure Proc_RFC_UPD_NEW_MRSP executed successfully');
EXCEPTION
WHEN OTHERS THEN
ecode := SQLCODE;
emesg := SQLERRM;
dbms_output.put_line('The procedure Proc_RFC_UPD_NEW_MRSP fail with folowing error '|| TO_CHAR(ecode) || ' and error message: ' || emesg);
NULL;
end Proc_RFC_UPD_NEW_MRSP;
如您所见,BEGIN
内的 for 循环后面还有另一个光标。这是允许的吗?该脚本未按预期工作。可能是什么问题呢?感谢任何帮助
【问题讨论】:
避免使用嵌套循环进行更新。在大多数情况下,您可以使用 join 而不是嵌套的 pl_sql 循环。也可以考虑批量/forall。 为什么不用内连接替换 x 游标中的 sql?你有不同的所以内部连接不会改变返回的行数但会更好。 你真的把你的变量命名为x
、y
和t
吗?我将i
和r
分别用于数字和游标循环(除非我需要像您的示例中那样嵌套东西 - 但我会努力避免这种情况),否则我总是使用有意义的名称,因为随机字母是相当混乱。
顺便说一句,捕获sqlcode
是不必要的,因为它只是sqlerrm
的数字部分,例如如果 sqlerrm 是 ORA-01426: numeric overflow
,那么 sqlcode 是 -1426
。它不会向消息中添加任何内容。而且你真的应该重新引发异常,否则即使过程只完成了应该完成的一些步骤,它也会看起来成功。
嗨@WilliamRobertson,我该如何按照你的建议重新提出异常?
【参考方案1】:
为此,您需要声明一个新块:
for t in y loop
DECLARE
cursor X IS (
SELECT DISTINCT s.NIF PREMISE,a.COD_UNICOM READING, A.COD_LECT_AREA AREA
FROM SUMCON s, LECT_AREAS a
WHERE a.COD_UNICOM = t.FCODE
AND s.NUM_SUM IN (select num_sum from RFC_UPD_NEW_MRSP_POOL where cod_unicom = t.FCODE)
);
BEGIN
for met in x loop
...
end loop;
END;
但是,我不太明白为什么你不能只声明这个游标和游标 y,用参数定义:
cursor X (p_FCODE RFC_UPD_NEW_MRSP_POOL.cod_unicom%TYPE) IS (
SELECT DISTINCT s.NIF PREMISE,a.COD_UNICOM READING, A.COD_LECT_AREA AREA
FROM SUMCON s, LECT_AREAS a
WHERE a.COD_UNICOM = p_FCODE
AND s.NUM_SUM IN (select num_sum from RFC_UPD_NEW_MRSP_POOL where cod_unicom = p_FCODE)
);
你可以这样引用光标:
FOR met IN x(t.FCODE) LOOP
【讨论】:
嗨。感谢您的输入。我正在使用WHERE a.COD_UNICOM = t.FCODE
,所以我不能与游标 y 一起声明
t.FCODE
是从第一个游标中执行的查询获得的值
听起来您正在使用带有输入参数的游标 - 您可以将输入值传递给游标,就像传入过程一样,并在游标中使用它们作为绑定。
嗨,DCookie 和@Rusty,你们太棒了。感谢您的贡献。
@DCookie 我什至认为您不需要在连接查询中使用该参数 - 您可以执行相关子查询,例如:WHERE s.NUM_SUM IN (select num_sum from RFC_UPD_NEW_MRSP_POOL t where t.cod_unicom = a.COD_UNICOM)
。【参考方案2】:
declare
cursor cur1 is
...
begin
...
declare
cursor cur2 is
...
begin
end;
end;
【讨论】:
以上是关于如何在 Oracle PL/SQL 过程的开始部分之后声明游标的主要内容,如果未能解决你的问题,请参考以下文章