使用 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,而且預存程序不會傳回任何結果集 (之後您將會在此主題中找到更複雜的案例):
針對每個參數,呼叫 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 資料類型是 SQL_APD_TYPE,否則,使用 SQLGetData 指定的 C 資料類型會覆寫 SQLBindParameter 中指定的 C 資料類型。
雖然串流輸出參數在輸出參數的資料類型為 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,警告與結果集會與串流輸出參數分開處理。 處理串流輸出參數之前,請先處理警告與結果集。
下表描述傳送至伺服器之單一命令的不同案例,以及應用程式應該如何運作。
案例 | 從 SQLExecute 或 SQLExecDirect 傳回值 | 後續步驟 |
---|---|---|
資料只包含串流輸出參數 | SQL_PARAM_DATA_AVAILABLE | 使用 SQLParamData 與 SQLGetData 擷取串流輸出參數。 |
資料包含結果集與串流輸出參數 | SQL_SUCCESS | 使用 SQLBindCol 與 SQLGetData 擷取結果集。 呼叫 SQLMoreResults 以開始處理串流輸出參數。 其應該會傳回 SQL_PARAM_DATA_AVAILABLE。 使用 SQLParamData 與 SQLGetData 擷取串流輸出參數。 |
資料包含警告訊息與串流輸出參數 | SQL_SUCCESS_WITH_INFO | 使用 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 (含 Option = 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;
}