プライマリ・コンテンツに移動
Pro*C/C++プログラマーズ・ガイド
12c リリース1(12.1)
B71397-03
目次へ移動
目次
索引へ移動
索引

前
次

構造体の配列

スカラー配列を使用すると、1つの列での複数行操作が可能です。また、スカラー構造体を使用すると、1つの行での複数列操作が可能です。

しかし、複数列で複数行を操作する場合、これまでは複数のスカラー並列配列を個別にまたは1つの構造体中でカプセル化して割り当てる必要がありました。この方法よりも、このデータ構造を複数の構造体からなる1つの配列にしなおす方が便利です。

Pro*C/C++では構造体配列がサポートされています。アプリケーション・プログラマはC構造体の配列を使用して複数行および複数列の操作を実行できます。この機能拡張により、Pro*C/C++ではスカラー構造体の単純な配列を埋込みSQL文でバインド変数として処理できるため、データの処理がより簡単になります。これで、プログラミングがさらに直観的になり、データ編成もはるかに自由にできます。

Pro*C/C++では、構造体配列がバインド変数としてサポートされるのみでなく、インジケータ構造体の配列を構造体配列の宣言と併用できます。

注意:

構造体をPL/SQLレコードにバインドすることと、構造体の配列をPL/SQLレコードの表にバインドすることは、この新たな機能の一部ではありません。また、構造体の配列を埋込みPL/SQLブロック内で使用することもできません。構造体の配列の制限も参照してください。

構造体の配列は、複数の列で複数の行を操作するために使用され、通常次のように使用されると考えてください。

構造体の配列の使用方法

構造体の配列という概念は、C言語のプログラマにとって特に目新しいものではありません。しかし、複数の並列配列からなる1つの構造体と比較してみると、データの格納方法に考え方の違いがあります。

複数の並列配列からなる1つの構造体では、個々の列のデータが連続して格納されます。一方、構造体の配列では、列のデータはインタリーブされます。この場合、配列内の各列の間は、その構造体内の他の列に必要な空白で区切られます。この空白はストライドと呼ばれます。

構造体の配列の制限

Pro*C/C++では、構造体配列の使用に次の制限事項があります。

  • 構造体配列(通常の構造体による)を埋込みPL/SQLブロック内で使用できません。

  • 構造体配列をWHERE句やFROM句で使用できません。

  • 構造体配列はOracle動的SQL方法4では使用できません。ANSI動的SQLでは使用できます。ANSI動的SQLも参照してください。

  • 構造体配列をUPDATE文のSET句で使用できません。

構造体配列の宣言では構文に違いはありません。ただし、構造体配列を使用する場合には留意事項がいくつかあります。

構造体の配列の宣言について

Pro*C/C++アプリケーションで使用する構造体配列を宣言する場合、プログラマは必ず次の点に留意してください。

  • 構造体には必ず構造体タグを付けてください。例は次のとおりです。

    struct person {
       char name[15];
       int  age;
    } people[10];

このコード・セグメントでは、person変数が構造体のタグです。このタグによって、プリコンパイラは構造体の名前を使用してストライドのサイズを計算できます。

  • 構造体のメンバーは配列にしないでください。ただし、charVARCHARなどの文字型の場合には、この規則は適用されません。このような型の変数の宣言で配列の構文が使用されるためです。

  • charおよびVARCHAR型のメンバーは2次元配列にしないでください。

  • ネストされた構造体は構造体配列のメンバーになることはできません。Pro*C/C++の旧リリースでは、ネストされた構造体はサポートされていなかったため、この制限は新しいものではありません。

  • 構造体自体のサイズは、符号付き4バイト数で表すことのできる最大値を超えないようにしてください。通常、この最大値は2GBです。

構造体配列の使用に関する前述の制限事項を満たしているので、Pro*C/C++では次の宣言は有効です。

struct department {
   int deptno;
   char dname[15];
   char loc[14];
} dept[4];

一方、次の宣言は無効です。

struct {              /* the struct is missing a structure tag */
  int empno[15];      /* struct members may not be arrays */
  char ename[15][10]; /* character types may not be 2-dimensional */
  struct nested {
    int salary;   /* nested struct not permitted in array of structs */
  } sal_struct;
} bad[15];

データ型の同値化を、構造体配列自体や構造体内の個々のフィールドには適用できないことにも注意してください。たとえば、empnoが前述の無効な構造体内の配列として宣言されていない場合、次の文は無効です。

exec sql var bad[3].empno is integer(4);

プリコンパイラには、構造体配列中の個々の構造体要素を追跡し記録する機能はありません。ただし、次を実行すれば、期待した結果が得られます。

typedef int myint;
exec sql type myint is integer(4);

struct equiv {
  myint empno; /* now legally considered an integer(4) datatype */
   ...
} ok[15];

Pro*C/C++の以前のリリースでは、個別の配列項目の同値化がサポートされていなかったため、これは予測できたことといえます。たとえば、次のスカラー配列宣言は何が有効で、何が有効でないかを示しています。

int empno[15];
exec sql var empno[3] is integer(4); /* illegal */

myint empno[15]; /* legal */

基本的には、個々の配列項目を同値化することはできません。

変数のガイドライン

標識変数は、構造体の配列の宣言でも、通常の構造体の宣言の場合とほとんど同じはたらきをします。構造体のインジケータ配列の宣言は、構造体の配列についての次の規則にも従う必要があります。

  • インジケータ構造体に含まれるフィールド数は、対応する構造体の配列に含まれるフィールド数以下である必要があります。

  • フィールドの順序は、対応する構造体の配列のメンバーの順序に一致する必要があります。

  • インジケータ構造体に含まれるすべての要素のデータ型は、shortである必要があります。

  • インジケータ配列のサイズは、ホスト変数で宣言されたサイズ以上である必要があります。ホスト変数で宣言されたサイズより大きい値は指定できますが、それより小さい値は指定できません。

これらの規則のほとんどには、Pro*C/C++の以前のリリースでの構造体使用規則が反映されています。配列制限も以前使用されたスカラーの配列に対するものと同じです。

これらの規則が適用されている場合に、次のように構造体を宣言するとします。

struct department {
   int deptno;
   char dname[15];
   char loc[14];
} dept[4];

次の標識変数の構造体の宣言は有効です。

struct department_ind {
   short deptno_ind;
   short dname_ind;
   short loc_ind;
} dept_ind[4];

一方、次は標識変数として無効です。

struct{               /* missing indicator structure tag */
  int deptno_ind;     /* indicator variable not of type short */
  short dname_ind[15];/* array element forbidden in indicator struct */
  short loc_ind[14];  /* array element forbidden in indicator struct */
} bad_ind[2];     /* indicator array size is smaller than host array */

構造体配列へのポインタの宣言について

構造体配列へのポインタを宣言する方が適切な場合があります。これにより、構造体配列へのポインタを他の関数に渡したり、埋込みSQL文で直接指定したりできます。

注意:

構造体配列へのポインタが参照する配列の長さは、プリコンパイル中にはわかりません。このため、埋込みSQL文で構造体の配列へのポインタ型になっているバインド変数を使用するときには、明示的なFOR句を使用してください。

ただし、FOR句は埋込みSQL SELECT文では指定できません。したがって、データを取り出して構造体配列へのポインタに入れる場合には、必ずカーソルとFETCH文をFOR句とともに明示的に指定してください。

次の例は、Pro*C/C++での構造体配列の機能について、様々な使用方法を示しています。

例1: スカラー構造体の単純な配列

次の構造体の宣言を指定したとします。

struct department {
   int deptno;
   char dname[15];
   char loc[14];
} my_dept[4];

次のようにdeptデータを選択してmy_deptに入れることができます。

exec sql select * into :my_dept from dept;

また、最初にmy_deptにデータを入れてから、dept表に一括して挿入できます。

exec sql insert into dept values (:my_dept);

標識変数を指定するには、構造体のパラレル・インジケータ配列を宣言します。

struct deptartment_ind {
   short deptno_ind;
   short dname_ind;
   short loc_ind;
} my_dept_ind[4];

データの選択に使用される問合せは、標識変数の指定が追加されたこと以外は同じです。

exec sql select * into :my_dept indicator :my_dept_ind from dept;

同様に、データの挿入時にもインジケータを指定できます。

exec sql insert into dept values (:my_dept indicator :my_dept_ind);

例2: スカラーの配列と構造体の配列との組合せ使用

Pro*C/C++の旧リリースと同様に、ユーザー・データのバルク処理機能に複数の配列を使用する場合は、各配列のサイズを同じにする必要があります。同じにしないと、最も小さい配列のサイズが選択され、残りの配列は変更されません。

次の宣言を指定したとします。

struct employee {
   int empno;
   char ename[11];
} emp[14];

float sal[14];
float comm[14];

ただ1つの問合せで、すべての列に対して複数行を選択できます。

exec sql select empno, ename, sal, comm into :emp, :sal, :comm from emp;

また、コミッション列の値がNULLかどうかを確認する必要があります。次のように宣言すれば、1つのインジケータ配列を指定するのみで済みます。

short comm_ind[14];
...
exec sql select empno, ename, sal, comm
   into :emp, :sal, :comm indicator :comm_ind from emp;

問合せからのインジケータ情報をすべてカプセル化した構造体の単一インジケータ配列は宣言できません。したがって、次のようになります。

struct employee_ind {   /* example of illegal usage */
   short empno_ind;
   short ename_ind;
   short sal_ind;
   short comm_ind;
} illegal_ind[15];

exec sql select empno, ename, sal, comm
   into :emp, :sal, :comm indicator :illegal_ind from emp;

この列は無効です(またお薦めできません)。上の文にはSELECT...INTOリスト全体ではなく、comm列しかないインジケータ配列が対応付けられています。

構造体の配列とsalcommおよびcomm_ind配列にデータを挿入する場合、その挿入方法は非常に簡単です。

exec sql insert into emp (empno, ename, sal, comm)
   values (:emp, :sal, :comm indicator :comm_ind);

例3: 複数の構造体配列と1つのカーソルとの組合せ使用

次の宣言をこの例として使用します。

struct employee {
   int empno;
   char ename[11];
   char job[10];
} emp[14];

struct compensation {
   int sal;
   int comm;
} wage[14];

struct compensation_ind {
   short sal_ind;
   short comm_ind;
} wage_ind[14];

Oracleのプログラムでは、構造体配列を次のように指定できます。

exec sql declare c cursor for 
   select empno, ename, job, sal, comm from emp;

exec sql open c;

exec sql whenever not found do break;
while(1)
{
  exec sql fetch c into :emp, :wage indicator :wage_ind;
  ... process batch rows returned by the fetch ...
}

printf("%d rows selected.\n", sqlca.sqlerrd[2]);

exec sql close c;

FOR句の使用方法について

FOR句を使用しても、FETCHで取り出す行数を指定できます。FOR句は、SELECT文を使用している場合には指定できませんが、INSERT文やFETCH文の場合には指定できます。

元の宣言に次を追加します。

int limit = 10;

次は、それに対応したコードの例です。

   exec sql for :limit
      fetch c into :emp, :wage indicator :wage_ind;

例4: 個々の配列メンバーおよび構造体メンバーの参照

これまでのPro*C/C++リリースで、構造体配列内の1構造体に対する配列参照が可能となっています。これによりバインド式はスカラーの単純な構造体で解決できるため、次は有効です。

exec sql select * into :dept[3] from emp;

次の例に示すように、構造体配列内の特定の構造体のスカラー・メンバーを個別に参照することも可能です。

exec sql select dname into :dept[3].dname from dept where ...;

この場合には当然、問合せは単一行問合せにする必要があり、1行のみを選択してこのバインド式で表される変数に代入します。

例5: 標識変数の使用(特殊な場合)

これまでのPro*C/C++リリースでは、インジケータ構造体のフィールド数はそれに対応するバインド変数構造体と同じにする必要がありました。構造体を通常に使用する場合には、この制限は緩和されています。前述の構造体のインジケータ配列についてのガイドラインに従っているので、次の例が可能です。

struct employee {
    float comm;
    float sal;
    int empno;
    char ename[10];
} emp[14];

struct employee_ind {
    short comm;
} emp_ind[14];

exec sql select comm, sal, empno, ename 
   into :emp indicator :emp_ind from emp;

標識変数はバインド値と1対1でマップされます。これらは、最初のフィールドから、対応した順番にマップされます。

ただし、他のフィールドでフェッチされた値がNULLであったり、インジケータが付いていなかったりすると、次のエラーが発生するため注意してください。

ORA-1405: fetched column value is NULL

たとえば、salがNULL値可能の場合にsalにはインジケータがないため、このエラーが発生します。

次のように構造体の配列を変更したとします。

struct employee {
   int empno;
   char ename[10];
   float sal;
   float comm;
} emp[15];

しかし、同じ構造体のインジケータ配列がまだ使用されているとします。

インジケータのマップは対応した順番に実行されるため、commインジケータがempnoフィールドにマップされて、commバインド変数にインジケータがないままとなり、再度ORA-01405エラーとなります。

対応するバインド変数の構造体よりもフィールド数の少ないインジケータ構造体を指定したときに、ORA-01405エラーが発生しないようにするには、NULL値可能の属性を最初から順番に並べる必要があります。

この例は、非配列の構造体を使用すれば複数列の単一行フェッチに簡単に変更でき、インジケータ構造体が次のように宣言されている場合と同等のはたらきをすると考えることができます。

struct employee_ind {
   short comm;
   short sal;
   short empno;
   short ename;
} emp_ind;

Pro*C/C++ではインジケータ構造体のフィールド数と対応する値の構造体のフィールド数が同じである必要がなくなったため、前述の例はこれまでPro*C/C++で無効でしたが現在は有効になりました。

Oracleのインジケータ構造体は、次のように簡単に指定できます。

struct employee_ind {
   short comm;
} emp_ind;

次のように配列なし構造体であるempおよびemp_indを指定すると、単一行フェッチを実行できます。

exec sql fetch comm, sal, empno, ename
   into :emp indicator :emp_ind from emp;

この場合にも、commインジケータがcommバインド変数にどのようにマップされるかに注意してください。

例6: 構造体配列へのポインタの使用

この例では、構造体配列へのポインタの使用方法を示します。

次の型の宣言を考えます。

typedef struct dept {
   int deptno;
   char dname[15];
   char loc[14];
} dept;

その型の構造体配列へのポインタを操作すると、様々な処理を実行できます。たとえば、構造体配列へのポインタを他の関数に渡すことができます。

void insert_data(d, n)
   dept *d;
   int n;
{
    exec sql for :n insert into dept values (:d);
}

void fetch_data(d, n)
   dept *d;
   int n;
{
   exec sql declare c cursor for select deptno, dname, loc from dept;
   exec sql open c;
   exec sql for :n fetch c into :d;
   exec sql close c;
}

このような関数をコールするには、例に示すように構造体配列のアドレスを渡します。

dept d[4];
dept *dptr = &d[0];
const int n = 4;

fetch_data(dptr, n);
insert_data(d, n); /* We are treating '&d[0]' as being equal to 'd' */

一部の埋込みSQL文では、構造体配列へのポインタを直接使用することができます。

exec sql for :n insert into dept values (:dptr);

FOR句の使用方法を間違えないように、十分に注意してください。