72334


PL/SQLでCURSORを使用してUPDATEしようとしているのですが、  
正常終了するものの、更新がされていません。何が問題でしょうか?

[code]

    CREATE TABLE TABLE1 ( 
        COL1    CHAR(3),
        COL2    CHAR(3)
    );
    CREATE TABLE TABLE2 ( 
        COL1    CHAR(3)
    );
    
    INSERT INTO TABLE1 VALUES('ABC','001');
    INSERT INTO TABLE2 VALUES('ABC');
    COMMIT;
    
[/code]

[code]

    DECLARE
       CURSOR C1_CURSOR IS
    
          SELECT T1.COL1,T1.COL2
            FROM TABLE1 T1
            JOIN TABLE2 T2
              ON T1.COL1 = T2.COL1
          FOR UPDATE;
                 
       C1 C1_CURSOR%ROWTYPE;
    
    BEGIN
       OPEN C1_CURSOR;
       LOOP
          FETCH C1_CURSOR INTO C1;
          EXIT WHEN C1_CURSOR%NOTFOUND;
     
          UPDATE TABLE1
             SET COL2 = '999'
            WHERE CURRENT OF C1_CURSOR;
    
      END LOOP;
       CLOSE C1_CURSOR;
    
       COMMIT;
    
    END;
    
[/code]

[code]

    SELECT * FROM TABLE1;
    
[/code]

**⇒ COL2 が '999'になっていない!!**


CC BY-SA 4.0
2020-12-01T10:04:35.283
72322
2020-12-03T01:44:51.723
2020-12-01T10:12:10.697
17222
17222
question
oracle plsql
PL/SQL JOIN使用時にCURSOR で更新できない


ご質問のように複数のテーブルを等価結合する時は、更新するテーブルに対して明示的にロックを掛けないとレコードが特定できないようです。  
つまり`FOR UPDATE;`を`FOR UPDATE OF T1.COL2;`に書き換えるとうまく行きます。  
cf: [SELECT 時に明示的な行ロックを行なう方法](https://www.shift-the-oracle.com/sql/select-for-
update.html)

また、単純にROWIDで一意に特定する代替手段もあります。  
カーソルのselect句に`T1.ROWID`を追加し、update文のwhere句を`WHERE ROWID =
C1.ROWID;`に書き換える方法です。  
cf: [CURRENT OFの疑似実行について](https://docs.oracle.com/cd/E57425_01/121/LNPCC/GUID-
DBA71301-C6C4-47C0-BAB5-1B6B221AB46E.htm)

もしくは単一テーブルに対するカーソルであれば一意の行が特定できるので、EXISTS句に書き換えることで`FOR UPDATE`でも対応可能です。

[code]

    CURSOR C1_CURSOR IS
      SELECT T1.COL1,T1.COL2
        FROM TABLE1 T1
       WHERE EXISTS (SELECT 1 FROM TABLE2 T2 WHERE T1.COL1 = T2.COL1)
      FOR UPDATE;
    
[/code]

本家SOの類似質問？: [Oracle FOR UPDATE (OF) Cursor
behaviour](https://stackoverflow.com/q/35722867)


CC BY-SA 4.0
2020-12-02T02:41:16.853
72334
2020-12-02T02:41:16.853
9820
72322
answer