カーソルによって参照された行は、更新したり削除したりできます。更新可能な行の場合、カーソルに対応する照会は読み取り専用であってはなりません。照会が更新可能または削除可能になる条件については、 SQL 解説書 の説明を参照してください。
カーソルを用いて更新を行うためには、 UPDATE ステートメントで WHERE CURRENT OF 文節を使用してください。結果表の列を更新したいシステムを示すには、FOR UPDATE 文節を使用します。 FOR UPDATE での列の指定は全部を選択しなくてもよいため、カーソルで明確に検索されない列でも更新することができます。 FOR UPDATE 文節を列名を使わずに指定すると、表の中のすべての列や、外部で全選択された最初の FROM 文節で識別された視点は更新可能であるとみなされます。 FOR UPDATE 文節では、必要以上の列を指定しないでください。 FOR UPDATE 文節の余分な列に名前を付けると、 DB2 がデータにアクセスする能率を低下させる場合もあります。
カーソルを用いた削除は、DELETE ステートメントで WHERE CURRENT OF 文節を使用して行います。一般に、FOR UPDATE 文節はカーソルの現在行の削除には必要ありません。 SAA1 に設定された LANGLEVEL でプリコンパイルされ、 BLOCKING ALL でバインドされたアプリケーション内の SELECT ステートメントまたは DELETE ステートメントのいずれかに対して動的 SQL (動的 SQL については、動的 SQL プログラムの作成を参照) を使った場合だけは例外です。この場合、SELECT ステートメントで FOR UPDATE 文節を指定する必要があります。プリコンパイラー・オプションの詳細については、 コマンド解説書 を参照してください。
DELETE ステートメントを使用すると、カーソルで参照される行を削除することができます。このとき、カーソルは次の 行の前に置かれたままになるため、カーソルに対して WHERE CURRENT OF 操作をさらに実行する前に、 FETCH ステートメントを発行する必要があります。
カーソルは以下の 3 種類に分類されます。
読み取り専用カーソルにはパフォーマンス上の利点もあります。読み取り専用カーソルの詳細については、 管理の手引き: インプリメンテーション を参照してください。
未確定カーソルは、プリコンパイル時またはバインド時に BLOCKING ALL オプションが指定されると読み取り専用とみなされます。そうでない場合は、更新可能とみなされます。
注: | 動的に処理されるカーソルは常に未確定です。 |
カーソルが読み取り専用か更新可能か未確定かを判別するための基準を示した全リストについては、 SQL 解説書 を参照してください。
この例では、カーソルを使用して表から選択し、カーソルをオープンし、その表から行を取り出します。そして、取り出したそれぞれの行に対して、削除すべきか更新すべきかを (単純な基準に基づいて) 判別します。このサンプルは、以下のプログラミング言語で入手可能です。
REXX 言語は静的 SQL をサポートしないため、サンプルはありません。
UPDATE ステートメントは現在行の位置を変更しないため、カーソルの位置はこの行から変わりません。
一方、DELETE ステートメントを用いると、現在 行は削除されるため、状況は変化します。これは、次の 行の前に置かれることと同じであり、追加の WHERE CURRENT OF 操作が実行される前に FETCH ステートメントを発行する必要があります。
CHECKERR マクロ / 関数は、プログラム外部にあるエラー検査ユーティリティーです。エラー検査ユーティリティーの所在は、ご使用のプログラミング言語により異なります。
このエラー検査ユーティリティーのソース・コードについては、 プログラム例での GET ERROR MESSAGE の使用を参照してください。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "utilemb.h" EXEC SQL INCLUDE SQLCA; int main(int argc, char *argv[]) { EXEC SQL BEGIN DECLARE SECTION; char pname[10]; short dept; char userid[9]; char passwd[19]; EXEC SQL END DECLARE SECTION; printf( "Sample C program: OPENFTCH\n" ); if (argc == 1) { EXEC SQL CONNECT TO sample; EMB_SQL_CHECK("CONNECT TO SAMPLE"); } else if (argc == 3) { strcpy (userid, argv[1]); strcpy (passwd, argv[2]); EXEC SQL CONNECT TO sample USER :userid USING :passwd; EMB_SQL_CHECK("CONNECT TO SAMPLE"); } else { printf ("\nUSAGE: openftch [userid passwd]\n\n"); return 1; } /* endif */ EXEC SQL DECLARE c1 CURSOR FOR (1) SELECT name, dept FROM staff WHERE job='Mgr' FOR UPDATE OF job; EXEC SQL OPEN c1; (2) EMB_SQL_CHECK("OPEN CURSOR"); do { EXEC SQL FETCH c1 INTO :pname, :dept; (3) if (SQLCODE != 0) break; if (dept > 40) { printf( "%-10.10s in dept. %2d will be demoted to Clerk\n", pname, dept ); EXEC SQL UPDATE staff SET job = 'Clerk' (4) WHERE CURRENT OF c1; EMB_SQL_CHECK("UPDATE STAFF"); } else { printf ("%-10.10s in dept. %2d will be DELETED!\n", pname, dept); EXEC SQL DELETE FROM staff WHERE CURRENT OF c1; EMB_SQL_CHECK("DELETE"); } /* endif */ } while ( 1 ); EXEC SQL CLOSE c1; (5) EMB_SQL_CHECK("CLOSE CURSOR"); EXEC SQL ROLLBACK; EMB_SQL_CHECK("ROLLBACK"); printf( "\nOn second thought -- changes rolled back.\n" ); EXEC SQL CONNECT RESET; EMB_SQL_CHECK("CONNECT RESET"); return 0; } /* end of program : OPENFTCH.SQC */
OpF_Curs.sqlj
// PURPOSE : This file, named OpF_Curs.sqlj, contains the definition // of the class OpF_Curs used in the sample program Openftch. import sqlj.runtime.ForUpdate; #sql public iterator OpF_Curs implements ForUpdate (String, short);
Openftch.sqlj
import java.sql.*; import sqlj.runtime.*; import sqlj.runtime.ref.*; class Openftch { static { try { Class.forName ("COM.ibm.db2.jdbc.app.DB2Driver").newInstance (); } catch (Exception e) { System.out.println ("\n Error loading DB2 Driver...\n"); System.out.println (e); System.exit(1); } } public static void main(String argv[]) { try { System.out.println (" Java Openftch Sample"); String url = "jdbc:db2:sample"; // URL is jdbc:db2:dbname Connection con = null; // Set the connection if (argv.length == 0) { // connect with default id/password con = DriverManager.getConnection(url); } else if (argv.length == 2) { String userid = argv[0]; String passwd = argv[1]; // connect with user-provided username and password con = DriverManager.getConnection(url, userid, passwd); } else { throw new Exception( "\nUsage: java Openftch [username password]\n"); } // if - else if - else // Set the default context DefaultContext ctx = new DefaultContext(con); DefaultContext.setDefaultContext(ctx); // Enable transactions con.setAutoCommit(false); // Executing SQLJ positioned update/delete statements. try { OpF_Curs forUpdateCursor; String name = null; short dept=0; #sql forUpdateCursor = { SELECT name, dept FROM staff WHERE job='Mgr' }; // #sql (1)(2) while (true) { #sql { FETCH :forUpdateCursor INTO :name, :dept }; // #sql (3) if (forUpdateCursor.endFetch()) break; if (dept > 40) { System.out.println ( name + " in dept. " + dept + " will be demoted to Clerk"); #sql { UPDATE staff SET job = 'Clerk' WHERE CURRENT OF :forUpdateCursor }; // #sql (4) } else { System.out.println ( name + " in dept. " + dept + " will be DELETED!"); #sql { DELETE FROM staff WHERE CURRENT OF :forUpdateCursor }; // #sql } // if - else } forUpdateCursor.close(); (5) } catch( Exception e ) { throw e; } finally { // Rollback the transaction System.out.println("\nRollback the transaction..."); #sql { ROLLBACK }; System.out.println("Rollback done."); } // try - catch - finally } catch( Exception e ) { System.out.println (e); } // try - catch } // main } // class Openftch
Identification Division. Program-ID. "openftch". Data Division. Working-Storage Section. copy "sqlca.cbl". EXEC SQL BEGIN DECLARE SECTION END-EXEC. 01 pname pic x(10). 01 dept pic s9(4) comp-5. 01 userid pic x(8). 01 passwd. 49 passwd-length pic s9(4) comp-5 value 0. 49 passwd-name pic x(18). EXEC SQL END DECLARE SECTION END-EXEC. 77 errloc pic x(80). Procedure Division. Main Section. display "Sample COBOL program: OPENFTCH". * Get database connection information. display "Enter your user id (default none): " with no advancing. accept userid. if userid = spaces EXEC SQL CONNECT TO sample END-EXEC else display "Enter your password : " with no advancing accept passwd-name. * Passwords in a CONNECT statement must be entered in a VARCHAR format * with the length of the input string. inspect passwd-name tallying passwd-length for characters before initial " ". EXEC SQL CONNECT TO sample USER :userid USING :passwd END-EXEC. move "CONNECT TO" to errloc. call "checkerr" using SQLCA errloc. EXEC SQL DECLARE c1 CURSOR FOR (1) SELECT name, dept FROM staff WHERE job='Mgr' FOR UPDATE OF job END-EXEC. EXEC SQL OPEN c1 END-EXEC (2) move "OPEN" to errloc. call "checkerr" using SQLCA errloc. * call the FETCH and UPDATE/DELETE loop. perform Fetch-Loop thru End-Fetch-Loop until SQLCODE not equal 0. EXEC SQL CLOSE c1 END-EXEC. (5) move "CLOSE" to errloc. call "checkerr" using SQLCA errloc. EXEC SQL ROLLBACK END-EXEC. move "ROLLBACK" to errloc. call "checkerr" using SQLCA errloc. display "On second thought -- changes rolled back.". EXEC SQL CONNECT RESET END-EXEC. move "CONNECT RESET" to errloc. call "checkerr" using SQLCA errloc. End-Main. go to End-Prog. Fetch-Loop Section. EXEC SQL FETCH c1 INTO :pname, :dept END-EXEC. (3) if SQLCODE not equal 0 go to End-Fetch-Loop. if dept greater than 40 go to Update-Staff. Delete-Staff. display pname, " in dept. ", dept, " will be DELETED!". EXEC SQL DELETE FROM staff WHERE CURRENT OF c1 END-EXEC. move "DELETE" to errloc. call "checkerr" using SQLCA errloc. go to End-Fetch-Loop. Update-Staff. display pname, " in dept. ", dept, " will be demoted to Clerk". EXEC SQL UPDATE staff SET job = 'Clerk' (4) WHERE CURRENT OF c1 END-EXEC. move "UPDATE" to errloc. call "checkerr" using SQLCA errloc. End-Fetch-Loop. exit. End-Prog. stop run.