在 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,且儲存程序不會回傳任何結果集(在本主題後面你會看到更複雜的情境):
對於每個參數,呼叫 SQLBindParameter,並將 InputOutputType 設為 SQL_PARAM_OUTPUT_STREAM,ParameterValuePtr 設為標記,例如參數編號、資料指標,或應用程式用來綁定輸入參數的結構指標。 此範例將使用參數序數作為標記。
使用 SQLExecDirect 或 SQLExecute 執行查詢。 SQL_PARAM_DATA_AVAILABLE會回傳,表示有串流輸出參數可供檢索。
呼叫 SQLParamData 以取得可取的參數。 SQLParamData 會回傳SQL_PARAM_DATA_AVAILABLE第一個可用參數的標記,該標記在 SQLBindParameter (步驟 1)中設定。 該代幣會被回傳到 ValuePtrPtr 所指向的緩衝區。
呼叫 SQLGetData ,並將參數 Col_or_Param_Num 設為參數序數,以取得第一個可用參數的資料。 若 SQLGetData 回傳 SQL_SUCCESS_WITH_INFO 與 SQLState 01004(資料截斷),且客戶端與伺服器的型別皆為可變長度,則可從第一個可用參數取得更多資料。 你可以繼續呼叫 SQLGetData,直到它回傳 SQL_SUCCESS,或與不同的 SQLState 一起回傳 SQL_SUCCESS_WITH_INFO。
重複步驟 3 和 4 以取得目前參數。
再呼叫 一次 SQLParamData 。 如果回傳除了 SQL_PARAM_DATA_AVAILABLE 以外的任何內容,就不會再有串流參數資料需要擷取,回傳碼將是下一個執行語句的回傳碼。
呼叫 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,該值的剩餘部分會被丟棄,應用程式即可處理下一個參數。
如果應用程式在所有串流輸出參數尚未處理前呼叫 SQLMoreResults (SQLParamData 仍會回傳 SQL_PARAM_DATA_AVAILABLE),則所有剩餘參數都會被丟棄。 同樣地,若應用程式在 SQLGetData 讀取完所有參數值前呼叫 SQLMoreResults,該值的其餘部分及所有參數都會被丟棄,應用程式可繼續處理下一個參數集。
請注意,應用程式可以在 SQLBindParameter 和 SQLGetData 中指定 C 資料型別。 用 SQLGetData 指定的 C 資料型別會覆蓋 SQLBindParameter 中指定的 C 資料型別,除非 SQLGetData 中指定的 C 資料型別為 SQL_APD_TYPE。
雖然當輸出參數的資料型態為 BLOB 時,串流輸出參數更為實用,但此功能也可用於任何資料型態。 串流輸出參數所支援的資料型態由驅動程式規範。
若有SQL_PARAM_INPUT_OUTPUT_STREAM參數需處理, SQLExecute 或 SQLExecDirect 會先回傳SQL_NEED_DATA。 應用程式可以呼叫 SQLParamData 和 SQLPutData 來傳送 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 | 使用 SQLParamData 和 SQLGetData 來取得串流輸出參數。 |
| 資料包含結果集及串流輸出參數 | SQL_SUCCESS | 用 SQLBindCol 和 SQLGetData 取得結果集。 呼叫 SQLMoreResults 以開始處理串流的輸出參數。 應該會返回SQL_PARAM_DATA_AVAILABLE。 使用 SQLParamData 和 SQLGetData 來取得串流輸出參數。 |
| 資料包含警告訊息及串流輸出參數 | SQL_SUCCESS_WITH_INFO(SQL操作成功但帶有信息) | 使用 SQLGetDiagRec 和 SQLGetDiagField 來處理警告訊息。 呼叫 SQLMoreResults 以開始處理串流的輸出參數。 應該返回SQL_PARAM_DATA_AVAILABLE。 使用 SQLParamData 和 SQLGetData 來取得串流輸出參數。 |
| 資料包含警告訊息、結果集及串流輸出參數 | SQL_SUCCESS_WITH_INFO | 使用 SQLGetDiagRec 和 SQLGetDiagField 來處理警告訊息。 接著呼叫 SQLMoreResults 開始處理結果集。 用 SQLBindCol 和 SQLGetData 取得結果集。 呼叫 SQLMoreResults 以開始處理串流的輸出參數。 SQLMoreResults 應該會回傳 SQL_PARAM_DATA_AVAILABLE。 使用 SQLParamData 和 SQLGetData 來取得串流輸出參數。 |
| 查詢使用 DAE 輸入參數,例如串流輸入/輸出(DAE)參數 | SQL NEED_DATA | 呼叫 SQLParamData 和 SQLPutData 來傳送 DAE 的輸入參數資料。 在處理完所有 DAE 輸入參數後, SQLParamData 可以回傳 SQLExecute 和 SQLExecDirect 能回傳的任意回傳碼。 此表中的案例可被套用。 若回傳碼為SQL_PARAM_DATA_AVAILABLE,則可使用串流輸出參數。 應用程式必須再次呼叫 SQLParamData ,才能取得串流輸出參數的權杖,詳見本表第一列所述。 如果回傳碼是SQL_SUCCESS,表示有結果集待處理,或是處理完成。 如果退回代碼為SQL_SUCCESS_WITH_INFO,則有警告訊息需要處理。 |
在 SQLExecute、 SQLExecDirect 或 SQLMoreResults 回傳 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
應用程式仍可使用 SQLSetDescField 或 SQLSetDescRec 來設定綁定資訊。 場域映射不會被更改。 然而,描述符內的欄位可能會回傳新的值。 例如,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;
}