使用 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 取得輸出參數。 輸入資料必須以執行時資料(data-at-run, 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. 對於每個參數,呼叫 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 輸入參數,SQLBindParameter 中參數 ParameterValuePtr(步驟 1)中使用的標記可指向應用程式資料結構,該結構包含參數的序數及更多應用專用資訊(如有需要)。

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

若應用程式在第 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 使用 SQLParamDataSQLGetData 來取得串流輸出參數。
資料包含結果集及串流輸出參數 SQL_SUCCESS SQLBindColSQLGetData 取得結果集。

呼叫 SQLMoreResults 以開始處理串流的輸出參數。 應該會返回SQL_PARAM_DATA_AVAILABLE。

使用 SQLParamDataSQLGetData 來取得串流輸出參數。
資料包含警告訊息及串流輸出參數 SQL_SUCCESS_WITH_INFO(SQL操作成功但帶有信息) 使用 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 (選項 = 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;  
}  

另請參閱

陳述參數