Получение выходных параметров с помощью метода 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 Driver Manager. Сведения о том, как включить более старое приложение для использования новых функций 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). Маркер возвращается в буфере, на который указывает значение ValuePtrPtrPtr .
ВызовИТЕ SQLGetData с аргументом Col_or_Param_Num в порядковый номер параметра, чтобы получить данные первого доступного параметра. Если SQLGetData возвращает SQL_SUCCESS_WITH_INFO и SQLState 01004 (усечение данных), а тип — переменная длина как на клиенте, так и на сервере, есть больше данных для получения из первого доступного параметра. Вы можете продолжать вызывать SQLGetData , пока он не возвращает SQL_SUCCESS или SQL_SUCCESS_WITH_INFO с другим SQLState.
Повторите шаг 3 и шаг 4, чтобы получить текущий параметр.
Снова вызовите SQLParamData . Если он возвращает что-либо, кроме SQL_PARAM_DATA_AVAILABLE, нет дополнительных потоковых данных параметров для извлечения, а возвращаемый код будет возвращаемым кодом следующей инструкции, выполняемой.
ВызовИТЕ SQLMoreResults для обработки следующего набора параметров, пока не будет возвращен SQL_NO_DATA. SQLMoreResults возвращает SQL_NO_DATA в этом примере, если для атрибута инструкции SQL_ATTR_PARAMSET_SIZE задано значение 1. В противном случае SQLMoreResults возвращает SQL_PARAM_DATA_AVAILABLE, чтобы указать, что потоковые выходные параметры доступны для следующего набора параметров для получения.
Как и входной параметр DAE, маркер, используемый в аргументе ParameterValuePtr в SQLBindParameter (шаг 1), может быть указателем, указывающим на структуру данных приложения, которая содержит порядковый номер параметра и дополнительные сведения о приложении при необходимости.
Порядок возвращаемых потоковых выходных данных или входных и выходных параметров является определенным драйвером и может не всегда совпадать с порядком, указанным в запросе.
Если приложение не вызывает SQLGetData на шаге 4, значение параметра не карта. Аналогичным образом, если приложение вызывает SQLParamData до того, как все значение параметра было прочитано SQLGetData, оставшаяся часть значения не карта, а приложение может обработать следующий параметр.
Если приложение вызывает SQLMoreResults до обработки всех потоковых выходных параметров (SQLParamData по-прежнему возвращает SQL_PARAM_DATA_AVAILABLE), все остальные параметры не карта. Аналогичным образом, если приложение вызывает SQLMoreResults до того, как все значение параметра было прочитано SQLGetData, оставшаяся часть значения и все остальные параметры не карта, и приложение может продолжать обрабатывать следующий набор параметров.
Обратите внимание, что приложение может указать тип данных C в SQLBindParameter и SQLGetData. Тип данных C, указанный в SQLGetData, переопределяет тип данных C, указанный в SQLBindParameter, если только тип данных C, указанный в SQLGetData, SQL_APD_TYPE.
Хотя потоковые выходные параметры более полезны, если тип данных выходного параметра имеет тип BLOB, эта функция также может использоваться с любым типом данных. Типы данных, поддерживаемые потоком выходных параметров, указываются в драйвере.
Если есть SQL_PARAM_INPUT_OUTPUT_STREAM параметры для обработки, SQLExecute или SQLExecDirect сначала вернет SQL_NEED_DATA. Приложение может вызывать SQLParamData и SQLPutData для отправки данных параметров DAE. При обработке всех входных параметров DAE SQLParamData возвращает SQL_PARAM_DATA_AVAILABLE для указания доступных потоковых выходных параметров.
При потоковой обработке выходных параметров и привязанных выходных параметров драйвер определяет порядок обработки выходных параметров. Таким образом, если выходной параметр привязан к буферу (параметр InputOutputType SQLBindParameterимеет значение 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
SQLGet Подключение Attr / 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;
}