使用 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 應用程式可以使用此功能,而且這些應用程式必須有 ODBC 3.8 驅動程式,此驅動程式必須支援使用 SQLGetData 和 ODBC 3.8 驅動程式管理員擷取輸出參數。 如需如何讓舊版應用程式使用新 ODBC 功能的相關資訊,請參閱相容性矩陣 (部分機器翻譯)。

使用範例

例如,請考慮執行預存程序 {CALL sp_f(?,?)},其中兩個參數都會繫結為 SQL_PARAM_OUTPUT_STREAM,而且預存程序不會傳回任何結果集 (之後您將會在此主題中找到更複雜的案例):

  1. 針對每個參數,呼叫 SQLBindParameter,其中 InputOutputType 設定為 SQL_PARAM_OUTPUT_STREAM,且 ParameterValuePtr 設定為權杖,例如參數編號、資料的指標,或應用程式用來繫結輸入參數之結構的指標。 此範例將使用參數序數作為權杖。

  2. 使用 SQLExecDirectSQLExecute 執行查詢。 系統會傳回 SQL_PARAM_DATA_AVAILABLE,指出有串流輸出參數可供擷取。

  3. 呼叫 SQLParamData 以取得可供擷取的參數。 SQLParamData 將以第一個可用參數的權杖傳回 SQL_PARAM_DATA_AVAILABLE,此權杖是在 SQLBindParameter 中設定的 (步驟 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 輸入參數,SQLBindParameterParameterValuePtr 引數所使用的權杖 (步驟 1) 可以是指向應用程式資料結構的指標,其中包含參數的序數以及更多應用程式專用資訊 (如有需要)。

傳回之串流輸出或輸入/輸出參數的順序是驅動程式專用的,而且不一定與查詢中指定的順序相同。

如果應用程式未在步驟 4 中呼叫 SQLGetData,則會捨棄參數值。 同樣地,如果應用程式在 SQLGetData 讀取所有參數值之前呼叫 SQLParamData,則會捨棄其餘的值,而且應用程式可以處理下一個參數。

如果應用程式在處理所有串流輸出參數之前呼叫 SQLMoreResults (SQLParamData 仍會傳回 SQL_PARAM_DATA_AVAILABLE),則會捨棄所有剩餘的參數。 同樣地,如果應用程式在 SQLGetData 讀取所有參數值之前呼叫 SQLMoreResults,則會捨棄該值的其餘部分與所有剩餘的參數,而且應用程式可以繼續處理下一個參數集。

請注意,應用程式可以在 SQLBindParameterSQLGetData 中指定 C 資料類型。 除非在 SQLGetData 中指定的 C 資料類型是 SQL_APD_TYPE,否則,使用 SQLGetData 指定的 C 資料類型會覆寫 SQLBindParameter 中指定的 C 資料類型。

雖然串流輸出參數在輸出參數的資料類型為 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,警告與結果集會與串流輸出參數分開處理。 處理串流輸出參數之前,請先處理警告與結果集。

下表描述傳送至伺服器之單一命令的不同案例,以及應用程式應該如何運作。

案例 從 SQLExecute 或 SQLExecDirect 傳回值 後續步驟
資料只包含串流輸出參數 SQL_PARAM_DATA_AVAILABLE 使用 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 輸入參數 (例如串流輸入/輸出 (DAE) 參數) 查詢 SQL NEED_DATA 呼叫 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 (含 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;  
}  

另請參閱

陳述式參數