Abrufen von Ausgabeparametern mithilfe von SQLGetData
Vor ODBC 3.8 konnte eine Anwendung nur die Ausgabeparameter einer Abfrage mit einem gebundenen Ausgabepuffer abrufen. Es ist jedoch schwierig, einen sehr großen Puffer zuzuordnen, wenn die Größe des Parameterwerts sehr groß ist (z. B. ein großes Bild). ODBC 3.8 bietet eine neue Möglichkeit zum Abrufen von Ausgabeparametern in Teilen. Eine Anwendung kann jetzt SQLGetData mit einem kleinen Puffer mehrmals aufrufen, um einen großen Parameterwert abzurufen. Dies ähnelt dem Abrufen großer Spaltendaten.
Um einen Ausgabeparameter oder einen Eingabe-/Ausgabeparameter zu binden, der in Teilen abgerufen werden soll, rufen Sie SQLBindParameter auf, wobei das InputOutputType-Argument auf SQL_PARAM_OUTPUT_STREAM oder SQL_PARAM_INPUT_OUTPUT_STREAM festgelegt ist. Mit SQL_PARAM_INPUT_OUTPUT_STREAM kann eine Anwendung SQLPutData verwenden, um Daten in den Parameter einzugeben, und dann SQLGetData zum Abrufen des Ausgabeparameters verwenden. Die Eingabedaten müssen im DAE-Formular (Data-at-Execution) vorliegen und SQLPutData verwenden, anstatt sie an einen vorab zugewiesenen Puffer zu binden.
Dieses Feature kann von ODBC 3.8-Anwendungen oder neu kompilierten ODBC 3.x- und ODBC 2.x-Anwendungen verwendet werden. Diese Anwendungen müssen über einen ODBC 3.8-Treiber verfügen, der das Abrufen von Ausgabeparametern mithilfe von SQLGetData und ODBC 3.8 Driver Manager unterstützt. Informationen dazu, wie Sie einer älteren Anwendung die Verwendung neuer ODBC-Features ermöglichen, finden Sie unter Kompatibilitätsmatrix.
Verwendungsbeispiel
Erwägen Sie beispielsweise die Ausführung einer gespeicherten Prozedur, {CALL sp_f(?,?)}, bei der beide Parameter als SQL_PARAM_OUTPUT_STREAM gebunden sind und die gespeicherte Prozedur kein Resultset zurückgibt (weiter unten in diesem Thema finden Sie ein komplexeres Szenario):
Rufen Sie für jeden Parameter SQLBindParameter auf, wobei InputOutputType auf SQL_PARAM_OUTPUT_STREAM und ParameterValuePtr auf ein Token festgelegt ist, z. B. eine Parameternummer, einen Zeiger auf Daten oder einen Zeiger auf eine Struktur, die die Anwendung zum Binden von Eingabeparametern verwendet. In diesem Beispiel wird die Ordnungszahl des Parameters als Token verwendet.
Führen Sie die Abfrage mit SQLExecDirect oder SQLExecute aus. SQL_PARAM_DATA_AVAILABLE werden zurückgegeben, was angibt, dass gestreamte Ausgabeparameter für den Abruf verfügbar sind.
Rufen Sie SQLParamData auf, um den Parameter abzurufen, der zum Abruf verfügbar ist. SQLParamData gibt SQL_PARAM_DATA_AVAILABLE mit dem Token des ersten verfügbaren Parameters zurück, der in SQLBindParameter (Schritt 1) festgelegt ist. Das Token wird im Puffer zurückgegeben, auf den valuePtrPtrPtr verweist.
Rufen Sie SQLGetData auf, wobei das Argument Col_or_Param_Num auf den Parameter ordinal festgelegt ist, um die Daten des ersten verfügbaren Parameters abzurufen. Wenn SQLGetData SQL_SUCCESS_WITH_INFO und SQLState 01004 (Daten abgeschnitten) zurückgibt und der Typ sowohl auf dem Client als auch auf dem Server variable Länge aufweist, müssen weitere Daten aus dem ersten verfügbaren Parameter abgerufen werden. Sie können SQLGetData weiterhin aufrufen, bis es SQL_SUCCESS oder SQL_SUCCESS_WITH_INFO mit einem anderen SQLState zurückgibt.
Wiederholen Sie Schritt 3 und Schritt 4, um den aktuellen Parameter abzurufen.
Rufen Sie SQLParamData erneut auf. Wenn es etwas außer SQL_PARAM_DATA_AVAILABLE zurückgibt, sind keine gestreamten Parameterdaten mehr abzurufen, und der Rückgabecode ist der Rückgabecode der nächsten Anweisung, die ausgeführt wird.
Rufen Sie SQLMoreResults auf , um den nächsten Satz von Parametern zu verarbeiten, bis er SQL_NO_DATA zurückgibt. SQLMoreResults gibt in diesem Beispiel SQL_NO_DATA zurück, wenn das Anweisungsattribut SQL_ATTR_PARAMSET_SIZE auf 1 festgelegt wurde. Andernfalls gibt SQLMoreResults SQL_PARAM_DATA_AVAILABLE zurück, um anzugeben, dass gestreamte Ausgabeparameter für den nächsten Abzurufenden Parametersatz verfügbar sind.
Ähnlich wie ein DAE-Eingabeparameter kann das token, das im Argument ParameterValuePtr in SQLBindParameter (Schritt 1) verwendet wird, ein Zeiger sein, der auf eine Anwendungsdatenstruktur verweist, die die Ordnungszahl des Parameters und ggf. weitere anwendungsspezifische Informationen enthält.
Die Reihenfolge der zurückgegebenen gestreamten Ausgabe- oder Eingabe-/Ausgabeparameter ist treiberspezifisch und ist möglicherweise nicht immer mit der in der Abfrage angegebenen Reihenfolge identisch.
Wenn die Anwendung SQLGetData in Schritt 4 nicht aufruft, wird der Parameterwert verworfen. Wenn die Anwendung SQLParamData aufruft, bevor der gesamte Parameterwert von SQLGetData gelesen wurde, wird der Rest des Werts verworfen, und die Anwendung kann den nächsten Parameter verarbeiten.
Wenn die Anwendung SQLMoreResults aufruft , bevor alle gestreamten Ausgabeparameter verarbeitet werden (SQLParamData gibt weiterhin SQL_PARAM_DATA_AVAILABLE zurück), werden alle verbleibenden Parameter verworfen. Wenn die Anwendung SQLMoreResults aufruft , bevor der gesamte Parameterwert von SQLGetData gelesen wurde, werden der Rest des Werts und alle verbleibenden Parameter verworfen, und die Anwendung kann weiterhin den nächsten Parametersatz verarbeiten.
Beachten Sie, dass eine Anwendung den C-Datentyp sowohl in SQLBindParameter als auch in SQLGetData angeben kann. Der mit SQLGetData angegebene C-Datentyp setzt den in SQLBindParameter angegebenen C-Datentyp außer Kraft, es sei denn, der in SQLGetData angegebene C-Datentyp ist SQL_APD_TYPE.
Obwohl ein gestreamter Ausgabeparameter nützlicher ist, wenn der Datentyp des Ausgabeparameters vom Typ BLOB ist, kann diese Funktionalität auch mit jedem Datentyp verwendet werden. Die von gestreamten Ausgabeparametern unterstützten Datentypen werden im Treiber angegeben.
Wenn SQL_PARAM_INPUT_OUTPUT_STREAM Zu verarbeitende Parameter vorhanden sind, gibt SQLExecute oder SQLExecDirect zuerst SQL_NEED_DATA zurück. Eine Anwendung kann SQLParamData und SQLPutData aufrufen, um DAE-Parameterdaten zu senden. Wenn alle DAE-Eingabeparameter verarbeitet werden, gibt SQLParamData SQL_PARAM_DATA_AVAILABLE zurück, um anzugeben, dass gestreamte Ausgabeparameter verfügbar sind.
Wenn gestreamte Ausgabeparameter und gebundene Ausgabeparameter verarbeitet werden sollen, bestimmt der Treiber die Reihenfolge für die Verarbeitung von Ausgabeparametern. Wenn also ein Ausgabeparameter an einen Puffer gebunden ist (der SQLBindParameter-ParameterInputOutputType ist auf SQL_PARAM_INPUT_OUTPUT oder SQL_PARAM_OUTPUT festgelegt), wird der Puffer möglicherweise erst aufgefüllt , wenn SQLParamData SQL_SUCCESS oder SQL_SUCCESS_WITH_INFO zurückgibt. Eine Anwendung sollte einen gebundenen Puffer erst lesen, nachdem SQLParamData SQL_SUCCESS oder SQL_SUCCESS_WITH_INFO zurückgibt, nachdem alle gestreamten Ausgabeparameter verarbeitet wurden.
Die Datenquelle kann zusätzlich zum gestreamten Ausgabeparameter eine Warnung und ein Resultset zurückgeben. Im Allgemeinen werden Warnungen und Resultsets getrennt von einem gestreamten Ausgabeparameter verarbeitet, indem SQLMoreResults aufgerufen wird. Prozesswarnungen und das Resultset vor der Verarbeitung des gestreamten Ausgabeparameters.
In der folgenden Tabelle werden verschiedene Szenarien eines einzelnen Befehls beschrieben, der an den Server gesendet wird, und wie die Anwendung funktionieren soll.
Szenario | Rückgabewert von SQLExecute oder SQLExecDirect | Nächste Schritte |
---|---|---|
Daten enthalten nur gestreamte Ausgabeparameter | SQL_PARAM_DATA_AVAILABLE | Verwenden Sie SQLParamData und SQLGetData , um gestreamte Ausgabeparameter abzurufen. |
Daten enthalten ein Resultset und gestreamte Ausgabeparameter | SQL_SUCCESS | Rufen Sie das Resultset mit SQLBindCol und SQLGetData ab. Rufen Sie SQLMoreResults auf, um mit der Verarbeitung gestreamter Ausgabeparameter zu beginnen. Es sollte SQL_PARAM_DATA_AVAILABLE zurückgegeben werden. Verwenden Sie SQLParamData und SQLGetData , um gestreamte Ausgabeparameter abzurufen. |
Daten enthalten eine Warnmeldung und gestreamte Ausgabeparameter | SQL_SUCCESS_WITH_INFO | Verwenden Sie SQLGetDiagRec und SQLGetDiagField , um Warnmeldungen zu verarbeiten. Rufen Sie SQLMoreResults auf, um mit der Verarbeitung gestreamter Ausgabeparameter zu beginnen. Es sollte SQL_PARAM_DATA_AVAILABLE zurückgegeben werden. Verwenden Sie SQLParamData und SQLGetData , um gestreamte Ausgabeparameter abzurufen. |
Daten enthalten eine Warnmeldung, ein Resultset und gestreamte Ausgabeparameter. | SQL_SUCCESS_WITH_INFO | Verwenden Sie SQLGetDiagRec und SQLGetDiagField , um Warnmeldungen zu verarbeiten. Rufen Sie dann SQLMoreResults auf, um mit der Verarbeitung des Resultsets zu beginnen. Rufen Sie ein Resultset mit SQLBindCol und SQLGetData ab. Rufen Sie SQLMoreResults auf, um mit der Verarbeitung gestreamter Ausgabeparameter zu beginnen. SQLMoreResults sollte SQL_PARAM_DATA_AVAILABLE zurückgeben. Verwenden Sie SQLParamData und SQLGetData , um gestreamte Ausgabeparameter abzurufen. |
Abfragen mit DAE-Eingabeparametern, z. B. einem gestreamten Eingabe-/Ausgabeparameter (DAE) | SQL NEED_DATA | Rufen Sie SQLParamData und SQLPutData auf, um DAE-Eingabeparameterdaten zu senden. Nachdem alle DAE-Eingabeparameter verarbeitet wurden, kann SQLParamData jeden Rückgabecode zurückgeben, den SQLExecute und SQLExecDirect zurückgeben können. Die Fälle in dieser Tabelle können dann angewendet werden. Wenn der Rückgabecode SQL_PARAM_DATA_AVAILABLE ist, sind gestreamte Ausgabeparameter verfügbar. Eine Anwendung muss SQLParamData erneut aufrufen, um das Token für den gestreamten Ausgabeparameter abzurufen, wie in der ersten Zeile dieser Tabelle beschrieben. Wenn der Rückgabecode SQL_SUCCESS ist, muss entweder ein Resultset verarbeitet werden, oder die Verarbeitung ist abgeschlossen. Wenn der Rückgabecode SQL_SUCCESS_WITH_INFO ist, müssen Warnmeldungen verarbeitet werden. |
Nachdem SQLExecute, SQLExecDirect oder SQLMoreResults SQL_PARAM_DATA_AVAILABLE zurückgegeben hat, tritt ein Funktionssequenzfehler auf, wenn eine Anwendung eine Funktion aufruft, die nicht in der folgenden Liste enthalten ist:
SQLAllocHandle / SQLAllocHandleStd
SQLDataSources / SQLDrivers
Sqlgetinfo / SQLGetFunctions
SQLGetConnectAttr / SQLGetEnvAttr / Sqlgetdescfield / Sqlgetdescrec
SQLNumParams
SQLDescribeParam
SQLNativeSql
SQLParamData
SQLMoreResults
SQLGetDiagField / Sqlgetdiagrec
SQLCancel
SQLCancelHandle (mit Anweisungshandle)
SQLFreeStmt (mit Option = SQL_CLOSE, SQL_DROP oder SQL_UNBIND)
SQLCloseCursor
SQLDisconnect
SQLFreeHandle (mit HandleType = SQL_HANDLE_STMT)
'SQLGetStmtAttr'
Anwendungen können weiterhin SQLSetDescField oder SQLSetDescRec verwenden, um die Bindungsinformationen festzulegen. Die Feldzuordnung wird nicht geändert. Felder innerhalb des Deskriptors geben jedoch möglicherweise neue Werte zurück. Beispielsweise können SQL_DESC_PARAMETER_TYPE SQL_PARAM_INPUT_OUTPUT_STREAM oder SQL_PARAM_OUTPUT_STREAM zurückgeben.
Verwendungsszenario: Abrufen eines Bilds in Teilen aus einem Resultset
SQLGetData kann verwendet werden, um Daten in Teilen abzurufen, wenn eine gespeicherte Prozedur ein Resultset zurückgibt, das eine Zeile mit Metadaten zu einem Bild enthält und das Bild in einem großen Ausgabeparameter zurückgegeben wird.
// 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;
}
Verwendungsszenario: Senden und Empfangen eines großen Objekts als gestreamter Eingabe-/Ausgabeparameter
SQLGetData kann verwendet werden, um Daten in Teilen abzurufen und zu senden, wenn eine gespeicherte Prozedur ein großes Objekt als Eingabe-/Ausgabeparameter übergibt und den Wert an und aus der Datenbank streamt. Sie müssen nicht alle Daten im Arbeitsspeicher speichern.
// 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;
}