[英]Problems with PL/SQL cursors
这些是表格:
SQL> DESC pais;
Name Null? Type
----------------------------------------- -------- ----------------------------
COD_PAIS NOT NULL NUMBER(4)
NOMBRE VARCHAR2(30)
CAPITAL VARCHAR2(20)
EXTENSION NUMBER(10)
MONEDA VARCHAR2(20)
NUM_HAB NUMBER(10)
PIB NUMBER(20,2)
CONTINENTE VARCHAR2(20)
CASCOS CHAR(1)
SQL> DESC pertenece_a;
Name Null? Type
----------------------------------------- -------- ----------------------------
COD_ORGANIZACION NOT NULL NUMBER(10)
COD_PAIS NOT NULL NUMBER(4)
SQL> DESC organizacion;
Name Null? Type
----------------------------------------- -------- ----------------------------
COD_ORGANIZACION NOT NULL NUMBER(10)
NOMBRE VARCHAR2(80)
SIGLAS VARCHAR2(6)
如果siglas是ONU ,我需要将值cascos更新为S 。 如果不是,则必须将其更新为N 。 这是我的代码。 SQL 开发人员未报告任何错误,但 C1 cursor 更改未更新。 我必须使用游标。
SET SERVEROUTPUT ON;
DECLARE
CURSOR C1 IS SELECT cascos FROM pais P, organizacion O, pertenece_a PE WHERE P.cod_pais=PE.cod_pais AND O.cod_organizacion=PE.cod_organizacion AND O.siglas='ONU' FOR UPDATE;
CURSOR C2 IS SELECT cascos FROM pais WHERE cascos IS NULL FOR UPDATE;
registro1 C1%ROWTYPE;
registro2 C2%ROWTYPE;
BEGIN
IF NOT C1%ISOPEN THEN
OPEN C1;
END IF;
IF NOT C2%ISOPEN THEN
OPEN C2;
END IF;
LOOP
FETCH C1 INTO registro1;
EXIT WHEN C1%NOTFOUND;
UPDATE pais SET cascos='S' WHERE CURRENT OF C1;
END LOOP;
LOOP
FETCH C2 INTO registro2;
EXIT WHEN C2%NOTFOUND;
UPDATE pais SET cascos='N' WHERE CURRENT OF C2;
END LOOP;
CLOSE C1;
CLOSE C2;
END;
/
SELECT * FROM pais WHERE cascos='S';
SQL 开发人员 output 只是说:
PL/SQL procedure successfully completed.
no rows selected
可能是什么错误? 问题可能是一排 pais 可以与多排 (siglas) 组织相关联吗? 例如:
NOMBRE SIGLAS
------------------------------ ------
Venezuela ONU
Venezuela OEA
Venezuela MS
Venezuela OPEP
Estados Unidos ONU
Estados Unidos OTAN
Estados Unidos OEA
Estados Unidos APEC
Estados Unidos OCDE
Estados Unidos OSCE
Estados Unidos TLCAN
谢谢你。
看来您想更新所有pais
行。 那些与 ONU 匹配的人得到cascos = 'S'
,其他人得到cascos = 'N'
。 为此,您可以仅使用更新语句,这甚至比使用 PL/SQL 更快。
例如:
update pais
set cascos =
case when cod_pais in (
select pe.cod_pais
from pertenece_a pe
join organizacion o using (cod_organizacion)
where o.siglas = 'ONU'
)
then 'S'
else 'N'
end;
你能用两个 UPDATE 语句试试这个吗?
UPDATE pais P SET cascos='S' WHERE EXISTS ( SELECT 1 FROM pais P, organizacion O, pertenece_a PE WHERE P.cod_pais=PE.cod_pais AND O.cod_organizacion=PE.cod_organizacion AND O.siglas='ONU');
UPDATE pais P SET cascos='N' WHERE nvl(cascos,'S')<>'S';
`
如果cascos
的所有值一开始都是 null,那么第二个循环中的更新将覆盖第一个循环中所做的更改。 快速解决方法是在完成第一个循环后打开c2
,而不是在它之前打开。
但是,您可以将代码简化如下:
begin
for r in (
select p.cod_pais
from pais p
join pertenece_a pe on pe.cod_pais = p.cod_pais
join organizacion o on o.cod_organizacion = pe.cod_organizacion
where o.siglas = 'ONU'
and nvl(p.cascos,'?') <> 'S'
for update
)
loop
update pais set cascos = 'S'
where cod_pais = r.cod_pais;
end loop;
update pais set cascos = 'N' where cascos is null;
end;
进一步的重构是将整个事情重写为单个update
或merge
,并且没有任何明确的 cursor 处理或循环。 它也会更有效,因为单个 SQL 语句优于每行一个语句,并且您可以避免通过pais
进行两次传递,尽管如果这是一个非常小的表,这可能并不重要。
顺便说一句,您不需要检查IF NOT C1%ISOPEN
,因为您刚刚声明了c1
并没有打开它,所以它不可能打开。 此外,最好使用 ANSI 样式的显式连接语法,因为它更难错过连接条件。
此外,您应该对所有字符列使用标准字符串类型varchar2
,而不是char
。 认为char
对短值有一些特殊优势是一个常见的错误,但事实并非如此,并且使用非标准类型只会导致错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.