简体   繁体   中英

Problems with PL/SQL cursors

These are the tables:

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)                 

I need to update the value cascos to S if siglas is ONU . If not, it has to be updated to N . This is my code. SQL Developer doesn't report any errors, however the C1 cursor change isn't updating. I must to use the cursors.

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';

The SQL Developer output just says:

PL/SQL procedure successfully completed.

no rows selected

What could be the mistake? Could the problem be that a row of pais can be related to more than one row (siglas) of organizacion? For example:

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 

Thank you.

It seems you want to update all pais rows. Those with an ONU match get cascos = 'S' , the others get cascos = 'N' . You can use a mere update statement for this, which is even faster than using PL/SQL.

For instance:

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;

can you please try this using two UPDATE statements.

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'; `

If all values of cascos are null to begin with, then the updates in the second loop will overwrite the changes made in the first loop. The quick fix would be to open c2 after completing the first loop, instead of before it.

However, you could simplify the code as follows:

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;

A further refactoring would be to rewrite the whole thing as a single update or merge , and not have any explicit cursor handling or loops. It would also be more efficient because a single SQL statement is better than one per row, and you could avoid making two passes through pais , although perhaps if this is a very small table it doesn't matter much.

By the way, you don't need to check IF NOT C1%ISOPEN , because you've just declared c1 and not opened it, so it cannot possibly be open. Also it is good practice to use ANSI-style explicit join syntax, as it is harder to miss join conditions.

Also, you should be using the standard string type, varchar2 , for all character columns, and never char . It is a common mistake to think that char has some special advantage for short values, but it does not and using nonstandard types only leads to bugs.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM