ホスト配列はSELECT文内の出力変数として使用できます。SELECTによって戻される最大行数がわかっている場合、その数の要素でホスト配列を宣言してください。次の例では、3つのホスト配列へのデータを直接選択します。このSELECTで戻される行が50行以下であることがわかっているため、配列は要素数50で宣言します。
char emp_name[50][20];
int emp_number[50];
float salary[50];
EXEC SQL SELECT ENAME, EMPNO, SAL
INTO :emp_name, :emp_number, :salary
FROM EMP
WHERE SAL > 1000;
この例ではSELECT文は50行まで戻します。選択される行数が49行以下の場合、または50行のみを取り出す場合はこの方法を使用します。ただし、選択される行数が51行以上の場合は、この方法ではすべての行を取り出せません。このSELECT文を再実行しても、他に選択対象の行があるとしても、最初の50行のみがまた戻されます。この場合は大きな配列を宣言するか、FETCH文で使用するカーソルを宣言する必要があります。
宣言した要素数を超える行数がSELECT INTO文によって戻されると、SELECT_ERROR=NOを指定していないかぎりエラー・メッセージが出されます。
関連項目:
SELECT_ERRORオプションの詳細は、プリコンパイラのオプションを参照してください。
SELECTが戻す最大行数がわからない場合には、カーソルを宣言して、一括でフェッチできます。
ループ内でバッチ・フェッチを実行すると、多数の行を簡単に取り出せます。FETCHを実行するたびに、次に続く行の集合がカレント・アクティブ・セットから戻されます。次の例では、20行ずつまとめて行をフェッチします。
int emp_number[20];
float salary[20];
EXEC SQL DECLARE emp_cursor CURSOR FOR
SELECT empno, sal FROM emp;
EXEC SQL OPEN emp_cursor;
EXEC SQL WHENEVER NOT FOUND do break;
for (;;)
{
EXEC SQL FETCH emp_cursor
INTO :emp_number, :salary;
/* process batch of rows */
...
}
...
最後のフェッチで実際に戻された行数を必ずチェックして処理してください。
関連項目:
INSERT文、UPDATE文、DELETE文およびSELECT INTO文の場合は、sqlca.sqlerrd[2]に処理済行数が記録されます。FETCH文の場合は、処理した行の累積数が記録されます。
FETCHでホスト配列を使用しているときに、その時点での最後のループで戻された行数を確認するには、sqlca.sqlerrd[2]の現在の値と(別の変数内に保存した)前回の値との差分をとります。次の例では、最後のフェッチで戻された行数を確認します。
int emp_number[100];
char emp_name[100][20];
int rows_to_fetch, rows_before, rows_this_time;
EXEC SQL DECLARE emp_cursor CURSOR FOR
SELECT empno, ename
FROM emp
WHERE deptno = 30;
EXEC SQL OPEN emp_cursor;
EXEC SQL WHENEVER NOT FOUND CONTINUE;
/* initialize loop variables */
rows_to_fetch = 20; /* number of rows in each "batch" */
rows_before = 0; /* previous value of sqlerrd[2] */
rows_this_time = 20;
while (rows_this_time == rows_to_fetch)
{
EXEC SQL FOR :rows_to_fetch
FETCH emp_cursor
INTO :emp_number, :emp_name;
rows_this_time = sqlca.sqlerrd[2] - rows_before;
rows_before = sqlca.sqlerrd[2];
}
...
配列の処理中にエラーが発生したときにもsqlca.sqlerrd[2]が役立ちます。処理はエラーが発生した行で停止するため、sqlerrd[2]には正常に処理された行数が格納されます。
各FETCHが戻すのは、最大でも配列の全行数分までです。次のような場合は最大行数より少ない行が戻ります。
アクティブ・セットの最後に達したとき:「データが見つかりません。「データが見つかりません」というOracleエラー・コードがSQLCA内でSQLCODEに戻されます。たとえば、要素数100の配列に行をフェッチしたとき20行しか戻されなかった場合にこれが起こります。
残っているフェッチ対象の行が、一括フェッチの全行数より少ないとき。たとえば、要素数20の配列に70行をフェッチすると、3回目のFETCHの後にはフェッチ対象の行が10しか残っていないため、この状態が発生します。
行の処理中にエラーが検出されたとき。FETCHは失敗し、該当するOracleエラー・コードがSQLCODEに戻されます。
戻された行の累積数は、このガイドではsqlerrd[2]と記載しているSQLCA内のsqlerrdの3番目の要素に保存されます。これはオープン状態のすべてのカーソルに適用されます。次の例では、カーソルの状態がそれぞれ更新されている様子がわかります。
EXEC SQL OPEN cursor1; EXEC SQL OPEN cursor2; EXEC SQL FETCH cursor1 INTO :array_of_20; /* now running total in sqlerrd[2] is 20 */ EXEC SQL FETCH cursor2 INTO :array_of_30; /* now running total in sqlerrd[2] is 30, not 50 */ EXEC SQL FETCH cursor1 INTO :array_of_20; /* now running total in sqlerrd[2] is 40 (20 + 20) */ EXEC SQL FETCH cursor2 INTO :array_of_30; /* now running total in sqlerrd[2] is 60 (30 + 30) */
ホスト配列はスクロール可能カーソルとともに使用することもできます。スクロール可能カーソルを使用する場合、sqlca.sqlerrd[2]は処理済最大(絶対)行数を表します。アプリケーションではスクロール可能モードでフェッチを任意の場所に配置できるため、この値が処理済行数の合計である必要はありません。
スクロール可能モードのFETCH文にホスト配列を使用している間は、sqlca.sqlerrd[2]の現在の値と前回の値との差分を取っても、その時点での最後のループで戻された行数を確認することはできません。アプリケーション・プログラムでは、FETCH LASTを実行して、結果セット内の合計行数を判断します。sqlca.sqlerrd[2]の値は、結果セット内の合計行数を示します。スクロール可能カーソルでのホスト配列の使用方法を示す例は、サンプル・プログラム: スクロール可能カーソルを使用するホスト配列を参照してください。
この項のデモ・プログラムでは、Pro*C/C++で問合せを作成するときの配列の使用方法を示しています。特に、SQLCA(sqlca.sqlerrd[2])の処理済行数に注目してください。このプログラムは、demoディレクトリのファイルsample3.pcとして、オンラインで使用可能です。
関連項目:
SQLCAの詳細は、ランタイム・エラーの処理を参照してください。
/*
* sample3.pc
* Host Arrays
*
* This program connects to ORACLE, declares and opens a cursor,
* fetches in batches using arrays, and prints the results using
* the function print_rows().
*/
#include <stdio.h>
#include <string.h>
#include <sqlca.h>
#define NAME_LENGTH 20
#define ARRAY_LENGTH 5
/* Another way to connect. */
char *username = "SCOTT";
char *password = "TIGER";
/* Declare a host structure tag. */
struct
{
int emp_number[ARRAY_LENGTH];
char emp_name[ARRAY_LENGTH][NAME_LENGTH];
float salary[ARRAY_LENGTH];
} emp_rec;
/* Declare this program's functions. */
void print_rows(); /* produces program output */
void sql_error(); /* handles unrecoverable errors */
main()
{
int num_ret; /* number of rows returned */
/* Connect to ORACLE. */
EXEC SQL WHENEVER SQLERROR DO sql_error("Connect error:");
EXEC SQL CONNECT :username IDENTIFIED BY :password;
printf("\nConnected to ORACLE as user: %s\n", username);
EXEC SQL WHENEVER SQLERROR DO sql_error("Oracle error:");
/* Declare a cursor for the FETCH. */
EXEC SQL DECLARE c1 CURSOR FOR
SELECT empno, ename, sal FROM emp;
EXEC SQL OPEN c1;
/* Initialize the number of rows. */
num_ret = 0;
/* Array fetch loop - ends when NOT FOUND becomes true. */
EXEC SQL WHENEVER NOT FOUND DO break;
for (;;)
{
EXEC SQL FETCH c1 INTO :emp_rec;
/* Print however many rows were returned. */
print_rows(sqlca.sqlerrd[2] - num_ret);
num_ret = sqlca.sqlerrd[2]; /* Reset the number. */
}
/* Print remaining rows from last fetch, if any. */
if ((sqlca.sqlerrd[2] - num_ret) > 0)
print_rows(sqlca.sqlerrd[2] - num_ret);
EXEC SQL CLOSE c1;
printf("\nAu revoir.\n\n\n");
/* Disconnect from the database. */
EXEC SQL COMMIT WORK RELEASE;
exit(0);
}
void
print_rows(n)
int n;
{
int i;
printf("\nNumber Employee Salary");
printf("\n------ -------- ------\n");
for (i = 0; i < n; i++)
printf("%-9d%-15.15s%9.2f\n", emp_rec.emp_number[i],
emp_rec.emp_name[i], emp_rec.salary[i]);
}
void
sql_error(msg)
char *msg;
{
EXEC SQL WHENEVER SQLERROR CONTINUE;
printf("\n%s", msg);
printf("\n% .70s \n", sqlca.sqlerrm.sqlerrmc);
EXEC SQL ROLLBACK WORK RELEASE;
exit(1);
}
このプログラムは、スクロール可能カーソルとともにホスト配列を使用する方法を示します。このプログラムは、demoディレクトリのファイルscdemo2.pcとして、オンラインで使用可能です。
注意:
結果セット内の行数の判断にはFETCH LASTを実行していることに注意してください。
/*
* A Sample program to demonstrate the use of scrollable
* cursors with host arrays.
*
* This program uses the hr/hr schema.Make sure
* that this schema exists before executing this program
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sqlca.h>
#define ARRAY_LENGTH 4
/* user and passwd */
char *username = "hr";
char *password = "hr";
/* Declare a host structure tag. */
struct emp_rec_array
{
int emp_number;
char emp_name[20];
float salary;
} emp_rec[ARRAY_LENGTH];
/* Print the result of the query */
void print_rows()
{
int i;
for (i=0; i<ARRAY_LENGTH; i++)
printf("%d %s %8.2f\n", emp_rec[i].emp_number,
emp_rec[i].emp_name, emp_rec[i].salary);
}
/* Oracle error handler */
void sql_error(char *msg)
{
EXEC SQL WHENEVER SQLERROR CONTINUE;
printf("\n%s", msg);
printf("\n% .70s \n", sqlca.sqlerrm.sqlerrmc);
EXEC SQL ROLLBACK WORK RELEASE;
exit(EXIT_FAILURE);
}
void main()
{
int noOfRows; /* Number of rows in the result set */
/* Error handler */
EXEC SQL WHENEVER SQLERROR DO sql_error("Connect error:");
/* Connect to the data base */
EXEC SQL CONNECT :username IDENTIFIED BY :password;
/* Error handle */
EXEC SQL WHENEVER SQLERROR DO sql_error("Oracle error:");
/* declare the cursor in scrollable mode */
EXEC SQL DECLARE c1 SCROLL CURSOR FOR
SELECT employee_id, first_name, salary FROM employees;
EXEC SQL OPEN c1;
EXEC SQL WHENEVER SQLERROR DO sql_error("Fetch Error:");
/* This is a dummy fetch to find out the number of rows
in the result set */
EXEC SQL FETCH LAST c1 INTO :emp_rec;
/* The number of rows in the result set is given by
the value of sqlca.sqlerrd[2] */
noOfRows = sqlca. sqlerrd[2];
printf("Total number of rows in the result set %d:\n",
noOfRows);
/* Fetch the first ARRAY_LENGTH number of rows */
EXEC SQL FETCH FIRST c1 INTO :emp_rec;
printf("******************** DEFAULT : \n");
print_rows();
/* Fetch the next set of ARRAY_LENGTH rows */
EXEC SQL FETCH NEXT c1 INTO :emp_rec;
printf("******************** NEXT : \n");
print_rows();
/* Fetch a set of ARRAY_LENGTH rows from the 3rd row onwards */
EXEC SQL FETCH ABSOLUTE 3 c1 INTO :emp_rec;
printf("******************** ABSOLUTE 3 : \n");
print_rows();
/* Fetch the current ARRAY_LENGTH set of rows */
EXEC SQL FETCH CURRENT c1 INTO :emp_rec;
printf("******************** CURRENT : \n");
print_rows();
/* Fetch a set of ARRAY_LENGTH rows from the 2nd offset
from the current cursor position */
EXEC SQL FETCH RELATIVE 2 c1 INTO :emp_rec;
printf("******************** RELATIVE 2 : \n");
print_rows();
/* Again Fetch the first ARRAY_LENGTH number of rows */
EXEC SQL FETCH ABSOLUTE 0 c1 INTO :emp_rec;
printf("******************** ABSOLUTE 0 : \n");
print_rows();
/* close the cursor */
EXEC SQL CLOSE c1;
/* Disconnect from the database. */
EXEC SQL COMMIT WORK RELEASE;
exit(EXIT_SUCCESS);
}
副問合せ文中を除き、SELECT文のWHERE句ではホスト配列を使用できません。例は、WHERE句の使用方法についてを参照してください。
またSELECT文またはFETCH文のINTO句では、単純ホスト変数とホスト配列を併用できません。ホスト変数のうち1つでも配列があれば、すべてのホスト変数を配列にする必要があります。
表8-1は、SELECT INTO文で有効なホスト配列の使用を示しています。
表8-1 SELECT INTOで有効なホスト配列
| INTO句 | WHERE句 | 有効/無効 |
|---|---|---|
array |
array |
無効 |
scalar |
scalar |
有効 |
array |
scalar |
有効 |
scalar |
array |
無効 |
配列をSELECTおよびFETCHするときは、必ずインジケータ配列を使用します。このようにして、関連する出力ホスト配列内にNULLがあるかどうかをテストできます。
DBMS = V7またはDBMS=v8のときに、インジケータ配列に対応付けられていないホスト配列にNULL列値をSELECTまたはFETCHすると、Oracleは処理を停止し、sqlerrd[2]に処理済行数を設定しエラー・メッセージを出します。
また、SELECTまたはFETCHの結果、NULLの使用によるORA-24347などの警告が発生した場合や、列にインジケータ配列がない場合には、Oracleは処理を停止します。SELECTまたはFETCHのすべての列で標識変数を使用します。インジケータのない列がある場合は、プリコンパイラ・オプションunsafe_null=yesをかわりに使用できます。