動的SQLを使用してデータベースからデータの取り出しを行う場合には、実行結果を受け取る相手指定を指定します。相手指定に実行結果を受け取るためには、選択リストの情報をその相手指定の属性と一致させることが必要です。そこで、データベースからSQL記述子域に選択リストの情報を取り込んで、属性を変更します。SQL記述子域の位置づけを以下に示します。
SQL記述子域は、識別子COUNTと0以上の項目記述子域(SQL記述子域の各要素)で構成されています。COUNTは、SQL記述子域の選択リストの数を示し、そのデータ型は2進の精度を持つ真数です。以下の図では、SQL記述子域の選択リストの数をnで表しています。
項目記述子域は以下の内容で構成されています。
識別子 | 意味 | データ型 |
---|---|---|
TYPE | データ型のコード | 2進の精度を持つ真数 |
LENGTH | 長さ(文字数) | 2進の精度を持つ真数 |
OCTET_LENGTH | 長さ(バイト数) | 2進の精度を持つ真数 |
PRECISION | 精度 | 2進の精度を持つ真数 |
SCALE | 位取り | 2進の精度を持つ真数 |
NULLABLE | NULL値を許すかどうかの識別 | 2進の精度を持つ真数 |
INDICATOR | 標識変数の値 | 2進の精度を持つ真数 |
DATA | データ値 | TYPE、 |
NAME | 名前 | 長さ38バイト以上108バイト以下の文字列型 |
CHARACTER_SET_NAME | 文字セット名 | 長さ38バイト以上108バイト以下の文字列型 |
DATETIME_INTERVAL_CODE | 日時型・時間隔型のコード | 2進の精度を持つ真数 |
DATETIME_INTERVAL_PRECISION | 時間隔先行フィールド精度 | 2進の精度を持つ真数 |
SQL記述子域のTYPEの値(データ型のコード)とSQLのデータ型の対応は以下のとおりです。
コード | データ型 |
---|---|
1 | CHARACTERまたはNATIONAL CHARACTER |
2 | NUMERIC |
3 | DECIMAL |
4 | INTEGER |
5 | SMALLINT |
7 | REAL |
8 | DOUBLE PRECISION |
9 | DATE, TIMEまたはTIMESTAMP |
10 | INTERVAL |
12 | CHARACTER VARYINGまたはNATIONAL CHARACTER VARYING |
30 | BLOB |
31 | (注) |
50 | ROW_ID |
注) SQL埋込みCプログラムにおいて、8バイトの整数型(long long型など)と対応します。DESCRIBE文では、コードに31は返却されません。
TYPEの値が1または12のとき、CHARACTER_SET_NAMEの値(文字セット名)は、文字列型と各国語文字列型で以下のように異なります。
文字列型 : BASIC
各国語文字列型 : NCHAR
TYPEの値が日時型を示す9の場合、DATETIME_INTERVAL_CODEの値は以下のとおりです。
コード | データ型 |
---|---|
1 | DATE |
2 | TIME |
3 | TIMESTAMP |
TYPEの値が時間隔型を示す10の場合、DATETIME_INTERVAL_CODEの値は以下のとおりです。
コード | 時間隔修飾子 |
---|---|
1 | YEAR |
2 | MONTH |
3 | DAY |
4 | HOUR |
5 | MINUTE |
6 | SECOND |
7 | YEAR TO MONTH |
8 | DAY TO HOUR |
9 | DAY TO MINUTE |
10 | DAY TO SECOND |
11 | HOUR TO MINUTE |
12 | HOUR TO SECOND |
13 | MINUTE TO SECOND |
実行結果の取り出し手順
USING記述子を使用して、実行結果を取り出す方法の順序を“図4.2 被準備文の実行結果の取り出し手順”に示します。ここでは処理の流れを説明します。なお、各SQL文については“図4.2 被準備文の実行結果の取り出し手順”以降で説明します。
図4.2 被準備文の実行結果の取り出し手順
SQL文を動的に変更してデータ操作を行う場合、そのSQL文を実行するための準備処理を行います。
動的SQLの実行の準備を行うには、PREPARE文を使用します。プログラムの実行時にSQL文を作成して実行させる場合に、そのSQL文を“SQL文変数”と呼ばれるホスト変数に文字列として格納します。そして、PREPARE文で実行の準備をします。このとき、SQL文変数に格納されたSQL文は文を識別するSQL文識別子と対応づけられます。
SQL文変数の内容が準備可能動的UPDATE文:位置づけまたは準備可能動的DELETE文:位置づけの場合は、事前に対応するカーソルをオープンしておくことが必要です。また、指定したカーソルは動的カーソルであることが必要です。
なお、すでにPREPARE文で定義されたSQL文識別子に対して、再度PREPARE文を実行する場合は、先の被準備文が解放され、新たに被準備文が準備されます。先の被準備文が動的SELECT文の場合、SQL文識別子に対応するカーソルを参照している準備可能動的UPDATE文:位置づけまたは準備可能動的DELETE文:位置づけの被準備文も同時に解放されます。ただし、被準備文が動的SELECT文の場合、SQL文識別子に対応するカーソルは閉じられた状態であることが必要です。
PREPARE文の指定例を以下に示します。実行準備を行うSQL文は、SQL文変数strに格納されているとします。このSQL文のSQL文識別子をSTMと定義します。
PREPARE STM FROM :str (1) (2)
(1) SQL文識別子
(2) SQL文変数
PREPARE文によって準備された被準備文を解放するには、DEALLOCATE PREPARE文を使用します。
被準備文が動的SELECT文の場合、SQL文識別子に対応するカーソルを参照している準備可能動的UPDATE文:位置づけまたは準備可能動的DELETE文:位置づけの被準備文も同時に解放されます。また、その際のカーソルは、閉じられた状態であることが必要です。
DEALLOCATE PREPARE文の指定例を以下に示します。解放する被準備文は、PREPARE文によってSQL文識別子をSTMと定義された被準備文とします。
DEALLOCATE PREPARE STM (1)
(1) SQL文識別子
“図4.2 被準備文の実行結果の取り出し手順”で示したように、データベースからSQL記述子域に選択リストの情報を取り込んだり、SQL記述子域に取り込まれた選択リストの情報を取得するには、それぞれSQL文を使用します。SQL記述子域を操作する各SQL文とその機能について以下で説明します。
ALLOCATE DESCRIPTOR文は、指定された記述子名のSQL記述子域を割り当てます。割り当てられたSQL記述子域は、実現値に指定された個数の項目記述子域を所有します。
“DESC1”という記述子名のSQL記述子域を割り当てる例を以下に示します。このとき、SQL記述子域の項目記述子域の数を200とします。
ALLOCATE DESCRIPTOR 'DESC1' WITH MAX 200 (1) (2)
(1) 記述子名
(2) 実現値
DEALLOCATE DESCRIPTOR文は、ALLOCATE DESCRIPTOR文で割り当てたSQL記述子域を解放します。DEALLOCATE DESCRIPTOR文で指定した記述子名が動的OPEN文に指定されている場合、カーソルは閉じられた状態であることが必要です。
“DESC1”という記述子名のSQL記述子域を解放する例を以下に示します。
DEALLOCATE DESCRIPTOR 'DESC1' (1)
(1) 記述子名
DESCRIBE文では、PREPARE文で準備された被準備文の選択リストの情報をSQL記述子域に取り込みます。このSQL文を“出力DESCRIBE文”といいます。
SQL文識別子STM2に対応する被準備文の選択リストの情報を、SQL記述子域に取り込む出力DESCRIBE文の例を以下に示します。
DESCRIBE OUTPUT STM2 USING SQL DESCRIPTOR 'DESC2' (1) (2)
(1) SQL文識別子
(2) 記述子名
SQL文識別子STM2に対する被準備文が以下の場合、SQL記述子域DESC2の内容は次のようになります。
SELECT 製品名, 在庫数量 FROM 在庫管理.在庫表 WHERE 製品番号 = 110 (1)
(1) 選択リスト
COUNT : 2
選択リストの個数2が設定されます。
項目記述子域
製品名、在庫数量の属性がそれぞれ設定されます。
製品名 : NATIONAL CHARACTER(10)(NOT NULL制約あり)
在庫数量 : INTEGER(NOT NULL制約なし)
識別子 | 製品名 | 在庫数量 |
---|---|---|
TYPE | 1 | 4 |
LENGTH | 10 | 4 |
OCTET_LENGTH | 20 | 4 |
PRECISION | 0 | 31 |
SCALE | 0 | 0 |
NULLABLE | 0 | 1 |
INDICATOR | 値は設定されない | 値は設定されない |
DATA | 値は設定されない | 値は設定されない |
NAME | 製品名 (注) | 在庫数量 (注) |
CHARACTER_SET_NAME | NCHAR (注) | 38バイトの空白 |
DATETIME_INTERVAL_CODE | 0 | 0 |
DATETIME_INTERVAL_PRECISION | 0 | 0 |
注) 38バイトに満たない場合は、残りの部分に空白が設定されます。
DESCRIPTOR取得文では、SQL記述子域に設定されている情報をホスト変数に取り出します。ホスト変数のデータ型は、それぞれの取得識別子のデータ型と一致していることが必要です。
選択リストの個数を、ホスト変数varcountに取得する例を以下に示します。
GET DESCRIPTOR 'DESC1' :varcount = COUNT (1) (2) (3)
(1) 記述子名
(2) 単純相手指定
(3) 取得識別子
SQL記述子域に設定されている選択リストの内容を、それぞれホスト変数vartype、varleng、varoctetに取得する例を以下に示します。また、取得する項目記述子域の要素番号はvarwcountに設定されている値とします。
GET DESCRIPTOR 'DESC1' VALUE :varwcount :vartype = TYPE, (1) (2) (3) (4) :varlengz = LENGTH, :varoctet = OCTET_LENGTH (3) (4) (3) (4)
(1) 記述子名
(2) SQLVAR取得番号
(3) 単純相手指定
(4) 取得識別子
SQL記述子域から実行結果を取得する場合は、ホスト変数のデータ型は、対応する項目記述子域のTYPE、LENGTH、OCTET_LENGTH、PRECISION、SCALE、CHARACTER_SET_NAMEに対応するデータ型であることが必要です。
SQL記述子域に設定されている実行結果を、ホスト変数vardataに取得する例を以下に示します。また、取得する項目記述子域の要素番号はvarwcountに設定されている値とします。
GET DESCRIPTOR 'DESC1' VALUE :varwcount :vardata = DATA (1) (2) (3) (4)
(1) 記述子名
(2) SQLVAR取得番号
(3) 単純相手指定
(4) 取得識別子
DESCRIPTOR設定文では、実行結果を取り出す相手指定のデータ型を、SQL記述子域の選択リストに設定します。定数またはホスト変数で指定します。ホスト変数のデータ型は、それぞれの設定識別子のデータ型と一致していることが必要です。
SQL記述子域の選択リストのデータ型を変更する例を以下に示します。ここでは、実行結果を取り出すホスト変数vardataのデータ型が、CHARACTER(11)と宣言されているとします。したがって、選択リストのデータ型をCHARACTER(10)に変更します。設定を行う項目記述子域の要素番号はvarwcountに設定されている値とします。
SET DESCRIPTOR 'DESC1' VALUE :varwcount TYPE = 1, (1) (2) (3) (4) LENGTH = 10, OCTET_LENGTH = 10 (3) (4) (3) (4)
(1) 記述子名
(2) SQLVAR設定番号
(3) 設定識別子
(4) 単純値指定
データの取り出しを連続的に行う場合には、動的SELECT文を準備して実行します。SQL記述子域を使用して、動的SELECT文を実行するアプリケーションの例を以下に示します。
端末から入力した動的SELECT文の例です。
#include <stdio.h> #include <string.h> EXEC SQL BEGIN DECLARE SECTION; char SQLSTATE[6]; char SQLMSG[256]; VARCHAR str[1024]; /* SQL文変数 */ …(1) short vartype; /* データ型 */ …(2) short varleng; /* 文字数 */ …(2) short varolen; /* バイト数 */ …(2) short varprec; /* 精度 */ …(2) short varscal; /* 位取り */ …(2) char varname[39]; /* 列の名前 */ …(2) char varchar[39]; /* 文字セット名 */ …(2) short vardcod; /* 日時,時間隔 */ …(2) short vardpre; /* 時間隔精度 */ …(2) short varocount; /* 選択リストの数 */ short i; /* SQL記述子域のカウンタ */ char ocdata[15]; /* CHARACTER型 */ …(3) char CHARACTER SET IS NCHAR oncdata[21]; /* NCHAR型 */ …(3) long oidata; /* 真数型 */ …(3) double ofdata; /* 概数型 */ …(3) VARCHAR oddata[20]; /* 日時,時間隔 */ …(3) EXEC SQL END DECLARE SECTION; void main() { memset(SQLSTATE, 0x00, 6); …(4) memset(SQLMSG, 0x00, 256); …(4) memset(&str, 0x00, sizeof(str)); …(4) memset(varname, 0x00, 39); …(4) memset(varchar, 0x00, 39); …(4) memset(ocdata, 0x00, 15); …(4) memset(oncdata, 0x00, 21); …(4) memset(&oddata, 0x00, sizeof(oddata)); …(4) EXEC SQL CONNECT TO DEFAULT; …(5) EXEC SQL WHENEVER SQLERROR GOTO :ERR; …(6) printf( "実行する動的SELECT文を指定してください \n" ); gets( str.sqlvar ); …(7) str.sqllen = strlen( str.sqlvar ); EXEC SQL DECLARE CUR1 CURSOR FOR STMID; …(8) EXEC SQL PREPARE STMID FROM :str; …(9) EXEC SQL ALLOCATE DESCRIPTOR 'OUTDESC' WITH MAX 100; …(10) EXEC SQL DESCRIBE OUTPUT STMID USING SQL DESCRIPTOR 'OUTDESC'; …(11) /**************/ /* 選択リスト */ /**************/ EXEC SQL GET DESCRIPTOR 'OUTDESC' :varocount = COUNT; …(12) for( i = 1; i <= varocount; i++ ) { EXEC SQL GET DESCRIPTOR 'OUTDESC' VALUE :i …(13) :vartype = TYPE, …(13) :varleng = LENGTH, …(13) :varolen = OCTET_LENGTH, …(13) :varprec = PRECISION, …(13) :varscal = SCALE, …(13) :varchar = CHARACTER_SET_NAME, …(13) :vardcod = DATETIME_INTERVAL_CODE, …(13) :vardpre = DATETIME_INTERVAL_PRECISION;…(13) /* CHAR型かNCHAR型 */ …(14) if( vartype == 1 || vartype == 12 ) { …(14) /* 文字列型 */ …(14) if( strncmp( varchar, "BASIC", 5 ) == 0 ) { …(14) EXEC SQL SET DESCRIPTOR 'OUTDESC' VALUE :i …(14) TYPE = 1, …(14) LENGTH = 14, …(14) OCTET_LENGTH = 14, …(14) CHARACTER_SET_NAME = 'BASIC'; …(14) } …(14) /* 各国語文字列型 */ …(14) else { …(14) EXEC SQL SET DESCRIPTOR 'OUTDESC' VALUE :i …(14) TYPE = 1, …(14) LENGTH = 10, …(14) OCTET_LENGTH = 20, …(14) CHARACTER_SET_NAME = 'NCHAR'; …(14) } …(14) } …(14) /* INTEGER型かSMALLINT型 */ …(14) else if( vartype == 4 || vartype == 5 ) { …(14) EXEC SQL SET DESCRIPTOR 'OUTDESC' VALUE :i …(14) TYPE = 4, …(14) PRECISION = 31, …(14) SCALE = 0; …(14) } …(14) /* NUMERIC型かDECIMAL型かREAL型かDOUBLE PRECISION型 */ …(14) else if( vartype == 2 || vartype == 3 || …(14) vartype == 7 || vartype == 8 ) { …(14) EXEC SQL SET DESCRIPTOR 'OUTDESC' VALUE :i …(14) TYPE = 8, …(14) PRECISION = 52, …(14) SCALE = 0; …(14) } …(14) /* 日時型かINTERVAL型 */ …(14) else if( vartype == 9 || vartype == 10 ) { …(14) EXEC SQL SET DESCRIPTOR 'OUTDESC' VALUE :i …(14) TYPE = 12, …(14) LENGTH = 19, …(14) OCTET_LENGTH = 19, …(14) CHARACTER_SET_NAME = 'BASIC'; …(14) } …(14) } EXEC SQL OPEN CUR1; …(15) EXEC SQL WHENEVER NOT FOUND GOTO :NOTFND; …(16) for(;;) { EXEC SQL FETCH CUR1 INTO SQL DESCRIPTOR 'OUTDESC'; …(17) /************/ /* 実行結果 */ /************/ for( i = 1; i <= varocount; i++ ) { EXEC SQL GET DESCRIPTOR 'OUTDESC' VALUE :i …(18) :vartype = TYPE, …(18) :varchar = CHARACTER_SET_NAME; …(18) /* CHAR型かNCHAR型 */ …(18) if( vartype == 1 ) { …(18) /* 文字列型 */ …(18) if( strncmp( varchar, "BASIC", 5 ) == 0 ) { …(18) EXEC SQL GET DESCRIPTOR 'OUTDESC' VALUE :i …(18) :varname = NAME, …(18) :ocdata = DATA; …(18) } …(18) /* 各国語文字列型 */ …(18) else { …(18) EXEC SQL GET DESCRIPTOR 'OUTDESC' VALUE :i …(18) :varname = NAME, …(18) :oncdata = DATA; …(18) } …(18) } …(18) /* 真数型 */ …(18) else if( vartype == 4 ) { …(18) EXEC SQL GET DESCRIPTOR 'OUTDESC' VALUE :i …(18) :varname = NAME, …(18) :oidata = DATA; …(18) } …(18) /* 概数型 */ …(18) else if( vartype == 8 ) { …(18) EXEC SQL GET DESCRIPTOR 'OUTDESC' VALUE :i …(18) :varname = NAME, …(18) :ofdata = DATA; …(18) } …(18) /* 日時型かINTERVAL型 */ …(18) else if( vartype == 12 ) { …(18) EXEC SQL GET DESCRIPTOR 'OUTDESC' VALUE :i …(18) :varname = NAME, …(18) :oddata = DATA; …(18) oddata.sqlvar[oddata.sqllen] = \0; …(18) printf( "%s -> %s \n", varname, oddata.sqlvar ); …(18) } …(18) } } NOTFND: EXEC SQL CLOSE CUR1; …(19) EXEC SQL DEALLOCATE DESCRIPTOR 'OUTDESC'; …(20) EXEC SQL DEALLOCATE PREPARE STMID; …(21) EXEC SQL COMMIT WORK; …(22) EXEC SQL DISCONNECT DEFAULT; …(23) return; ERR: EXEC SQL WHENEVER SQLERROR CONTINUE; printf( "SQLERROR SQLSTATE = %s SQLMSG = %s\n", SQLSTATE, SQLMSG ); EXEC SQL ROLLBACK WORK; EXEC SQL DISCONNECT DEFAULT; return; }
(1) SQL文変数はSQL文を格納する領域です。可変長文字列は、コンパイル時に以下の構造体に展開されます。
struct { short sqllen; /* 長さ領域の変数 */ char sqlvar[1024]; /* 文字列領域の変数 */ }str;
(2) SQL記述子域
(3) 相手指定
(4) ホスト変数を初期化します。
(5) コネクションを接続します。
(6) SQL文の実行でデータなし以外のエラーが生じた場合にERRへ進み、エラー情報を出力してプログラムは終了します。
(7) 端末から入力された動的SELECT文を読み込み、strに格納します。
(8) カーソルCUR1を宣言します。カーソル指定としてSQL文識別子STMIDを指定します。
(9) strのSQL文を実行できるようにするための準備処理を行います。
(10) 相手指定のためのSQL記述子域OUTDESCを割り当てます。
(11) 入力した動的SELECT文の選択リストの情報をSQL記述子域OUTDESCに取り込みます。
(12) OUTDESCに設定されている選択リストの個数(COUNTの値)を取得します。
(13) OUTDESCに設定されているそれぞれの選択リストの情報(TYPE、LENGTH、OCTET_LENGTH、PRECISION、SCALE、CHARACTER_SET_NAMEの値)を取得します。
(14) OUTDESCに実行結果を取り出す相手指定のデータ型を設定します。
CHARACTER(n)、CHARACTER VARYING(n)をCHARACTER(14)に変更します。(nは文字列の長さ)
NATIONAL CHARACTER(n)、NATIONAL CHARACTER VARYING(n)をNATIONAL CHARACTER(10)に変更します。(nは文字列の長さ)
INTEGER、SMALLINTをINTEGERに変更します。
NUMERIC(p,q)、DECIMAL(p,q)、REAL、DOUBLE PRECISIONをDOUBLE PRECISIONに変更します。(pは精度、qは位取り)
DATE、TIME、TIMESTAMPおよびINTERVALをCHARACTER VARYING(19)に変更します。(nは文字列の長さ)
(15) カーソルCUR1をオープンします。
(16) 検索の結果データが見つからない場合にNOTFNDに進み、プログラムは終了します。
(17) カーソルCUR1の位置づけおよびデータの読込みを行います。このとき、OUTDESCのDATAに実行結果の値が設定されます。
(18) SQL記述子域OUTDESCからNAMEとDATAの値を取得し、それぞれ相手指定のデータ型に一致したホスト変数に格納します。
(19) カーソルCUR1をクローズします。
(20) SQL記述子域OUTDESCを解放します。
(21) SQL文識別子STMIDに対応する被準備文を解放します。
(22) 現行のトランザクションを終了します。
(23) コネクションを切断します。
1行のデータの取り出しを行う場合には、動的単一行SELECT文を準備して実行します。動的単一行SELECT文を実行するには、USING句を指定したEXECUTE文を使用します。SQL記述子域を使用して、動的単一行SELECT文の指定例を以下に示します。なお、SQL記述子域を操作するSQL文の詳細については、“図4.2 被準備文の実行結果の取り出し手順”の例を参照してください。
端末から入力した動的単一行SELECT文の例です。
:
[端末から入力した動的単一行SELECT文をstrに格納]
:
EXEC SQL PREPARE STMID FROM :str;
EXEC SQL ALLOCATE DESCRIPTOR 'OUTDESC' WITH MAX 100;
EXEC SQL DESCRIBE OUTPUT STMID USING SQL DESCRIPTOR 'OUTDESC';
EXEC SQL GET DESCRIPTOR 'OUTDESC' :ocount = COUNT;
for( i = 1; i <= ocount; i++) {
EXEC SQL GET DESCRIPTOR 'OUTDESC' VALUE :i …
:
[選択リストの情報をSQL記述子域から取得]
:
EXEC SQL SET DESCRIPTOR 'OUTDESC' VALUE :i …
:
[SQL記述子域の選択リストの情報を変更]
:
}
EXEC SQL EXECUTE STMID INTO SQL DESCRIPTOR 'OUTDESC';
EXEC SQL GET DESCRIPTOR 'OUTDESC' VALUE :i …
:
[実行結果の値の取得]
:
EXEC SQL DEALLOCATE DESCRIPTOR 'OUTDESC';
EXEC SQL DEALLOCATE PREPARE STMID;
: