FETCH 文は一度に複数のローをフェッチするように変更できます。こうするとパフォーマンスが向上することがあります。これを「ワイド・フェッチ」または「配列フェッチ」といいます。
SQL Anywhere は、ワイド・プットとワイド挿入もサポートします。PUT 文 [ESQL]とEXECUTE 文 [ESQL]を参照してください。
Embedded SQL でワイド・フェッチを使用するには、コードに次のような FETCH 文を含めます。
EXEC SQL FETCH ... ARRAY nnn |
ARRAY nnn は FETCH 文の最後の項目です。フェッチ回数を示す nnn にはホスト変数も使用できます。SQLDA 内の変数の数はローあたりのカラム数と nnn との積にしてください。最初のローは SQLDA の変数 0 から (ロー当たりのカラム数) - 1 に入り、以後のローも同様です。
各カラムは、SQLDA の各ローと同じ型にしてください。型が同じでない場合、SQLDA_INCONSISTENT エラーが返されます。
サーバはフェッチしたレコード数を SQLCOUNT に返します。この値は、エラーまたは警告がないかぎり、常に正の数です。ワイド・フェッチでは、エラーではなくて SQLCOUNT が 1 の場合、有効なローが 1 つフェッチされたことを示します。
次は、ワイド・フェッチの使用例です。このコードは samples-dir\SQLAnywhere\esqlwidefetch\widefetch.sqc にもあります。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "sqldef.h" EXEC SQL INCLUDE SQLCA; EXEC SQL WHENEVER SQLERROR { PrintSQLError(); goto err; }; static void PrintSQLError() { char buffer[200]; printf( "SQL error %d -- %s\n", SQLCODE, sqlerror_message( &sqlca, buffer, sizeof( buffer ) ) ); } static SQLDA * PrepareSQLDA( a_sql_statement_number stat0, unsigned width, unsigned *cols_per_row ) /* Allocate a SQLDA to be used for fetching from the statement identified by "stat0". "width" rows are retrieved on each FETCH request. The number of columns per row is assigned to "cols_per_row". */ { int num_cols; unsigned row, col, offset; SQLDA * sqlda; EXEC SQL BEGIN DECLARE SECTION; a_sql_statement_number stat; EXEC SQL END DECLARE SECTION; stat = stat0; sqlda = alloc_sqlda( 100 ); if( sqlda == NULL ) return( NULL ); EXEC SQL DESCRIBE :stat INTO sqlda; *cols_per_row = num_cols = sqlda->sqld; if( num_cols * width > sqlda->sqln ) { free_sqlda( sqlda ); sqlda = alloc_sqlda( num_cols * width ); if( sqlda == NULL ) return( NULL ); EXEC SQL DESCRIBE :stat INTO sqlda; } // copy first row in SQLDA setup by describe // to following (wide) rows sqlda->sqld = num_cols * width; offset = num_cols; for( row = 1; row < width; row++ ) { for( col = 0; col < num_cols; col++, offset++ ) { sqlda->sqlvar[offset].sqltype = sqlda->sqlvar[col].sqltype; sqlda->sqlvar[offset].sqllen = sqlda->sqlvar[col].sqllen; // optional: copy described column name memcpy( &sqlda->sqlvar[offset].sqlname, &sqlda->sqlvar[col].sqlname, sizeof( sqlda->sqlvar[0].sqlname ) ); } } fill_s_sqlda( sqlda, 40 ); return( sqlda ); err: return( NULL ); } static void PrintFetchedRows( SQLDA * sqlda, unsigned cols_per_row ) { /* Print rows already wide fetched in the SQLDA */ long rows_fetched; int row, col, offset; if( SQLCOUNT == 0 ) { rows_fetched = 1; } else { rows_fetched = SQLCOUNT; } printf( "Fetched %d Rows:\n", rows_fetched ); for( row = 0; row < rows_fetched; row++ ) { for( col = 0; col < cols_per_row; col++ ) { offset = row * cols_per_row + col; printf( " \"%s\"", (char *)sqlda->sqlvar[offset].sqldata ); } printf( "\n" ); } } static int DoQuery( char * query_str0, unsigned fetch_width0 ) { /* Wide Fetch "query_str0" select statement * using a width of "fetch_width0" rows" */ SQLDA * sqlda; unsigned cols_per_row; EXEC SQL BEGIN DECLARE SECTION; a_sql_statement_number stat; char * query_str; unsigned fetch_width; EXEC SQL END DECLARE SECTION; query_str = query_str0; fetch_width = fetch_width0; EXEC SQL PREPARE :stat FROM :query_str; EXEC SQL DECLARE QCURSOR CURSOR FOR :stat FOR READ ONLY; EXEC SQL OPEN QCURSOR; sqlda = PrepareSQLDA( stat, fetch_width, &cols_per_row ); if( sqlda == NULL ) { printf( "Error allocating SQLDA\n" ); return( SQLE_NO_MEMORY ); } for( ;; ) { EXEC SQL FETCH QCURSOR INTO DESCRIPTOR sqlda ARRAY :fetch_width; if( SQLCODE != SQLE_NOERROR ) break; PrintFetchedRows( sqlda, cols_per_row ); } EXEC SQL CLOSE QCURSOR; EXEC SQL DROP STATEMENT :stat; free_filled_sqlda( sqlda ); err: return( SQLCODE ); } void main( int argc, char *argv[] ) { /* Optional first argument is a select statement, * optional second argument is the fetch width */ char *query_str = "SELECT GivenName, Surname FROM Employees"; unsigned fetch_width = 10; if( argc > 1 ) { query_str = argv[1]; if( argc > 2 ) { fetch_width = atoi( argv[2] ); if( fetch_width < 2 ) { fetch_width = 2; } } } db_init( &sqlca ); EXEC SQL CONNECT "DBA" IDENTIFIED BY "sql"; DoQuery( query_str, fetch_width ); EXEC SQL DISCONNECT; err: db_fini( &sqlca ); } |
PrepareSQLDA 関数では、alloc_sqlda 関数を使用して SQLDA 用のメモリを割り付けています。この関数では、alloc_sqlda_noind 関数とは違って、インジケータ変数用の領域が確保できます。
フェッチされたローの数が要求より少ないが 0 ではない場合 (たとえばカーソルの終端に達したとき)、SQLDA のフェッチされなかったローに対応する項目は、インジケータ変数に値を設定して、NULL として返されます。インジケータ変数が指定されていない場合は、エラーが発生します (SQLE_NO_INDICATOR: NULL の結果に対してインジケータ変数がありません)。
フェッチしようとしたローが更新され、警告 (SQLE_ROW_UPDATED_WARNING) が出された場合、フェッチは警告を引き起こしたロー上で停止します。そのときまでに処理されたすべてのロー (警告を起こしたローも含む) の値が返されます。SQLCOUNT には、フェッチしたローの数 (警告を引き起こしたローも含む) が入ります。残りの SQLDA の項目はすべて NULL になります。
フェッチしようとしたローが削除またはロックされ、エラー (SQLE_NO_CURRENT_ROW または SQLE_LOCKED) が発生した場合、SQLCOUNT にはエラー発生までに読み込まれたローの数が入ります。この値にはエラーを起こしたローは含みません。SQLDA にはローの値は入りません。エラーの時は、SQLDA に値が返らないためです。SQLCOUNT の値は、ローを読み込む必要がある場合、カーソルの再位置付けに使用できます。
Copyright © 2009, iAnywhere Solutions, Inc. - SQL Anywhere 11.0.1 |