Click here to view and discuss this page in DocCommentXchange. In the future, you will be sent there automatically.

SQL Anywhere 11.0.1 (中文) » SQL Anywhere 服务器 - 编程 » SQL Anywhere 数据访问 API » SQL Anywhere 嵌入式 SQL » 读取数据

 

一次读取多个行

可以将 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 值来重新定位游标,以便读取行。