データ入力項目および更新アプリケーション (特に図形アプリケーション) によっては、ユーザーがデータ入力書式のセルを多数挿入、削除、または変更してから、データベースへデータを送信するよう依頼するような状況が頻繁に生じます。このような大量の挿入、削除、または更新の状況に備えて、 DB2 CLI では配列入力方式が設けられており、アプリケーションが同一 INSERT、 DELETE、または UPDATE ステートメントで繰り返し SQLExecute() を呼び出さなくても済むようになっています。加えて、ネットワーク・フローで有効な節約ができます。
アプリケーションが SQL ステートメントのパラメーター・マーカーを配列にバインドできる以下の 2 つの方法があります。
SQLBindParameter() も、パラメーターに対するバッファーをバインドするために使用します。唯一の違いは、渡されるアドレスが配列アドレスであり単一変数のアドレスではないという点です。また、アプリケーションは、列方向または行方向のどちらのバインドを使用するかを指定するため、 SQL_ATTR_PARAM_BIND_TYPE ステートメント属性をセットする必要があります。
この方式では、SQLBindParameter() 呼び出しによってパラメーター・マーカーを記憶場所の配列にバインドします。文字および 2 進入力データの場合は、アプリケーションが SQLBindParameter() 呼び出しで最大入力バッファー・サイズの引き数 (BufferLength) を使用して、 DB2 CLI に入力配列内の値の場所を示します。その他の入力データ・タイプの場合は、配列内の各要素の長さは C データ・タイプのサイズであるとみなされます。 SQL ステートメントの実行前に、ステートメント属性 SQL_ATTR_PARAMSET_SIZE に (SQLSetStmtAttr() への呼び出しを指定して) 配列のサイズをセットする必要があります。
図 10 で、出退勤時間データ入力用紙の OVERTIME_WORKED 列と OVERTIME_PAID 列の値をユーザーが変更できるようなアプリケーションがあるとします。また、基礎 EMPLOYEE 表の基本キーは EMPLOY_ID であるとします。この場合、アプリケーションは次のような SQL ステートメントを準備するよう要求することができます。
UPDATE EMPLOYEE SET OVERTIME_WORKED= ? and OVERTIME_PAID= ? WHERE EMPLOY_ID=?
ユーザーがすべての変更内容を入力すると、アプリケーションは変更される n 行をカウントし、変更されるデータと基本キーを保管するための m=3 配列を割り振ります。次に SQLBindParameter() を呼び出して、
3 つのパラメーター・マーカーをメモリー内の 3 つの配列の場所にバインドします。次に、変更する行数 (配列のサイズ) を指定するには、
(SQLSetStmtAttr() への呼び出しを指定して) ステートメント属性 SQL_ATTR_PARAMSET_SIZE をセットします。次に SQLExecute() を 1 回呼び出すと、すべての更新内容がデータベースへ送信されます。これが 図 10 の右側に示した流れです。
基本方式については、図 10 の左側に示してあります。この場合、SQLBindParameter() が呼び出されて、 3 つのパラメーター・マーカーをメモリー内の 3 つの変数の場所にバインドします。 SQLExecute() が呼び出されて、最初の変更の集まりをデータベースに送信します。変更箇所の次の行の値を反映するよう変数が更新され、再度 SQLExecute() が呼び出されます。この方式では、n-1 回余分に SQLExecute() 呼び出しがあることに注意してください。
![]() |
アプリケーションでアクセスできるエラーに関する詳細については、 診断情報の取り出しを参照してください。
行方向配列の挿入を使用する場合の最初のステップは、パラメーターごとに 2 つの要素を含む構造体を作成することです。パラメーターごとの最初の要素では、長さ / 標識バッファーを保持し、2 番目の要素はその値を保持します。この構造体が一度定義されたなら、アプリケーションは構造体の配列を割り当てる必要があります。配列の行数は、パラメーターごとに使用される値の数に対応しています。
struct { SQLINTEGER La; SQLINTEGER A; /* Information for parameter A */ SQLINTEGER Lb; SQLCHAR B[4]; /* Information for parameter B */ SQLINTEGER Lc; SQLCHAR C[11]; /* Information for parameter C */ } R[n];
図 11 には、3 つのパラメーターでなる構造体 R を示しています。それは n 行の配列にあります。その後、その配列には適切なデータを移植できます。
配列が一度作成され移植されたなら、アプリケーションは行方向のバインドを使用することを指示する必要があります。これは、ステートメント属性 SQL_ATTR_PARAM_BIND_TYPE を、作成された構造体の長さに設定することで行われます。また、ステートメント属性 SQL_ATTR_PARAMSET_SIZE を、配列の行数に設定する必要があります。
この段階で、各パラメーターは SQLBindParameter() を使用して、 (配列の最初の行における) 構造体の適切な 2 つの要素にバインドすることが可能になります。
/* Parameter A */ rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 5, 0, &R[0].A, 0, &R.La); /* Parameter B */ rc = SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 0, R[0].B, 10, &R.Lb); /* Parameter C */ rc = SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 3, 0, R[0].C, 3, &R.Lc);
ここで、アプリケーションが SQLExecute() を一度呼び出すと、すべての更新内容がデータベースへ送信されます。
![]() |
アプリケーションでアクセスできるエラーに関する詳細については、診断情報の取り出しを参照してください。
パラメーター状況の配列 は、 SQLExecute() または SQLExecDirect() 呼び出し後に移植できます。この配列には、パラメーターの各セットの処理に関する情報が収められています。詳細については、ステートメント属性 SQL_ATTR_PARAM_STATUS_PTR または対応する IPD 記述子のヘッダー・フィールド SQL_DESC_ARRAY_STATUS_PTR を参照してください。
ステートメント属性 SQL_ATTR_PARAMS_PROCESSED (または対応する IPD 記述子のヘッダー・フィールド SQL_DESC_ROWS_PROCESSED_PTR) を使用すると、すでに処理されたパラメーターのセットの数を返すことができます。 SQLSetStmtAttr() または SQLSetDescField() の説明の中のこれらの属性の項を参照してください。
アプリケーションがどのパラメーターにエラーがあるかを一度判別したなら、ステートメント属性 SQL_ATTR_PARAM_OPERATION_PTR (または対応する APD 記述子のヘッダー・フィールド SQL_DESC_ARRAY_STATUS_PTR、どちらも値の配列を指す) を使用すると、 SQLExecute() または SQLExecDirect() への 2 番目の呼び出しにおいて、パラメーターのどのセットを無効にするかを制御することができます。 SQLSetStmtAttr() または SQLSetDescField() の説明の中のこれらの属性の項を参照してください。
その他の情報
基礎サポートで複合 SQL が使える環境 (DB2 ユニバーサル・データベース、または DB2 コネクト V 2.3 以降の DRDA 環境) では、ネットワーク・フローをさらに節約します。配列内のすべてのデータは実行要求と一緒に 1 つの流れとしてパッケージされます。 DRDA 環境の場合、基礎の複合 SQL サポートは常に NOT ATOMIC COMPOUND SQL です。このことは、中間の配列要素のいずれかでエラーが検出されても、実行は続行することを意味します。配列操作の後で SQLRowCount() が呼び出される場合、受け取る行カウントは、入力パラメーター値の配列内のすべての要素によって制御される行の集合の数です。
DB2 ユニバーサル・データベースに接続した場合、アプリケーションは ATOMIC または NOT ATOMIC COMPOUND SQL を選択することができます。 ATOMIC SQL (省略時値) にすると、配列のすべての要素は正常に処理されるか、またはまったく処理されません。アプリケーションは、 SQLSetStmtAttr() を指定して SQL_ATTR_PARAMOPT_ATOMIC 属性を設定することで COMPOUND SQL タイプを使用するよう選択できます。
注: | SQLBindParam() を使用して、配列の記憶場所をパラメーター・マーカーにバインドすることはできません。文字または 2 進入力データの場合、入力配列内の各要素のサイズを指定する方法はありません。 |
WHERE 文節にパラメーター・マーカーを指定した照会の場合、入力値の配列によっては複数の順次結果セットが生成される可能性があります。 SQLMoreResults() を呼び出すと、個々の結果セットを処理し、次の集まりに進むことができます。詳細および例については、SQLMoreResults - さらに結果セットがあるかどうかを判別するを参照してください。
パラメーター・バインドの変更の必要が生じた場合、アプリケーションはもう一度 SQLBindParameter() を呼び出すことができます。これにより、バインドされているパラメーターのバッファー・アドレスと、それに対応する使用中の長さ / 標識バッファー・アドレスを変更します。この変更は、行方向配列の挿入でのみ使用できますが、アプリケーションが個々にまたは配列を使用してパラメーターをバインドするかどうかを決めます。
SQLBindParameter() への複数の呼び出しの代わりに、 DB2 CLI はパラメーター・バインドの相対位置もサポートしています。毎回再バインドするよりも、相対位置を使用すると、 SQLExecute() または SQLExecDirect() への次の呼び出しで使用される新しいバッファー・アドレスおよび長さ / 標識アドレスを指定することができます。
パラメーター・バインドの相対位置を使用するには、アプリケーションは次のステップに従ってください。
ステートメント属性 SQL_ATTR_PARAM_BIND_OFFSET_PTR は、相対位置が保管されることになる SQLINTEGER バッファーのアドレスを指します。このアドレスは、カーソルがクローズするまで有効である必要があります。
この、余分のレベルの間接参照によって、単一のメモリー変数を使用するだけで、異なるステートメント・ハンドルにあるパラメーター・バッファーの複数のセットについて、相対位置を保管することができます。アプリケーションは、この 1 つのメモリー変数と、変更されるすべての相対位置だけを設定する必要があります。
相対位置の値は、常に最初にバインドされている値のメモリー位置に加えられ、この合計が有効なメモリー・アドレスを指すことになります。
詳細については、SQLBindParam() の 『パラメーター・バインドの相対位置』を参照してください。
この例では配列の INSERT ステートメントを示します。配列の照会ステートメントの例については、SQLMoreResults - さらに結果セットがあるかどうかを判別するを参照してください。
/* ... */ SQLCHAR * stmt = "INSERT INTO CUSTOMER ( Cust_Num, First_Name, Last_Name ) " "VALUES (?, ?, ?)" ; SQLINTEGER Cust_Num[] = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250, } ; SQLCHAR First_Name[][31] = { "EVA", "EILEEN", "THEODORE", "VINCENZO", "SEAN", "DOLORES", "HEATHER", "BRUCE", "ELIZABETH", "MASATOSHI", "MARILYN", "JAMES", "DAVID", "WILLIAM", "JENNIFER", "JAMES", "SALVATORE", "DANIEL", "SYBIL", "MARIA", "ETHEL", "JOHN", "PHILIP", "MAUDE", "BILL", } ; SQLCHAR Last_Name[][31] = { "SPENSER", "LUCCHESI", "O'CONNELL", "QUINTANA", "NICHOLLS", "ADAMSON", "PIANKA", "YOSHIMURA", "SCOUTTEN", "WALKER", "BROWN", "JONES", "LUTZ", "JEFFERSON", "MARINO", "SMITH", "JOHNSON", "PEREZ", "SCHNEIDER", "PARKER", "SMITH", "SETRIGHT", "MEHTA", "LEE", "GOUNOT", } ; /* ... */ /* Prepare the statement */ rc = SQLPrepare( hstmt, stmt, SQL_NTS ) ; CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc ) ; rc = SQLSetStmtAttr( hstmt, SQL_ATTR_PARAMSET_SIZE, ( SQLPOINTER ) row_array_size, 0 ) ; CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc ) ; rc = SQLBindParameter( hstmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, Cust_Num, 0, NULL ) ; CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc ) ; rc = SQLBindParameter( hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 31, 0, First_Name, 31, NULL ) ; CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc ) ; rc = SQLBindParameter( hstmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 31, 0, Last_Name, 31, NULL ) ; CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc ) ; rc = SQLExecute( hstmt ) ; CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc ) ; printf( "Inserted %ld Rows\n", row_array_size ) ;