簡體   English   中英

PL/SQL 游標的問題

[英]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)                 

如果siglasONU ,我需要將值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;

進一步的重構是將整個事情重寫為單個updatemerge ,並且沒有任何明確的 cursor 處理或循環。 它也會更有效,因為單個 SQL 語句優於每行一個語句,並且您可以避免通過pais進行兩次傳遞,盡管如果這是一個非常小的表,這可能並不重要。

順便說一句,您不需要檢查IF NOT C1%ISOPEN ,因為您剛剛聲明了c1並沒有打開它,所以它不可能打開。 此外,最好使用 ANSI 樣式的顯式連接語法,因為它更難錯過連接條件。

此外,您應該對所有字符列使用標准字符串類型varchar2 ,而不是char 認為char對短值有一些特殊優勢是一個常見的錯誤,但事實並非如此,並且使用非標准類型只會導致錯誤。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM