可以将 FETCH 语句修改为一次读取多行,这样可能会改善性能。这种方式称为宽读取或数组读取。
SQL Anywhere 还支持宽放置和宽插入。请参见PUT 语句 [ESQL]和EXECUTE 语句 [ESQL]。
要在嵌入式 SQL 中使用宽读取,请将 fetch 语句包括在代码中,如下所示:
EXEC SQL FETCH ... ARRAY nnn |
其中 ARRAY nnn 是 FETCH 语句的最后一项。读取计数 nnn 可以是一个主机变量。SQLDA 中的变量数必须是 nnn 和每行的列数的乘积。第一行放在 SQLDA 变量 0 和(每行的列数)-1 之间,依此类推。
SQLDA 的每一行中的每一列的类型必须相同,否则会返回 SQLDA_INCONSISTENT 错误。
服务器在 SQLCOUNT 中返回读取的记录数,除非有错误或警告,否则该记录数始终大于零。在宽读取时,在没有错误的情况下,SQLCOUNT 为一 (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 中,SQLDA 内存是使用 alloc_sqlda 函数分配的。这样就为指示符变量留出了空间,而不用使用 alloc_sqlda_noind 函数。
如果读取的行数小于请求的行数,但又不是零(例如在游标的末尾),则通过设置指示符值可将对应于未读取的行的 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 |