Mengambil Parameter Output Menggunakan SQLGetData
Sebelum ODBC 3.8, aplikasi hanya dapat mengambil parameter output kueri dengan buffer output terikat. Namun, sulit untuk mengalokasikan buffer yang sangat besar ketika ukuran nilai parameter sangat besar (misalnya, gambar besar). ODBC 3.8 memperkenalkan cara baru untuk mengambil parameter output di beberapa bagian. Aplikasi sekarang dapat memanggil SQLGetData dengan buffer kecil beberapa kali untuk mengambil nilai parameter besar. Ini mirip dengan mengambil data kolom besar.
Untuk mengikat parameter output atau parameter input/output yang akan diambil dalam bagian, panggil SQLBindParameter dengan argumen InputOutputType yang diatur ke SQL_PARAM_OUTPUT_STREAM atau SQL_PARAM_INPUT_OUTPUT_STREAM. Dengan SQL_PARAM_INPUT_OUTPUT_STREAM, aplikasi dapat menggunakan SQLPutData untuk memasukkan data ke dalam parameter, lalu menggunakan SQLGetData untuk mengambil parameter output. Data input harus dalam formulir data-at-execution (DAE), menggunakan SQLPutData alih-alih mengikatnya ke buffer yang telah dialokasikan sebelumnya.
Fitur ini dapat digunakan oleh aplikasi ODBC 3.8 atau aplikasi ODBC 3.x dan ODBC 2.x yang dikompilasi ulang, dan aplikasi ini harus memiliki driver ODBC 3.8 yang mendukung pengambilan parameter output menggunakan SQLGetData dan ODBC 3.8 Driver Manager. Untuk informasi tentang cara mengaktifkan aplikasi lama untuk menggunakan fitur ODBC baru, lihat Matriks Kompatibilitas.
Contoh Penggunaan
Misalnya, pertimbangkan untuk menjalankan prosedur tersimpan, {CALL sp_f(?,?)}, di mana kedua parameter terikat sebagai SQL_PARAM_OUTPUT_STREAM, dan prosedur tersimpan tidak mengembalikan tataan hasil (nanti dalam topik ini Anda akan menemukan skenario yang lebih kompleks):
Untuk setiap parameter, panggil SQLBindParameter dengan InputOutputType diatur ke SQL_PARAM_OUTPUT_STREAM dan ParameterValuePtr diatur ke token, seperti nomor parameter, penunjuk ke data, atau penunjuk ke struktur yang digunakan aplikasi untuk mengikat parameter input. Contoh ini akan menggunakan parameter ordinal sebagai token.
Jalankan kueri dengan SQLExecDirect atau SQLExecute. SQL_PARAM_DATA_AVAILABLE akan dikembalikan, menunjukkan bahwa ada parameter output yang dialirkan yang tersedia untuk pengambilan.
Panggil SQLParamData untuk mendapatkan parameter yang tersedia untuk diambil. SQLParamData akan mengembalikan SQL_PARAM_DATA_AVAILABLE dengan token parameter pertama yang tersedia, yang diatur dalam SQLBindParameter (langkah 1). Token dikembalikan dalam buffer yang dituju valuePtrPtr .
Panggil SQLGetData dengan argumen Col_or_Param_Num diatur ke ordinal parameter untuk mengambil data parameter pertama yang tersedia. Jika SQLGetData mengembalikan SQL_SUCCESS_WITH_INFO dan SQLState 01004 (data terpotong), dan jenisnya adalah panjang variabel pada klien dan server, ada lebih banyak data untuk diambil dari parameter pertama yang tersedia. Anda dapat terus memanggil SQLGetData hingga kembali SQL_SUCCESS atau SQL_SUCCESS_WITH_INFO dengan SQLState yang berbeda.
Ulangi langkah 3 dan langkah 4 untuk mengambil parameter saat ini.
Hubungi SQLParamData lagi. Jika mengembalikan apa pun kecuali SQL_PARAM_DATA_AVAILABLE, tidak ada lagi data parameter yang dialirkan untuk diambil, dan kode pengembalian akan menjadi kode pengembalian dari pernyataan berikutnya yang dijalankan.
Panggil SQLMoreResults untuk memproses set parameter berikutnya hingga mengembalikan SQL_NO_DATA. SQLMoreResults akan mengembalikan SQL_NO_DATA dalam contoh ini jika atribut pernyataan SQL_ATTR_PARAMSET_SIZE diatur ke 1. Jika tidak, SQLMoreResults akan mengembalikan SQL_PARAM_DATA_AVAILABLE untuk menunjukkan bahwa ada parameter output yang dialirkan yang tersedia untuk kumpulan parameter berikutnya yang akan diambil.
Mirip dengan parameter input DAE, token yang digunakan dalam argumen ParameterValuePtr di SQLBindParameter (langkah 1) dapat menjadi penunjuk yang menunjuk ke struktur data aplikasi, yang berisi ordinal parameter dan informasi yang lebih spesifik aplikasi, jika perlu.
Urutan parameter output atau input/output yang dialirkan yang dikembalikan spesifik driver dan mungkin tidak selalu sama dengan urutan yang ditentukan dalam kueri.
Jika aplikasi tidak memanggil SQLGetData di langkah 4, nilai parameter akan dibuang. Demikian pula, jika aplikasi memanggil SQLParamData sebelum semua nilai parameter telah dibaca oleh SQLGetData, sisa nilai dibuang, dan aplikasi dapat memproses parameter berikutnya.
Jika aplikasi memanggil SQLMoreResults sebelum semua parameter output yang dialirkan diproses (SQLParamData masih mengembalikan SQL_PARAM_DATA_AVAILABLE), semua parameter yang tersisa akan dibuang. Demikian pula, jika aplikasi memanggil SQLMoreResults sebelum semua nilai parameter telah dibaca oleh SQLGetData, sisa nilai dan semua parameter yang tersisa dibuang, dan aplikasi dapat terus memproses set parameter berikutnya.
Perhatikan bahwa aplikasi dapat menentukan jenis data C di SQLBindParameter dan SQLGetData. Jenis data C yang ditentukan dengan SQLGetData mengambil alih jenis data C yang ditentukan dalam SQLBindParameter, kecuali jenis data C yang ditentukan dalam SQLGetData SQL_APD_TYPE.
Meskipun parameter output yang dialirkan lebih berguna ketika jenis data parameter output berjenis BLOB, fungsionalitas ini juga dapat digunakan dengan jenis data apa pun. Jenis data yang didukung oleh parameter output yang dialirkan ditentukan dalam driver.
Jika ada parameter SQL_PARAM_INPUT_OUTPUT_STREAM yang akan diproses, SQLExecute atau SQLExecDirect akan mengembalikan SQL_NEED_DATA terlebih dahulu. Aplikasi dapat memanggil SQLParamData dan SQLPutData untuk mengirim data parameter DAE. Ketika semua parameter input DAE diproses, SQLParamData mengembalikan SQL_PARAM_DATA_AVAILABLE untuk menunjukkan parameter output yang dialirkan tersedia.
Ketika ada parameter output yang dialirkan dan parameter output terikat yang akan diproses, driver menentukan urutan untuk memproses parameter output. Jadi, jika parameter output terikat ke buffer (parameter SQLBindParameter InputOutputType diatur ke SQL_PARAM_INPUT_OUTPUT atau SQL_PARAM_OUTPUT), buffer mungkin tidak diisi sampai SQLParamData kembali SQL_SUCCESS atau SQL_SUCCESS_WITH_INFO. Aplikasi harus membaca buffer terikat hanya setelah SQLParamData mengembalikan SQL_SUCCESS atau SQL_SUCCESS_WITH_INFO setelah semua parameter output yang dialirkan diproses.
Sumber data dapat mengembalikan peringatan dan tataan hasil, selain parameter output yang dialirkan. Secara umum, peringatan dan tataan hasil diproses secara terpisah dari parameter output yang dialirkan dengan memanggil SQLMoreResults. Peringatan proses dan tataan hasil sebelum memproses parameter output yang dialirkan.
Tabel berikut ini menjelaskan skenario yang berbeda dari satu perintah yang dikirim ke server, dan cara kerja aplikasi.
Skenario | Mengembalikan nilai dari SQLExecute atau SQLExecDirect | Apa yang harus dilakukan selanjutnya |
---|---|---|
Data hanya menyertakan parameter output yang dialirkan | SQL_PARAM_DATA_AVAILABLE | Gunakan SQLParamData dan SQLGetData untuk mengambil parameter output yang dialirkan. |
Data mencakup kumpulan hasil dan parameter output yang dialirkan | SQL_SUCCESS | Ambil tataan hasil dengan SQLBindCol dan SQLGetData. Panggil SQLMoreResults untuk mulai memproses parameter output yang dialirkan. Ini harus mengembalikan SQL_PARAM_DATA_AVAILABLE. Gunakan SQLParamData dan SQLGetData untuk mengambil parameter output yang dialirkan. |
Data mencakup pesan peringatan dan parameter output yang dialirkan | SQL_SUCCESS_WITH_INFO | Gunakan SQLGetDiagRec dan SQLGetDiagField untuk memproses pesan peringatan. Panggil SQLMoreResults untuk mulai memproses parameter output yang dialirkan. Ini harus mengembalikan SQL_PARAM_DATA_AVAILABLE. Gunakan SQLParamData dan SQLGetData untuk mengambil parameter output yang dialirkan. |
Data mencakup pesan peringatan, kumpulan hasil, dan parameter output yang dialirkan | SQL_SUCCESS_WITH_INFO | Gunakan SQLGetDiagRec dan SQLGetDiagField untuk memproses pesan peringatan. Kemudian panggil SQLMoreResults untuk mulai memproses kumpulan hasil. Ambil tataan hasil dengan SQLBindCol dan SQLGetData. Panggil SQLMoreResults untuk mulai memproses parameter output yang dialirkan. SQLMoreResults harus mengembalikan SQL_PARAM_DATA_AVAILABLE. Gunakan SQLParamData dan SQLGetData untuk mengambil parameter output yang dialirkan. |
Kueri dengan parameter input DAE, misalnya, parameter input/output (DAE) yang dialirkan | SQL NEED_DATA | Panggil SQLParamData dan SQLPutData untuk mengirim data parameter input DAE. Setelah semua parameter input DAE diproses, SQLParamData dapat mengembalikan kode pengembalian apa pun yang dapat dikembalikan oleh SQLExecute dan SQLExecDirect . Kasus dalam tabel ini kemudian dapat diterapkan. Jika kode pengembalian SQL_PARAM_DATA_AVAILABLE, parameter output yang dialirkan tersedia. Aplikasi harus memanggil SQLParamData lagi untuk mengambil token untuk parameter output yang dialirkan, seperti yang dijelaskan dalam baris pertama tabel ini. Jika kode pengembalian SQL_SUCCESS, ada hasil yang diatur untuk diproses atau pemrosesan selesai. Jika kode pengembalian SQL_SUCCESS_WITH_INFO, ada pesan peringatan untuk diproses. |
Setelah SQLExecute, SQLExecDirect, atau SQLMoreResults mengembalikan SQL_PARAM_DATA_AVAILABLE, kesalahan urutan fungsi akan mengakibatkan jika aplikasi memanggil fungsi yang tidak ada dalam daftar berikut:
SQLAllocHandle / SQLAllocHandleStd
SQLDataSources / SQLDrivers
SQLGetInfo / SQLGetFunctions
SQLGet Koneksi Attr SQLGetEnvAttr / SQLGetDescField / SQLGetDescRec /
SQLNumParams
SQLDescribeParam
SQLNativeSql
SQLParamData
SQLMoreResults
SQLGetDiagField / SQLGetDiagRec
SQLCancel
SQLCancelHandle (dengan handel pernyataan)
SQLFreeStmt (dengan Opsi = SQL_CLOSE, SQL_DROP, atau SQL_UNBIND)
SQLCloseCursor
SQLDisconnect
SQLFreeHandle (dengan HandleType = SQL_HANDLE_STMT)
SQLGetStmtAttr
Aplikasi masih dapat menggunakan SQLSetDescField atau SQLSetDescRec untuk mengatur informasi pengikatan. Pemetaan bidang tidak akan diubah. Namun, bidang di dalam deskriptor mungkin mengembalikan nilai baru. Misalnya, SQL_DESC_PARAMETER_TYPE mungkin mengembalikan SQL_PARAM_INPUT_OUTPUT_STREAM atau SQL_PARAM_OUTPUT_STREAM.
Skenario Penggunaan: Mengambil Gambar di Bagian dari Tataan Hasil
SQLGetData dapat digunakan untuk mendapatkan data di beberapa bagian saat prosedur tersimpan mengembalikan tataan hasil yang berisi satu baris metadata tentang gambar dan gambar dikembalikan dalam parameter output besar.
// 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;
}
Skenario Penggunaan: Mengirim dan Menerima Objek Besar sebagai Parameter Input/Output yang Dialirkan
SQLGetData dapat digunakan untuk mendapatkan dan mengirim data dalam beberapa bagian ketika prosedur tersimpan meneruskan objek besar sebagai parameter input/output, mengalirkan nilai ke dan dari database. Anda tidak perlu menyimpan semua data dalam memori.
// 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;
}