Ví dụ này minh họa cách tiếp cận với việc sử dụng loại thu thập, được xác định trong cơ sở dữ liệu để chuyển danh sách tham số.
SYS.ODCINumberList
là loại bộ sưu tập tiêu chuẩn có sẵn cho tất cả người dùng. Truy vấn, được sử dụng trong mẫu chỉ cần chọn 100 số nguyên đầu tiên (test) và sau đó lọc số nguyên này bằng danh sách trong mệnh đề IN(...)
.
#include "stdafx.h"
#include <iostream>
#include <occi.h>
using namespace oracle::occi;
using namespace std;
// Vector type to pass as parameter list
typedef vector<Number> ValueList;
int _tmain(int argc, _TCHAR* argv[])
{
Environment *env;
Connection *con;
// Note that Environment must be initialized in OBJECT mode
// to use collection mapping features.
env = Environment::createEnvironment(Environment::OBJECT);
con = env->createConnection ("test_user", "test_password", "ORACLE_TNS_NAME");
try {
Statement *stmt = con->createStatement(
"select * from "
" (select level as col from dual connect by level <= 100)"
"where "
" col in (select column_value from table(:key_list))"
);
cout << endl << endl << "Executing the block :" << endl
<< stmt->getSQL() << endl << endl;
// Create instance of vector trype defined above
// and populate it with numbers.
ValueList value_list;
value_list.push_back(Number(10));
value_list.push_back(Number(20));
value_list.push_back(Number(30));
value_list.push_back(Number(40));
// Bind vector to parameter #1 in query and treat it as SYS.ODCINumberList type.
setVector(stmt, 1, value_list, "SYS", "ODCINUMBERLIST");
ResultSet *rs = stmt->executeQuery();
while(rs->next())
std::cout << "value: " << rs->getInt(1) << std::endl;
stmt->closeResultSet(rs);
con->terminateStatement (stmt);
} catch(SQLException ex) {
cout << ex.what();
}
env->terminateConnection (con);
Environment::terminateEnvironment (env);
return 0;
}
Bạn có thể sử dụng various ODCIxxxList types để chuyển danh sách số, ngày hoặc chuỗi tới Oracle qua OCI hoặc thậm chí xác định loại của riêng bạn trong DB.
Ví dụ được biên dịch bằng Visual Studio 10 Express và this version of OCI libraries. Được thử nghiệm đối với Oracle 11.2.0.3.0.
Cập nhật
Dưới đây là ví dụ ứng dụng mà làm điều tương tự nhưng với đồng bằng chức năng C OCIxxx.
//
// OCI collection parameters binding - example application
//
#include "stdafx.h"
#include <iostream>
#include <oci.h>
#include <oro.h>
using namespace std;
// connection parameters
const char *db_alias = "ORACLE_DB_ALIAS";
const char *db_user_name = "test_user";
const char *db_user_password = "test_password";
// helper error checking procedure to shorten main code, returns true if critical error detected
// and prints out error information
bool check_oci_error(char *error_point, OCIError *errhp, sword status, OCIEnv *envhp);
int _tmain(int argc, _TCHAR* argv[]) {
//----- CONNECTION INITIALIZATION PART ------------------------------------------------------
sword rc;
OCIEnv *myenvhp; /* the environment handle */
OCIServer *mysrvhp; /* the server handle */
OCIError *myerrhp; /* the error handle */
OCISession *myusrhp; /* user session handle */
OCISvcCtx *mysvchp; /* the service handle */
/* initialize the mode to be the threaded and object environment */
/* NOTE: OCI_OBJECT must be present to work with object/collection types */
rc = OCIEnvCreate(&myenvhp, OCI_THREADED|OCI_OBJECT, (dvoid *)0, 0, 0, 0, (size_t) 0, (dvoid **)0);
if(check_oci_error("OCIEnvCreate", NULL, rc, NULL)) {
return -1;
}
/* allocate a server handle */
rc = OCIHandleAlloc ((dvoid *)myenvhp, (dvoid **)&mysrvhp, OCI_HTYPE_SERVER, 0, (dvoid **) 0);
if(check_oci_error("OCIHandleAlloc(OCI_HTYPE_SERVER)", NULL, rc, myenvhp)) return -1;
/* allocate an error handle */
rc = OCIHandleAlloc ((dvoid *)myenvhp, (dvoid **)&myerrhp, OCI_HTYPE_ERROR, 0, (dvoid **) 0);
if(check_oci_error("OCIHandleAlloc(OCI_HTYPE_ERROR)", NULL, rc, myenvhp)) return -1;
/* create a server context */
rc = OCIServerAttach(mysrvhp, myerrhp, (text *)db_alias, strlen (db_alias), OCI_DEFAULT);
if(check_oci_error("OCIServerAttach()", myerrhp, rc, myenvhp)) return -1;
/* allocate a service handle */
rc = OCIHandleAlloc ((dvoid *)myenvhp, (dvoid **)&mysvchp, OCI_HTYPE_SVCCTX, 0, (dvoid **) 0);
if(check_oci_error("OCIHandleAlloc(OCI_HTYPE_SVCCTX)", myerrhp, rc, myenvhp)) return -1;
/* set the server attribute in the service context handle*/
rc = OCIAttrSet((dvoid *)mysvchp, OCI_HTYPE_SVCCTX, (dvoid *)mysrvhp, (ub4) 0, OCI_ATTR_SERVER, myerrhp);
if(check_oci_error("OCIAttrSet(OCI_HTYPE_SVCCTX,OCI_ATTR_SERVER)", myerrhp, rc, myenvhp)) return -1;
/* allocate a user session handle */
rc = OCIHandleAlloc((dvoid *)myenvhp, (dvoid **)&myusrhp, OCI_HTYPE_SESSION, 0, (dvoid **) 0);
if(check_oci_error("OCIHandleAlloc(OCI_HTYPE_SESSION)", myerrhp, rc, myenvhp)) return -1;
/* set user name attribute in user session handle */
rc = OCIAttrSet((dvoid *)myusrhp, OCI_HTYPE_SESSION, (dvoid *)db_user_name, strlen(db_user_name), OCI_ATTR_USERNAME, myerrhp);
if(check_oci_error("OCIAttrSet(OCI_HTYPE_SESSION,OCI_ATTR_USERNAME)", myerrhp, rc, myenvhp)) return -1;
/* set password attribute in user session handle */
rc = OCIAttrSet((dvoid *)myusrhp, OCI_HTYPE_SESSION, (dvoid *)db_user_password, strlen(db_user_password), OCI_ATTR_PASSWORD, myerrhp);
if(check_oci_error("OCIAttrSet(OCI_HTYPE_SESSION,OCI_ATTR_PASSWORD)", myerrhp, rc, myenvhp)) return -1;
rc = OCISessionBegin(mysvchp, myerrhp, myusrhp, OCI_CRED_RDBMS, OCI_DEFAULT);
if(check_oci_error("OCISessionBegin()", myerrhp, rc, myenvhp)) return -1;
/* set the user session attribute in the service context handle*/
rc = OCIAttrSet((dvoid *)mysvchp, OCI_HTYPE_SVCCTX, (dvoid *)myusrhp, (ub4) 0, OCI_ATTR_SESSION, myerrhp);
if(check_oci_error("OCIAttrSet(OCI_HTYPE_SVCCTX,OCI_ATTR_SESSION)", myerrhp, rc, myenvhp)) return -1;
cout << endl << "Initialization done." << endl;
//----- REGISTER TYPE INFORMATION ------------------------------------------------------
// This section can be invoked once per session to minimize server roundtrips.
char *type_owner_name = "SYS";
char *type_name = "ODCINUMBERLIST";
OCIType *type_tdo = NULL;
rc= OCITypeByName(
myenvhp, myerrhp, mysvchp,
(CONST text *)type_owner_name, strlen(type_owner_name),
(CONST text *) type_name, strlen(type_name),
NULL, 0,
OCI_DURATION_SESSION, OCI_TYPEGET_HEADER,
&type_tdo
);
if(check_oci_error("OCITypeByName()", myerrhp, rc, myenvhp)) return -1;
//----- PREPARE PARAMETER INSTANCE ---------------------------------------------
OCIArray *array_param = NULL;
rc = OCIObjectNew(
myenvhp, myerrhp, mysvchp,
OCI_TYPECODE_VARRAY,
type_tdo, NULL, OCI_DURATION_SESSION, TRUE,
(void**) &array_param
);
if(check_oci_error("OCITypeByName()", myerrhp, rc, myenvhp)) return -1;
//----- FILL PARAMETER ---------------------------------------------------------
OCINumber num_val;
int int_val;
for(int i = 1; i <= 3; i++) {
int_val = i*10;
rc = OCINumberFromInt(myerrhp, &int_val, sizeof(int_val), OCI_NUMBER_SIGNED, &num_val);
if(check_oci_error("OCINumberFromInt()", myerrhp, rc, myenvhp)) return -1;
rc = OCICollAppend(myenvhp, myerrhp, &num_val, NULL, array_param);
if(check_oci_error("OCICollAppend()", myerrhp, rc, myenvhp)) return -1;
}
//----- BIND PARAMETER VALUE AND EXECUTE STATEMENT ------------------------------
OCIStmt *mystmthp = NULL;
OCIDefine *col1defp = NULL;
double col1value;
OCIBind *bndp = NULL;
char *query_text = "select * from "
" (select level as col from dual connect by level < 100)"
"where "
" col in (select column_value from table(:key_list))";
rc = OCIHandleAlloc(myenvhp, (void **)&mystmthp, OCI_HTYPE_STMT, 0, NULL);
if(check_oci_error("OCIHandleAlloc(OCI_HTYPE_STMT)", myerrhp, rc, myenvhp)) return -1;
rc = OCIStmtPrepare(
mystmthp, myerrhp,
(const OraText *)query_text, strlen(query_text),
OCI_NTV_SYNTAX, OCI_DEFAULT
);
if(check_oci_error("OCIStmtPrepare()", myerrhp, rc, myenvhp)) return -1;
// result column
rc = OCIDefineByPos(mystmthp, &col1defp, myerrhp, 1, &col1value, sizeof(col1value), SQLT_BDOUBLE, NULL, NULL, NULL, OCI_DEFAULT);
if(check_oci_error("OCIDefineByPos()", myerrhp, rc, myenvhp)) return -1;
// parameter collection
rc = OCIBindByName(
mystmthp, &bndp, myerrhp,
(text *)":key_list", strlen(":key_list"),
NULL, 0,
SQLT_NTY, NULL, 0, 0, 0, 0,
OCI_DEFAULT
);
if(check_oci_error("OCIBindByName()", myerrhp, rc, myenvhp)) return -1;
rc = OCIBindObject(
bndp, myerrhp,
type_tdo, (dvoid **) &array_param,
NULL, NULL, NULL
);
if(check_oci_error("OCIBindByName()", myerrhp, rc, myenvhp)) return -1;
// execute and fetch
rc = OCIStmtExecute(mysvchp, mystmthp, myerrhp, 0, 0, NULL, NULL, OCI_DEFAULT);
if(check_oci_error("OCIBindByName()", myerrhp, rc, myenvhp)) return -1;
rc = OCIStmtFetch2(mystmthp, myerrhp, 1, OCI_FETCH_NEXT, 0, OCI_DEFAULT);
while(rc != OCI_NO_DATA) {
if(check_oci_error("OCIStmtFetch2()", myerrhp, rc, myenvhp)) return -1;
cout << "value: " << col1value << endl;
rc = OCIStmtFetch2(mystmthp, myerrhp, 1, OCI_FETCH_NEXT, 0, OCI_DEFAULT);
}
// free collection object parameter
rc = OCIObjectFree(myenvhp, myerrhp, array_param, OCI_OBJECTFREE_FORCE);
if(check_oci_error("OCIObjectFree()", myerrhp, rc, myenvhp)) return -1;
cout << endl << "Main test done." << endl;
//------- FINALIZATION -----------------------------------------------------------
rc= OCISessionEnd(mysvchp, myerrhp, myusrhp, OCI_DEFAULT);
if(check_oci_error("OCISessionEnd()", myerrhp, rc, myenvhp)) return -1;
rc = OCIServerDetach(mysrvhp, myerrhp, OCI_DEFAULT);
if(check_oci_error("OCIServerDetach()", myerrhp, rc, myenvhp)) return -1;
OCIHandleFree(myenvhp, OCI_HTYPE_ENV);
cout << endl << "Finalization done." << endl;
return 0;
}
// helper error checking procedure to shorten main code, returns true if critical error detected
// and prints out error information
bool check_oci_error(char *error_point, OCIError *errhp, sword status, OCIEnv *envhp) {
text errbuf[1024];
sb4 errcode;
bool ret_code = true;
switch (status) {
case OCI_SUCCESS:
ret_code = false;
break;
case OCI_SUCCESS_WITH_INFO:
OCIErrorGet ((dvoid *) errhp, (ub4) 1, (text *) NULL, &errcode, errbuf, (ub4) sizeof(errbuf), (ub4) OCI_HTYPE_ERROR);
cout << error_point << " Error: OCI_SUCCESS_WITH_INFO; Info: " << errbuf << endl;
ret_code = (errcode == 436 || errcode == 437 || errcode == 438 || errcode == 439);
break;
case OCI_NEED_DATA:
cout << error_point << " Error: OCI_NEED_DATA"<< endl;
break;
case OCI_NO_DATA:
cout << error_point << " Error: OCI_NO_DATA"<< endl;
break;
case OCI_ERROR:
OCIErrorGet ((dvoid *) errhp, (ub4) 1, (text *) NULL, &errcode, errbuf, (ub4) sizeof(errbuf), (ub4) OCI_HTYPE_ERROR);
cout << error_point << " Error: " << errbuf << endl;
break;
case OCI_INVALID_HANDLE:
cout << error_point << " Error: OCI_INVALID_HANDLE" << endl;
break;
case OCI_STILL_EXECUTING:
cout << error_point << " Error: OCI_STILL_EXECUTE"<< endl;
break;
case OCI_CONTINUE:
cout << error_point << " Error: OCI_CONTINUE" << endl;
break;
default:
cout << error_point << " Error: UNKNOWN(" << status << ")" << endl;
break;
}
if(ret_code && (envhp != NULL)) OCIHandleFree(envhp, OCI_HTYPE_ENV);
return ret_code;
}
P.S. Bạn có thể lấy thông tin từ tài liệu Oracle và this example code.
Đọc phần này: http://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:110612348061 Nếu bạn không quen thuộc với asktom, bạn đang thiếu một điều tốt nguồn. – OldProgrammer
Có thể thực hiện được trong OCI, mặc dù không dễ dàng. Các hoạt động ở mức thu thập này không được ghi lại trong tài liệu tham khảo OCI. Bạn đã cài đặt toàn bộ máy chủ Oracle (bao gồm cả Demos) và đọc nguồn demo OCI. Nhưng dù sao thì bạn cũng phải có 1 bộ sưu tập 'cast' OCI vào' TABLE'. Oracle truy vấn không thể đọc trực tiếp từ bộ sưu tập. – ibre5041
Ivan, âm thanh đầy hứa hẹn - bạn có thể chỉ cho tôi hướng tới một bản trình diễn cụ thể hoặc cung cấp đoạn mã không? Thanks :) – StilesCrisis