Поделиться через


Получение выходных параметров с помощью 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, а хранимая процедура не возвращает результирующий набор (далее в этом разделе вы найдете более сложный сценарий):

  1. Для каждого параметра вызовите SQLBindParameter с параметром InputOutputType , равным SQL_PARAM_OUTPUT_STREAM, а параметрValuePtr — маркеру, например номеру параметра, указателю на данные или указателю на структуру, которую приложение использует для привязки входных параметров. В этом примере в качестве токена используется порядковый номер параметра.

  2. Выполните запрос с помощью SQLExecDirect или SQLExecute. 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 или SQL_SUCCESS_WITH_INFO, но с измененным SQLState.

  5. Повторите шаг 3 и шаг 4, чтобы получить текущий параметр.

  6. Снова вызовите SQLParamData . Если он возвращает что-либо, кроме SQL_PARAM_DATA_AVAILABLE, больше нет данных параметров потока для извлечения, а возвращаемый код будет кодом возврата следующей выполняемой инструкции.

  7. Вызовите SQLMoreResults для обработки следующего набора параметров, пока 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 для указания доступных потоковых выходных параметров.

При потоковой обработке выходных параметров и привязанных выходных параметров драйвер определяет порядок обработки выходных параметров. Таким образом, если выходной параметр привязан к буферу (параметр InputOutputTypeSQLBindParameter имеет значение 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 Используйте 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;  
}  

См. также

Параметры инструкции