標識変数とは

プリコンパイラ製品独特の概念として標識変数(indicator variable)というものがあります。 標識変数はホスト変数(ORACLEとクライアントプログラム間で受け渡し可能な変数)のステータスを表示、 設定するものでC言語等では変数の値としてNULLを表すことができないためNULLを扱うプログラムではホスト変数に標識変数を紐づける必要があります。

標識変数の使い方

以下のようにホスト変数に続けてINDICATOR句とともに付与することで使用可能です。 (フェッチした段階で標識変数に値が自動的にセットされる)
EXEC SQL FETCH emp_cursor
INTO :empno , :ename INDICATOR :ind_ename, :comm INDICATOR :ind_comm;

以下のようにINDICATOR句を省略してホスト変数に続けて記述することも可能です。
EXEC SQL FETCH emp_cursor
INTO :empno , :ename:ind_ename, :comm:ind_comm;

標識変数の値の意味

0・・・正常
-1・・・NULL
-2・・・LONG型で切り捨てられた(文字数不明)
>0・・・切り捨てられた(値は文字数)

サンプルプログラム

以下はscottユーザでselect empno,ename,comm を実行するプログラムです。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//接続定義
#define ORAUSER    "scott"
#define ORAPASSWD  "tiger"
#define ORASYSID_T "ORCL102"

EXEC SQL INCLUDE sqlca;
EXEC SQL BEGIN DECLARE SECTION;
     VARCHAR  h_user[64];
     VARCHAR  h_pswd[64];
     VARCHAR  h_sid[64];
     long     empno;
     short    ind_empno;
     VARCHAR  ename[10+1];
     short    ind_ename[10+1];
     float    comm;
     short    ind_comm;
EXEC SQL END   DECLARE SECTION;

//oracleエラー時にコールされる関数
void sql_error(char* msg)
{
    char err_msg[128];
    size_t buf_len, msg_len;

    EXEC SQL WHENEVER SQLERROR CONTINUE;

    printf("\n%s\n", msg);
    buf_len = sizeof (err_msg);
    sqlglm(err_msg, &buf_len, &msg_len);
    printf("%.*s\n", msg_len, err_msg);

    EXEC SQL ROLLBACK RELEASE;
    exit(EXIT_FAILURE);
}

//メイン関数
int main()
{
    //ユーザ名、パスワードセット
    strcpy((char *)h_user.arr,ORAUSER);
    h_user.len = strlen((char *)h_user.arr);
    strcpy((char *)h_pswd.arr,ORAPASSWD);
    h_pswd.len = strlen((char *)h_pswd.arr);
    strcpy((char *)h_sid.arr,ORASYSID_T);
    h_sid.len = strlen((char *)h_sid.arr);

    EXEC SQL WHENEVER SQLERROR DO sql_error("ORACLE error--\n");
    //接続
    EXEC SQL CONNECT :h_user IDENTIFIED BY :h_pswd;

    //select
    EXEC SQL DECLARE emp_cursor CURSOR FOR
      SELECT empno,ename,comm FROM scott.emp;
    EXEC SQL OPEN emp_cursor;
    EXEC SQL WHENEVER SQLERROR CONTINUE;
    printf("empno,ename,comm\n");
    for (;;){
      EXEC SQL FETCH emp_cursor INTO :empno , :ename INDICATOR :ind_ename, :comm:ind_comm;
      if (sqlca.sqlcode == 0){
        if (ind_comm == -1) {
          printf("%d,%.*s,*null*\n", empno,ename.len,ename.arr,comm);
        }else{
          printf("%d,%.*s,%8.2f\n", empno,ename.len,ename.arr,comm);
        }
      }else if (sqlca.sqlcode == 1403){
        printf("end of data\n");
        break;
      }else{
        sql_error("error");
      }
    }

    EXEC SQL COMMIT WORK RELEASE;
    printf("END\n");
    return 0;
}

コンパイル+実行
[ora102@linux1 20131103]$ make -f $ORACLE_HOME/precomp/demo/proc/demo_proc.mk  OBJS=select.o EXE=select build
make -f /app/oracle/product/10.2.0/db_1/precomp/demo/proc/demo_proc.mk PROCFLAGS="" PCCSRC=select I_SYM=include= pc1
make[1]: ディレクトリ `/home/ora102/kensho/20131103' に入ります
proc  iname=select include=. include=/app/oracle/product/10.2.0/db_1/precomp/public include=/app/oracle/product/10.2.0/db_1/rdbms/public include=/app/oracle/product/10.2.0/db_1/rdbms/demo include=/app/oracle/product/10.2.0/db_1/plsql/public
 include=/app/oracle/product/10.2.0/db_1/network/public

Pro*C/C++: Release 10.2.0.4.0 - Production on 月 11月 4 02:52:01 2013

Copyright (c) 1982, 2007, Oracle.  All rights reserved.

システムのデフォルト・オプション値: /app/oracle/product/10.2.0/db_1/precomp/admin/pcscfg.cfg

make[1]: ディレクトリ `/home/ora102/kensho/20131103' から出ます
/usr/bin/gcc  -O2   -fPIC -DPRECOMP -I. -I/app/oracle/product/10.2.0/db_1/precomp/public -I/app/oracle/product/10.2.0/db_1/rdbms/public -I/app/oracle/product/10.2.0/db_1/rdbms/demo -I/app/oracle/product/10.2.0/db_1/plsql/public
 -I/app/oracle/product/10.2.0/db_1/network/public -DLINUX -D_GNU_SOURCE -D_LARGEFILE64_SOURCE=1 -D_LARGEFILE_SOURCE=1 -DSLTS_ENABLE -DSLMXMX_ENABLE -D_REENTRANT -DNS_THREADS    -c select.c
/usr/bin/gcc -o select select.o -L/app/oracle/product/10.2.0/db_1/lib/ -lclntsh `cat /app/oracle/product/10.2.0/db_1/lib/ldflags`   `cat /app/oracle/product/10.2.0/db_1/lib/sysliblist` -ldl -lm
[ora102@linux1 20131103]$ ./select
empno,ename,comm
7369,SMITH,*null*
7499,ALLEN,  300.00
7521,WARD,  500.00
7566,JONES,*null*
7654,MARTIN, 1400.00
7698,BLAKE,*null*
7782,CLARK,*null*
7788,SCOTT,*null*
7839,KING,*null*
7844,TURNER,    0.00
7876,ADAMS,*null*
7900,JAMES,*null*
7902,FORD,  100.10
7934,MILLER,*null*
end of data
END

標識変数を使わないとどうなるか

デフォルト設定では標識変数を使わないでNULL値をfetchした場合「ORA-01405: フェッチした列の値がNULLです」のエラーとなりますが、 MODE=ORACLE DBMS=V8 UNSAFE_NULL=YESのオプションでプリコンパイルするとORA-01405の発生を抑止することができます。 この場合でエラーは発生しませんがNULLデータをフェッチした場合ホスト変数に格納されるデータは不正な値となります。 本検証環境で実際に確認した限り初期値または前回の値のままで表示されました。
[ora102@linux1 20131103]$ ./select
empno,ename,comm
7369,SMITH,    0.00
7499,ALLEN,  300.00
7521,WARD,  500.00
7566,JONES,  500.00
7654,MARTIN, 1400.00
7698,BLAKE, 1400.00
7782,CLARK, 1400.00
7788,SCOTT, 1400.00
7839,KING, 1400.00
7844,TURNER,    0.00
7876,ADAMS,    0.00
7900,JAMES,    0.00
7902,FORD,  100.10
7934,MILLER,  100.10
end of data
END

パフォーマンスについて

プログラムによっては標識変数を使うことでほんおわずかながらfetchの性能が劣化するケースがあります。 値にNULLがないことがわかっており少しでも性能を上げさせたい場合はあえて標識変数を使わないという選択肢もあります。
カスタム検索

★ORACLE案件承ります
▼ORACLE掲示板