使用 SQLGetData 检索输出参数

在 ODBC 3.8 之前,应用程序只能检索具有绑定输出缓冲区的查询的输出参数。 但是,当参数值的大小非常大(例如,大型图像)时,很难分配非常大的缓冲区。 ODBC 3.8 引入了检索部件中的输出参数的新方法。 应用程序现在可以多次调用具有小型缓冲区的 SQLGetData 来检索大型参数值。 这类似于检索大型列数据。

若要在部件中绑定要检索的输出参数或输入/输出参数,请调用 SQLBindParameter ,并将 InputOutputType 参数设置为SQL_PARAM_OUTPUT_STREAM或SQL_PARAM_INPUT_OUTPUT_STREAM。 使用 SQL_PARAM_INPUT_OUTPUT_STREAM,应用程序可以使用 SQLPutData 将数据输入参数,然后使用 SQLGetData 检索输出参数。 输入数据必须是以执行时数据 (DAE) 格式呈现,使用 SQLPutData 而不是绑定到预先分配的缓冲区。

此功能可由 ODBC 3.8 应用程序或重新编译的 ODBC 3.x 和 ODBC 2.x 应用程序使用,并且这些应用程序必须具有支持使用 SQLGetData 和 ODBC 3.8 驱动程序管理器检索输出参数的 ODBC 3.8 驱动程序。 有关如何使旧应用程序能够使用新的 ODBC 功能的信息,请参阅 兼容性矩阵

用法示例

例如,请考虑执行存储过程 {CALL sp_f(?,?)},其中两个参数都绑定为SQL_PARAM_OUTPUT_STREAM,并且存储过程不返回结果集(本主题稍后将找到更复杂的方案):

  1. 对于每个参数,调用将 InputOutputType 设置为 SQL_PARAM_OUTPUT_STREAM 的 SQLBindParameter,并将 ParameterValuePtr 设置为标记,例如参数编号、指向数据的指针或指向应用程序用来绑定输入参数的结构的指针。 此示例将使用参数序号作为标记。

  2. 使用 SQLExecDirectSQLExecute 执行查询。 将返回SQL_PARAM_DATA_AVAILABLE,指示有可用于检索的流式输出参数。

  3. 调用 SQLParamData 以获取可用于检索的参数。 SQLParamData 将使用第一个可用参数的令牌(在 SQLBindParameter 中设置)返回SQL_PARAM_DATA_AVAILABLE(步骤 1)。 令牌在 ValuePtrPtr 指向的缓冲区中返回。

  4. 调用 SQLGetData,将参数 Col_or_Param_Num 设置为参数序号,以检索第一个可用参数的数据。 如果 SQLGetData 返回 SQL_SUCCESS_WITH_INFO 且 SQLState 01004(数据截断),并且该类型在客户端和服务器上都是可变长度,则需要从第一个可用参数中检索更多数据。 可以继续调用 SQLGetData,直到它返回 SQL_SUCCESS 或返回带有不同 SQLState 的 SQL_SUCCESS_WITH_INFO。

  5. 重复步骤 3 和步骤 4 以检索当前参数。

  6. 再次调用 SQLParamData 。 如果它返回除SQL_PARAM_DATA_AVAILABLE之外的任何内容,则不再有要检索的流式处理参数数据,并且返回代码将是执行下一个语句的返回代码。

  7. 调用 SQLMoreResults 以处理下一组参数,直到返回SQL_NO_DATA。 如果语句属性SQL_ATTR_PARAMSET_SIZE设置为 1,则 SQLMoreResults 将返回此示例中的SQL_NO_DATA。 否则, SQLMoreResults 将返回SQL_PARAM_DATA_AVAILABLE,以指示有可用于下一组参数检索的流式输出参数。

与 DAE 输入参数类似,SQLBindParameter(步骤 1)参数 ParameterValuePtr 中使用的令牌可以是指向应用程序数据结构的指针,该结构包含参数序号和更多特定于应用程序的信息(如有必要)。

返回的流式输出或输入/输出参数的顺序特定于驱动程序,并且可能并不总是与查询中指定的顺序相同。

如果应用程序未在步骤 4 中调用 SQLGetData ,则放弃参数值。 同样,如果应用程序在 SQLGetData 读取所有参数值之前调用 SQLParamData,则放弃值的其余部分,并且应用程序可以处理下一个参数。

如果应用程序在处理所有流式输出参数之前调用 SQLMoreResultsSQLParamData 仍然返回SQL_PARAM_DATA_AVAILABLE),则丢弃所有剩余的参数。 同样,如果应用程序在 SQLGetData 读取所有参数值之前调用 SQLMoreResults,则放弃该值的其余部分和所有剩余参数,并且应用程序可以继续处理下一个参数集。

请注意,应用程序可以在 SQLBindParameterSQLGetData 中指定 C 数据类型。 使用 SQLGetData 指定的 C 数据类型将替代 SQLBindParameter 中指定的 C 数据类型,除非 SQLGetData 中指定的 C 数据类型SQL_APD_TYPE。

尽管当输出参数的数据类型为 BLOB 时,流式输出参数更有用,但此功能也可用于任何数据类型。 驱动程序中指定了流式输出参数支持的数据类型。

如果要处理SQL_PARAM_INPUT_OUTPUT_STREAM参数, 则 SQLExecuteSQLExecDirect 将首先返回SQL_NEED_DATA。 应用程序可以调用 SQLParamDataSQLPutData 来发送 DAE 参数数据。 处理所有 DAE 输入参数时, SQLParamData 返回SQL_PARAM_DATA_AVAILABLE以指示流式输出参数可用。

当有要处理的流式输出参数和绑定的输出参数时,驱动程序将确定处理输出参数的顺序。 因此,如果输出参数绑定到缓冲区( SQLBindParameter 参数 InputOutputType 设置为 SQL_PARAM_INPUT_OUTPUT 或 SQL_PARAM_OUTPUT),则在 SQLParamData 返回SQL_SUCCESS或SQL_SUCCESS_WITH_INFO之前,可能无法填充缓冲区。 只有在 SQLParamData 返回SQL_SUCCESS或SQL_SUCCESS_WITH_INFO处理所有流式输出参数之后,应用程序才应读取绑定缓冲区。

除了流式输出参数外,数据源还可以返回警告和结果集。 通常,警告和结果集通过调用 SQLMoreResults 与流式输出参数分开处理。 在处理流式输出参数之前处理警告和结果集。

下表描述了发送到服务器的单个命令的不同方案,以及应用程序应如何工作。

Scenario 从 SQLExecute 或 SQLExecDirect 返回值 下一步作
数据仅包括流式输出参数 SQL_PARAM_DATA_AVAILABLE (SQL 参数数据可用) 使用 SQLParamDataSQLGetData 检索流式传输的输出参数。
数据包括结果集和流式输出参数 SQL_SUCCESS 使用 SQLBindColSQLGetData 检索结果集。

调用 SQLMoreResults 开始处理流式处理输出参数。 它应返回SQL_PARAM_DATA_AVAILABLE。

使用 SQLParamDataSQLGetData 检索流式传输的输出参数。
数据包括警告消息和流式输出参数 SQL_SUCCESS_WITH_INFO 使用 SQLGetDiagRecSQLGetDiagField 处理警告消息。

调用 SQLMoreResults 开始处理流式处理输出参数。 它应返回SQL_PARAM_DATA_AVAILABLE。

使用 SQLParamDataSQLGetData 检索流式传输的输出参数。
数据包括警告消息、结果集和流式输出参数 SQL_SUCCESS_WITH_INFO 使用 SQLGetDiagRecSQLGetDiagField 处理警告消息。 然后调用 SQLMoreResults 开始处理结果集。

使用 SQLBindColSQLGetData 检索结果集。

调用 SQLMoreResults 开始处理流式处理输出参数。 SQLMoreResults 应返回SQL_PARAM_DATA_AVAILABLE。

使用 SQLParamDataSQLGetData 检索流式传输的输出参数。
使用 DAE 输入参数进行查询,例如流式输入/输出参数。 SQL NEED_DATA(SQL需要数据) 调用 SQLParamDataSQLPutData 以发送 DAE 输入参数数据。

处理所有 DAE 输入参数后, SQLParamData 可以返回 SQLExecuteSQLExecDirect 可以返回的任何返回代码。 然后,可以应用此表中的事例。

如果返回代码SQL_PARAM_DATA_AVAILABLE,则流式输出参数可用。 应用程序必须再次调用 SQLParamData,才能检索流式传输的输出参数的令牌,如该表第一行所述。

如果返回代码SQL_SUCCESS,则存在要处理的结果集或处理完成。

如果返回代码SQL_SUCCESS_WITH_INFO,则存在要处理的警告消息。

SQLExecuteSQLExecDirectSQLMoreResults 返回SQL_PARAM_DATA_AVAILABLE后,如果应用程序调用不在以下列表中的函数,则函数序列错误将产生:

  • SQLAllocHandle / SQLAllocHandleStd

  • SQLDataSources / SQLDrivers

  • SQLGetInfo / SQLGetFunctions

  • SQLGetConnectAttr / SQLGetEnvAttr / SQLGetDescField / SQLGetDescRec

  • SQLNumParams

  • SQLDescribeParam

  • SQLNativeSql

  • SQLParamData

  • SQLMoreResults

  • SQLGetDiagField / SQLGetDiagRec

  • SQLCancel

  • SQLCancelHandle (带句柄)

  • SQLFreeStmt (使用 Option = SQL_CLOSE、SQL_DROP 或 SQL_UNBIND)

  • SQLCloseCursor

  • SQLDisconnect

  • SQLFreeHandle (with HandleType = SQL_HANDLE_STMT)

  • SQLGetStmtAttr

应用程序仍然可以使用 SQLSetDescFieldSQLSetDescRec 设置绑定信息。 字段映射不会更改。 但是,描述符内的字段可能会返回新值。 例如,SQL_DESC_PARAMETER_TYPE 可能返回 SQL_PARAM_INPUT_OUTPUT_STREAM 或 SQL_PARAM_OUTPUT_STREAM。

使用场景:从结果集中检索分部图像

当存储过程返回一个包含图像元数据的结果集的行,并通过大型输出参数返回图像时,SQLGetData 可用于分段获取数据。

// CREATE PROCEDURE SP_TestOutputPara  
//      @ID_of_picture   as int,  
//      @Picture         as varbinary(max) out  
// AS  
//     output the image data through streamed output parameter  
// GO  
BOOL displayPicture(SQLUINTEGER idOfPicture, SQLHSTMT hstmt) {  
   SQLLEN      lengthOfPicture;    // The actual length of the picture.  
   BYTE        smallBuffer[100];   // A very small buffer.  
   SQLRETURN   retcode, retcode2;  
  
   // Bind the first parameter (input parameter)  
   SQLBindParameter(  
         hstmt,  
         1,                         // The first parameter.   
         SQL_PARAM_INPUT,           // Input parameter: The ID_of_picture.  
         SQL_C_ULONG,               // The C Data Type.  
         SQL_INTEGER,               // The SQL Data Type.  
         0,                         // ColumnSize is ignored for integer.  
         0,                         // DecimalDigits is ignored for integer.  
         &idOfPicture,              // The Address of the buffer for the input parameter.  
         0,                         // BufferLength is ignored for integer.  
         NULL);                     // This is ignored for integer.  
  
   // Bind the streamed output parameter.  
   SQLBindParameter(  
         hstmt,   
         2,                         // The second parameter.  
         SQL_PARAM_OUTPUT_STREAM,   // A streamed output parameter.   
         SQL_C_BINARY,              // The C Data Type.    
         SQL_VARBINARY,             // The SQL Data Type.  
         0,                         // ColumnSize: The maximum size of varbinary(max).  
         0,                         // DecimalDigits is ignored for binary type.  
         (SQLPOINTER)2,             // ParameterValuePtr: An application-defined  
                                    // token (this will be returned from SQLParamData).  
                                    // In this example, we used the ordinal   
                                    // of the parameter.  
         0,                         // BufferLength is ignored for streamed output parameters.  
         &lengthOfPicture);         // StrLen_or_IndPtr: The status variable returned.   
  
   retcode = SQLPrepare(hstmt, L"{call SP_TestOutputPara(?, ?)}", SQL_NTS);  
   if ( retcode == SQL_ERROR )  
      return FALSE;  
  
   retcode = SQLExecute(hstmt);  
   if ( retcode == SQL_ERROR )  
      return FALSE;  
  
   // Assume that the retrieved picture exists.  Use SQLBindCol or SQLGetData to retrieve the result-set.  
  
   // Process the result set and move to the streamed output parameters.  
   retcode = SQLMoreResults( hstmt );  
  
   // SQLGetData retrieves and displays the picture in parts.  
   // The streamed output parameter is available.  
   while (retcode == SQL_PARAM_DATA_AVAILABLE) {  
      SQLPOINTER token;   // Output by SQLParamData.  
      SQLLEN cbLeft;      // #bytes remained  
      retcode = SQLParamData(hstmt, &token);   // returned token is 2 (according to the binding)  
      if ( retcode == SQL_PARAM_DATA_AVAILABLE ) {  
         // A do-while loop retrieves the picture in parts.  
         do {  
            retcode2 = SQLGetData(   
               hstmt,   
               (UWORD) token,          // the value of the token is the ordinal.   
               SQL_C_BINARY,           // The C-type.  
               smallBuffer,            // A small buffer.   
               sizeof(smallBuffer),    // The size of the buffer.  
               &cbLeft);               // How much data we can get.  
         }  
         while ( retcode2 == SQL_SUCCESS_WITH_INFO );  
      }  
   }  
  
   return TRUE;  
}  

使用方案:以流式输入/输出参数的形式发送和接收大型对象

当存储过程将大型对象作为输入/输出参数传递时,SQLGetData 可用于获取和发送部分中的数据,并将值流式传输到数据库和从数据库发送。 无需在内存中存储所有数据。

// CREATE PROCEDURE SP_TestInOut  
//       @picture as varbinary(max) out  
// AS  
//    output the image data through output parameter   
// go  
  
BOOL displaySimilarPicture(BYTE* image, ULONG lengthOfImage, SQLHSTMT hstmt) {  
   BYTE smallBuffer[100];   // A very small buffer.  
   SQLRETURN retcode, retcode2;  
   SQLLEN statusOfPicture;  
  
   // First bind the parameters, before preparing the statement that binds the output streamed parameter.  
   SQLBindParameter(  
      hstmt,   
      1,                                 // The first parameter.  
      SQL_PARAM_INPUT_OUTPUT_STREAM,     // I/O-streamed parameter: The Picture.  
      SQL_C_BINARY,                      // The C Data Type.  
      SQL_VARBINARY,                     // The SQL Data Type.  
      0,                                 // ColumnSize: The maximum size of varbinary(max).  
      0,                                 // DecimalDigits is ignored.   
      (SQLPOINTER)1,                     // An application defined token.   
      0,                                 // BufferLength is ignored for streamed I/O parameters.  
      &statusOfPicture);                 // The status variable.  
  
   statusOfPicture = SQL_DATA_AT_EXEC;   // Input data in parts (DAE parameter at input).  
  
   retcode = SQLPrepare(hstmt, L"{call SP_TestInOut(?) }", SQL_NTS);  
   if ( retcode == SQL_ERROR )  
      return FALSE;  
  
   // Execute the statement.  
   retcode = SQLExecute(hstmt);  
   if ( retcode == SQL_ERROR )  
      return FALSE;  
  
   if ( retcode == SQL_NEED_DATA ) {  
      // Use SQLParamData to loop through DAE input parameters.  For  
      // each, use SQLPutData to send the data to database in parts.  
  
      // This example uses an I/O parameter with streamed output.  
      // Therefore, the last call to SQLParamData should return  
      // SQL_PARAM_DATA_AVAILABLE to indicate the end of the input phrase   
      // and report that a streamed output parameter is available.  
  
      // Assume retcode is set to the return value of the last call to  
      // SQLParamData, which is equal to SQL_PARAM_DATA_AVAILABLE.  
   }  
  
   // Start processing the streamed output parameters.  
   while ( retcode == SQL_PARAM_DATA_AVAILABLE ) {  
      SQLPOINTER token;   // Output by SQLParamData.  
      SQLLEN cbLeft;     // #bytes remained  
      retcode = SQLParamData(hstmt, &token);  
      if ( retcode == SQL_PARAM_DATA_AVAILABLE ) {  
         do {  
            retcode2 = SQLGetData(   
               hstmt,   
               (UWORD) token,          // the value of the token is the ordinal.   
               SQL_C_BINARY,           // The C-type.  
               smallBuffer,            // A small buffer.   
               sizeof(smallBuffer),    // The size of the buffer.  
               &cbLeft);               // How much data we can get.  
         }  
         while ( retcode2 == SQL_SUCCESS_WITH_INFO );  
      }  
   }   
  
   return TRUE;  
}  

另请参阅

语句参数