Oracle Database 管理者リファレンス 10g リリース2(10.2) for UNIX Systems B19278-06 |
|
この付録では、Oracle ODBC Driverの使用方法について説明します。次の項目について説明します。
Oracle ODBC Driverは、次のODBC 3.0機能をサポートしていません。
SQL_C_UBIGINT
およびSQL_C_SBIGINT
Cデータ型識別子
SQL_LOGIN_TIMEOUT
属性
Oracle ODBC Driverは、次の表に示すSQL関数をサポートしていません。
文字列関数 | 数値関数 | 時間関数、日付関数および間隔関数 |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
この項では、DATE
、TIMESTAMP
および浮動小数点の各データ型について説明します。
OracleのDATE
およびTIMESTAMP
データ型のセマンティクスは、同名のODBCデータ型と必ずしも正確に対応していません。OracleのDATE
データ型には、日付と時間の両方の情報が格納されています。これに対して、SQL_DATE
データ型に格納されているのは、日付情報のみです。OracleのTIMESTAMP
データ型にも日付と時間の情報が格納されていますが、その小数秒の精度は他方に比較して高くなります。Oracle ODBC Driverは、情報が失われるのを防ぐために、OracleのDATE
列とTIMESTAMP
列の両方のデータ型をSQL_TIMESTAMP
として報告します。同様に、Oracle ODBC DriverはSQL_TIMESTAMP
パラメータをOracleのTIMESTAMP値としてバインドします。
リリース10.1以上のOracle Databaseに接続すると、Oracle ODBC Driverは、Oracleの浮動小数点データ型BINARY_FLOAT
とBINARY_DOUBLE
をODBCデータ型SQL_REAL
とSQL_DOUBLE
にそれぞれマップします。これより前のリリースでは、SQL_REAL
とSQL_DOUBLE
がOracleの汎用数値データ型にマップされていました。
Oracle ODBC DriverおよびOracle Databaseには、データ型に関する制限事項があります。次の表に制限事項を示します。
SQLDriverConnect
関数は、Oracle ODBC Driverによって実装される関数の1つです。次の表に、SQLDriverConnect
関数コールの接続文字列の引数に含めることができるキーワードを示します。
Oracle Databaseは、トランザクション間のロックの競合が解決されるまで無期限に待機します。ただし、Oracle Databaseがロックの解決を待機する時間は制限できます。制限するには、ODBCのSQLSetStmtAttr
関数のコール時にSQL_ATTR_QUERY_TIMEOUT
属性を設定してから、データソースに接続します。
プログラムをリンクする場合は、そのプログラムをDriver Managerライブラリlibodbc.so
にリンクする必要があります。
ODBCのSQLSpecialColumns
関数は、表内の列に関する情報を返します。Oracle ODBC Driverで使用すると、この関数はOracle表に関連付けられたOracle ROWIDに関する情報を返します。
ROWIDは、SQL文のWHERE
句で使用できます。ただし、ROWID値はパラメータ・マーカー内に存在している必要があります。
Oracle参照カーソル(結果セットとも呼ばれます)によって、アプリケーションでは、ストアド・プロシージャとストアド・ファンクションを使用してデータを取得できます。ここでは、参照カーソルを使用してODBCを介して結果セットを有効にする方法を説明します。
RSET
です。
Procedure call: {CALL Example1(?)} {CALL RSET.Example1(?)} Function Call: {? = CALL Example1(?)} {? = CALL RSET.Example1(?)}
Example2
には4つのパラメータが定義されているとします。パラメータ1と3は参照カーソル・パラメータで、パラメータ2と4は文字列です。コールは、次のように指定されます。
{CALL RSET.Example2("Literal 1", "Literal 2")}
次のサンプル・アプリケーションは、Oracle ODBC Driverを使用して結果セットを返す方法を示します。
/* * Sample Application using Oracle reference cursors through ODBC * * Assumptions: * * 1) Oracle Sample database is present with data loaded for the EMP table. * * 2) Two fields are referenced from the EMP table, ename and mgr. * * 3) A data source has been setup to access the sample database. * * * Program Description: * * Abstract: * * This program demonstrates how to return result sets using * Oracle stored procedures * * Details: * * This program: * Creates an ODBC connection to the database. * Creates a Packaged Procedure containing two result sets. * Executes the procedure and retrieves the data from both result sets. * Displays the data to the user. * Deletes the package then logs the user out of the database. * * * The following is the actual PL/SQL this code generates to * create the stored procedures. * DROP PACKAGE ODBCRefCur; CREATE PACKAGE ODBCRefCur AS TYPE ename_cur IS REF CURSOR; TYPE mgr_cur IS REF CURSOR; PROCEDURE EmpCurs(Ename IN OUT ename_cur, Mgr IN OUT mgr_cur, pjob IN VARCHAR2); END; / CREATE PACKAGE BODY ODBCRefCur AS PROCEDURE EmpCurs(Ename IN OUT ename_cur, Mgr IN OUT mgr_cur, pjob IN VARCHAR2) AS BEGIN IF NOT Ename%ISOPEN THEN OPEN Ename for SELECT ename from emp; END IF; IF NOT Mgr%ISOPEN THEN OPEN Mgr for SELECT mgr from emp where job = pjob; END IF; END; END; / * * End PL/SQL for Reference Cursor. */ /* * Include Files */ #include <stdio.h> #include <sql.h> #include <sqlext.h> /* * Defines */ #define JOB_LEN 9 #define DATA_LEN 100 #define SQL_STMT_LEN 500 /* * Procedures */ void DisplayError( SWORD HandleType, SQLHANDLE hHandle, char *Module ); /* * Main Program */ int main() { SQLHENV hEnv; SQLHDBC hDbc; SQLHSTMT hStmt; SQLRETURN rc; char *DefUserName ="scott"; char *DefPassWord ="tiger"; SQLCHAR ServerName[DATA_LEN]; SQLCHAR *pServerName=ServerName; SQLCHAR UserName[DATA_LEN]; SQLCHAR *pUserName=UserName; SQLCHAR PassWord[DATA_LEN]; SQLCHAR *pPassWord=PassWord; char Data[DATA_LEN]; SQLINTEGER DataLen; char error[DATA_LEN]; char *charptr; SQLCHAR SqlStmt[SQL_STMT_LEN]; SQLCHAR *pSqlStmt=SqlStmt; char *pSalesMan = "SALESMAN"; SQLINTEGER sqlnts=SQL_NTS; /* * Allocate the Environment Handle */ rc = SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv ); if (rc != SQL_SUCCESS) { printf( "Cannot Allocate Environment Handle\n"); printf( "\nHit Return to Exit\n"); charptr = gets ((char *)error); exit(1); } /* * Set the ODBC Version */ rc = SQLSetEnvAttr( hEnv,SQL_ATTR_ODBC_VERSION,(void *)SQL_OV_ODBC3,0); if (rc != SQL_SUCCESS) { printf( "Cannot Set ODBC Version\n"); printf( "\nHit Return to Exit\n"); charptr = gets ((char *)error); exit(1); } /* * Allocate the Connection handle */ rc = SQLAllocHandle( SQL_HANDLE_DBC, hEnv, &hDbc ); if (rc != SQL_SUCCESS) { printf( "Cannot Allocate Connection Handle\n"); printf( "\nHit Return to Exit\n"); charptr = gets ((char *)error); exit(1); } /* * Get User Information */ strcpy ((char *) pUserName, DefUserName ); strcpy ((char *) pPassWord, DefPassWord ); /* * Data Source name */ printf( "\nEnter the ODBC Data Source Name\n" ); charptr = gets ((char *) ServerName); /* * User Name */ printf ( "\nEnter User Name Default [%s]\n", pUserName); charptr = gets ((char *) UserName); if (*charptr == '\0') { strcpy ((char *) pUserName, (char *) DefUserName ); } /* * Password */ printf ( "\nEnter Password Default [%s]\n", pPassWord); charptr = gets ((char *)PassWord); if (*charptr == '\0') { strcpy ((char *) pPassWord, (char *) DefPassWord ); } /* * Connection to the database */ rc = SQLConnect( hDbc,pServerName,(SQLSMALLINT) strlen((char *)pServerName),pUserName,(SQLSMALLINT) strlen((char *)pUserName),pPassWord,(SQLSMALLINT) strlen((char *)pPassWord)); if (rc != SQL_SUCCESS) { DisplayError(SQL_HANDLE_DBC, hDbc, "SQLConnect"); } /* * Allocate a Statement */ rc = SQLAllocHandle( SQL_HANDLE_STMT, hDbc, &hStmt ); if (rc != SQL_SUCCESS) { printf( "Cannot Allocate Statement Handle\n"); printf( "\nHit Return to Exit\n"); charptr = gets ((char *)error); exit(1); } /* * Drop the Package */ strcpy( (char *) pSqlStmt, "DROP PACKAGE ODBCRefCur"); rc = SQLExecDirect(hStmt, pSqlStmt, strlen((char *)pSqlStmt)); /* * Create the Package Header */ strcpy( (char *) pSqlStmt, "CREATE PACKAGE ODBCRefCur AS\n"); strcat( (char *) pSqlStmt, " TYPE ename_cur IS REF CURSOR;\n"); strcat( (char *) pSqlStmt, " TYPE mgr_cur IS REF CURSOR;\n\n"); strcat( (char *) pSqlStmt, " PROCEDURE EmpCurs (Ename IN OUT ename_cur,"); strcat( (char *) pSqlStmt, "Mgr IN OUT mgr_cur,pjob IN VARCHAR2);\n\n"); strcat( (char *) pSqlStmt, "END;\n"); rc = SQLExecDirect(hStmt, pSqlStmt, strlen((char *)pSqlStmt)); if (rc != SQL_SUCCESS) { DisplayError(SQL_HANDLE_STMT, hStmt, "SQLExecDirect"); } /* * Create the Package Body */ strcpy( (char *) pSqlStmt, "CREATE PACKAGE BODY ODBCRefCur AS\n"); strcat( (char *) pSqlStmt, " PROCEDURE EmpCurs (Ename IN OUT ename_cur,"); strcat( (char *) pSqlStmt, "Mgr IN OUT mgr_cur, pjob IN VARCHAR2)\n AS\n BEGIN\n"); strcat( (char *) pSqlStmt, " IF NOT Ename%ISOPEN\n THEN\n"); strcat( (char *) pSqlStmt, " OPEN Ename for SELECT ename from emp;\n"); strcat( (char *) pSqlStmt, " END IF;\n\n"); strcat( (char *) pSqlStmt, " IF NOT Mgr%ISOPEN\n THEN\n"); strcat( (char *) pSqlStmt, " OPEN Mgr for SELECT mgr from emp where job = pjob;\n"); strcat( (char *) pSqlStmt, " END IF;\n"); strcat( (char *) pSqlStmt, " END;\n"); strcat( (char *) pSqlStmt, "END;\n"); rc = SQLExecDirect(hStmt, pSqlStmt, strlen((char *)pSqlStmt)); if (rc != SQL_SUCCESS) { DisplayError(SQL_HANDLE_STMT, hStmt, "SQLExecDirect"); } /* * Bind the Parameter */ rc = SQLBindParameter(hStmt,1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,JOB_ LEN,0,pSalesMan,0,&sqlnts); /* * Call the Store Procedure which executes the Result Sets */ strcpy( (char *) pSqlStmt, "{CALL ODBCRefCur.EmpCurs(?)}"); rc = SQLExecDirect(hStmt, pSqlStmt, strlen((char *)pSqlStmt)); if (rc != SQL_SUCCESS) { DisplayError(SQL_HANDLE_STMT, hStmt, "SQLExecDirect"); } /* * Bind the Data */ rc = SQLBindCol( hStmt,1,SQL_C_CHAR,Data,sizeof(Data),&DataLen); if (rc != SQL_SUCCESS) { DisplayError(SQL_HANDLE_STMT, hStmt, "SQLBindCol"); } /* * Get the data for Result Set 1 */ printf( "\nEmployee Names\n\n"); while ( rc == SQL_SUCCESS ) { rc = SQLFetch( hStmt ); if ( rc == SQL_SUCCESS ) { printf("%s\n", Data); } else { if (rc != SQL_NO_DATA) { DisplayError(SQL_HANDLE_STMT, hStmt, "SQLFetch"); } } } printf( "\nFirst Result Set - Hit Return to Continue\n"); charptr = gets ((char *)error); /* * Get the Next Result Set */ rc = SQLMoreResults( hStmt ); if (rc != SQL_SUCCESS) { DisplayError(SQL_HANDLE_STMT, hStmt, "SQLMoreResults"); } /* * Get the data for Result Set 2 */ printf( "\nManagers\n\n"); while ( rc == SQL_SUCCESS ) { rc = SQLFetch( hStmt ); if ( rc == SQL_SUCCESS ) { printf("%s\n", Data); } else { if (rc != SQL_NO_DATA) { DisplayError(SQL_HANDLE_STMT, hStmt, "SQLFetch"); } } } printf( "\nSecond Result Set - Hit Return to Continue\n"); charptr = gets ((char *)error); /* * Should Be No More Results Sets */ rc = SQLMoreResults( hStmt ); if (rc != SQL_NO_DATA) { DisplayError(SQL_HANDLE_STMT, hStmt, "SQLMoreResults"); } /* * Drop the Package */ strcpy( (char *) pSqlStmt, "DROP PACKAGE ODBCRefCur"); rc = SQLExecDirect(hStmt, pSqlStmt, strlen((char *)pSqlStmt)); /* * Free handles close connections to the database */ SQLFreeHandle( SQL_HANDLE_STMT, hStmt ); SQLDisconnect( hDbc ); SQLFreeHandle( SQL_HANDLE_DBC, hDbc ); SQLFreeHandle( SQL_HANDLE_ENV, hEnv ); printf( "\nAll Done - Hit Return to Exit\n"); charptr = gets ((char *)error); return(0); } /* * Display Error Messages */ void DisplayError( SWORD HandleType, SQLHANDLE hHandle, char *Module ) { SQLCHAR MessageText[255]; SQLCHAR SQLState[80]; SQLRETURN rc=SQL_SUCCESS; long NativeError; SWORD RetLen; SQLCHAR error[25]; char *charptr; rc = SQLGetDiagRec(HandleType,hHandle,1,SQLState,&NativeError,MessageText,255,&RetLen); printf( "Failure Calling %s\n", Module ); if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { printf( "\t\t\t State: %s\n", SQLState); printf( "\t\t\t Native Error: %d\n", NativeError ); printf( "\t\t\t Error Message: %s\n", MessageText ); } printf( "\nHit Return to Exit\n"); charptr = gets ((char *)error); exit(1); }
使用しているSQL ServerのEXEC
文の構文を変更せずに同等のOracleプロシージャ・コールに容易に変換できる場合、その構文は、このオプションを有効にすると、Oracle ODBC Driverで変換できます。
SQL Serverプロシージャの完全な名前は、次に示す最大4つの識別子で構成されます。
この名前の書式は次のとおりです。
[[[server.][database].][owner_name].]procedure_name
Microsoft SQL ServerデータベースからOracle Databaseに移行する際、各SQL Serverプロシージャまたはファンクションの定義は同等のOracle Database構文に変換され、Oracle Databaseのスキーマで定義されます。移行したプロシージャは、多くの場合、次のいずれかの方法で再編成(およびスキーマで作成)されます。
移行したプロシージャを編成するこれら3通りの方法をサポートするためには、いずれかのスキーマ名オプションを指定してプロシージャ名を変換できます。変換したOracleプロシージャ・コールのオブジェクト名では、大/小文字が区別されません。
この項では、Oracle ODBC Driverでサポートされている機能について説明します。次の項目について説明します。
Oracle ODBC Driverリリース10.2.0.1.0以上では、コア、レベル2およびレベル1のすべての関数をサポートしています。
次の表に、Oracle ODBC Driverが特定の関数を実装する方法を示します。
SQLProcedures
コールおよびSQLProcedureColumns
コールは、パッケージ内に含まれている場合でも、すべてのプロシージャおよびファンクションに関する情報を検索して返すように変更されました。これより前のリリースでは、これらのコールで検索されるのは、パッケージ外のプロシージャとファンクションのみでした。次の例では、SQL_ATTR_METADATA_ID
属性をSQL_FALSE
に設定した場合に返されるプロシージャまたはファンクションを示します。
この例では、次のストアド・プロシージャがあるとします。
"BAR" "BARX" "XBAR" "XBARX" "SQLPROCTEST.BAR" "SQLPROCTEST.BARX" "SQLPROCTEST.XBAR" "SQLPROCTEST.XBARX"
%
または%%%%%%
と指定して検索すると、これら8つのプロシージャがすべて返されます。
%_
または_%
と指定して検索すると、次のプロシージャが返されます。
BAR BARX XBAR XBARX
.
、.%
、%.%
、SQLPROC%.
またはSQLPROC%.%
と指定して検索すると、次のプロシージャが返されます。
SQLPROCTEST.BAR SQLPROCTEST.BARX SQLPROCTEST.XBAR SQLPROCTEST.XBARX
%BAR
と指定して検索すると、次のプロシージャが返されます。
BAR XBAR
.%BAR
または%.%BAR
と指定して検索すると、次のプロシージャが返されます。
SQLPROCTEST.BAR SQLPROCTEST.XBAR
SQLPROC%
または.SQLPROC%
と指定して検索すると、次のプロシージャが返されます。
nothing (0 rows)
比較述語で比較の2番目の式としてパラメータ・マーカーが使用され、そのパラメータの値がSQLBindParameter
を使用してSQL_NULL_DATA
に設定されている場合、比較は失敗します。これは、ODBC SQLのNULL述語構文と一致しています。
プログラマがデータ型を実装する際に最も考慮する必要があるのは、CHAR
、VARCHAR
およびVARCHAR2
の各データ型です。
SQL_VARCHAR
の値がfSqlType
の場合、SQLGetTypeInfo
はOracle Databaseデータ型VARCHAR2
を返します。SQL_CHAR
の値がfSqlType
の場合、SQLGetTypeInfo
はOracle Databaseデータ型CHAR
を返します。
この項では、Unicodeのサポートについて説明します。次の項目について説明します。
ODBC Driver Managerを使用すると、すべてのODBCドライバは、Unicodeをサポートしているかどうかに関係なくUnicode準拠と同様に動作します。これによって、ODBCアプリケーションは、基礎となるODBCドライバのUnicode機能に関係なく記述できます。
Driver ManagerがANSI ODBCドライバに対するUnicodeサポートをエミュレートできる範囲は、Unicodeデータとローカル・コード・ページ間で可能な変換内容によって制限されます。Driver ManagerがデータをUnicodeからローカル・コード・ページに変換する際、データが失われる場合があります。基礎となるODBCドライバがUnicodeをサポートしていないかぎり、Unicodeの完全なサポートは不可能です。Oracle ODBC Driverは、Unicodeを完全にサポートしています。
ODBC APIは、W
およびA
接尾辞の変換を使用して、UnicodeおよびANSIエントリ・ポイントの両方をサポートします。ODBCアプリケーション開発者は、接尾辞を指定してエントリ・ポイントを明示的にコールする必要はありません。UNICODEおよび_UNICODEプリプロセッサ定義を使用してコンパイルされたODBCアプリケーションによって、適切なコールが生成されます。たとえば、SQLPrepare
へのコールは、SQLPrepareW
としてコンパイルされます。
Cデータ型のSQL_C_WCHAR
がODBCインタフェースに追加されたため、アプリケーションでは、入力パラメータをUnicodeとしてエンコードするように指定するか、またはUnicodeとして返された列データを要求できます。マクロのSQL_C_TCHAR
は、UnicodeおよびANSIの両方で作成する必要があるアプリケーションで有効です。SQL_C_TCHAR
マクロは、Unicodeアプリケーションの場合はSQL_C_WCHAR
として、ANSIアプリケーションの場合はSQL_C_CHAR
としてコンパイルされます。
SQLデータ型のSQL_WCHAR
、SQL_WVARCHAR
およびSQL_WLONGVARCHAR
がODBCインタフェースに追加され、表内で定義された列がUnicodeで表現されるようになりました。これらの値は、SQLDescribeCol
、SQLColAttribute
、SQLColumns
およびSQLProcedureColumns
へのコールから返すことも可能です。
Unicodeエンコーディングは、SQL列型のNCHAR、NVARCHAR2およびNCLOBに対してサポートされています。さらに、文字セマンティクスが列定義で指定されている場合、UnicodeエンコーディングはSQL列型のCHARおよびVARCHAR2に対してもサポートされています。
Oracle ODBC Driverは、これらのSQL列型をサポートしてODBC SQLデータ型にマップします。次の表に、サポートされているSQLデータ型および対応するODBC SQLデータ型を示します。
SQLデータ型 | ODBC SQLデータ型 |
---|---|
|
|
|
|
|
|
|
|
|
|
SQLGetData
関数を使用すると、ODBCアプリケーションはデータ型を指定して、データのフェッチ後に列を取得できます。OCIでは、Oracle ODBC Driverはデータ型を指定してからフェッチする必要があります。この場合、Oracle ODBC Driverは(データベースで定義された)列のデータ型に関する情報を使用し、OCIを介して列をフェッチする最適なデフォルト方法を判断します。
文字データを含む列がSQLBindCol
によってバインドされていない場合、Oracle ODBC Driverは、その列をUnicodeとしてフェッチするか、またはローカル・コード・ページとしてフェッチするかを判断する必要があります。ドライバは、列をUnicodeとして受け入れるように常にデフォルト設定できます。ただし、この設定では、2回の不要な変換が実行されます。たとえば、データベース内のデータがANSIとしてエンコードされた場合、データをOracle ODBC DriverにフェッチするためにANSIからUnicodeへの変換が実行されます。次に、ODBCアプリケーションがそのデータをSQL_C_CHAR
として要求すると、データを元のエンコーディングに戻すための変換が実行されます。
Oracle Database Clientのデフォルト・エンコーディングは、データをフェッチする際に使用されます。ただし、ODBCアプリケーションでは、このデフォルトを上書きし、列またはパラメータをWCHARデータ型としてバインドすることによって、データをUnicodeとしてフェッチできます。
Oracle ODBC Driver自体がTCHAR
マクロを使用して実装されているため、ODBCアプリケーション・プログラムでは、TCHAR
を使用して、このドライバを利用することをお薦めします。
次の例では、UNICODE
および_UNICODE
を指定してコンパイルするとWCHAR
データ型になるTCHAR
の使用方法を示します。
このコードを使用するには、SQLConnect
にUnicodeリテラルのみ指定する必要があります。
HENV envHnd; HDBC conHnd; HSTMT stmtHnd; RETCODE rc; rc = SQL_SUCCESS; // ENV is allocated rc = SQLAllocEnv(&envHnd); // Connection Handle is allocated rc = SQLAllocConnect(envHnd, &conHnd); rc = SQLConnect(conHnd, _T("stpc19"), SQL_NTS, _T("scott"), SQL_NTS, _T("tiger"), SQL_ NTS); . . . if (conHnd) SQLFreeConnect(conHnd); if (envHnd) SQLFreeEnv(envHnd);例 G-2 単純な取得
次の例では、EMP
表から従業員名と役職名を取得します。すべてのODBC関数にTCHAR
準拠のデータを指定する必要があることを除いて、ANSIの場合と違いはありません。Unicodeアプリケーションの場合は、SQLBindCol
のコール時にバッファ長をBYTE長に指定する必要があります。たとえば、sizeof(ename)
の場合は次のとおりです。
/* ** Execute SQL, bind columns, and Fetch. ** Procedure: ** ** SQLExecDirect ** SQLBindCol ** SQLFetch ** */ static SQLTCHAR *sqlStmt = _T("SELECT ename, job FROM emp"); SQLTCHAR ename[50]; SQLTCHAR job[50]; SQLINTEGER enamelen, joblen; _tprintf(_T("Retrieve ENAME and JOB using SQLBindCol 1.../n[%s]/n"), sqlStmt); // Step 1: Prepare and Execute rc = SQLExecDirect(stmtHnd, sqlStmt, SQL_NTS); // select checkSQLErr(envHnd, conHnd, stmtHnd, rc); // Step 2: Bind Columns rc = SQLBindCol(stmtHnd, 1, SQL_C_TCHAR, ename, sizeof(ename), &enamelen); checkSQLErr(envHnd, conHnd, stmtHnd, rc); rc = SQLBindCol(stmtHnd, 2, SQL_C_TCHAR, job, sizeof(job), &joblen); checkSQLErr(envHnd, conHnd, stmtHnd, rc); do { // Step 3: Fetch Data rc = SQLFetch(stmtHnd); if (rc == SQL_NO_DATA) break; checkSQLErr(envHnd, conHnd, stmtHnd, rc); _tprintf(_T("ENAME = %s, JOB = %s/n"), ename, job); } while (1); _tprintf(_T("Finished Retrieval/n/n"));例 G-3 SQLGetDataを使用した取得(フェッチ後のバインド)
この例では、SQLGetData
の使用方法を示します。Unicode固有の事項に関しては、ANSIアプリケーションとの違いはありません。
/* ** Execute SQL, bind columns, and Fetch. ** Procedure: ** ** SQLExecDirect ** SQLFetch ** SQLGetData */ static SQLTCHAR *sqlStmt = _T("SELECT ename,job FROM emp"); // same as Case 1. SQLTCHAR ename[50]; SQLTCHAR job[50]; _tprintf(_T("Retrieve ENAME and JOB using SQLGetData.../n[%s]/n"), sqlStmt); if (rc != SQL_SUCCESS) { _tprintf(_T("Failed to allocate STMT/n")); goto exit2; } // Step 1: Prepare and Execute rc = SQLExecDirect(stmtHnd, sqlStmt, SQL_NTS); // select checkSQLErr(envHnd, conHnd, stmtHnd, rc); do { // Step 2: Fetch rc = SQLFetch(stmtHnd); if (rc == SQL_NO_DATA) break; checkSQLErr(envHnd, conHnd, stmtHnd, rc); // Step 3: GetData rc = SQLGetData(stmtHnd, 1, SQL_C_TCHAR, (SQLPOINTER)ename, sizeof(ename), NULL); checkSQLErr(envHnd, conHnd, stmtHnd, rc); rc = SQLGetData(stmtHnd, 2, SQL_C_TCHAR, (SQLPOINTER)job, sizeof(job), NULL); checkSQLErr(envHnd, conHnd, stmtHnd, rc); _tprintf(_T("ENAME = %s, JOB = %s/n"), ename, job); } while (1); _tprintf(_T("Finished Retrieval/n/n"));例 G-4 単純な更新
この例では、データの更新方法を示します。SQLBindParameter
のデータ長は、Unicodeアプリケーションの場合でもBYTE長で指定する必要があります。
/* ** Execute SQL, bind columns, and Fetch. ** Procedure: ** ** SQLPrepare ** SQLBindParameter ** SQLExecute */ static SQLTCHAR *sqlStmt = _T("INSERT INTO emp(empno,ename,job) VALUES(?,?,?)"); static SQLTCHAR *empno = _T("9876"); // Emp No static SQLTCHAR *ename = _T("ORACLE"); // Name static SQLTCHAR *job = _T("PRESIDENT"); // Job _tprintf(_T("Insert User ORACLE using SQLBindParameter.../n[%s]/n"), sqlStmt); // Step 1: Prepare rc = SQLPrepare(stmtHnd, sqlStmt, SQL_NTS); // select checkSQLErr(envHnd, conHnd, stmtHnd, rc); // Step 2: Bind Parameter rc = SQLBindParameter(stmtHnd, 1, SQL_PARAM_INPUT, SQL_C_TCHAR, SQL_DECIMAL, 4, // 4 digit 0, (SQLPOINTER)empno, 0, NULL); checkSQLErr(envHnd, conHnd, stmtHnd, rc); rc = SQLBindParameter(stmtHnd, 2, SQL_PARAM_INPUT, SQL_C_TCHAR, SQL_CHAR, lstrlen(ename)*sizeof(TCHAR), 0, (SQLPOINTER)ename, lstrlen(ename)*sizeof(TCHAR), NULL); checkSQLErr(envHnd, conHnd, stmtHnd, rc); rc = SQLBindParameter(stmtHnd, 3, SQL_PARAM_INPUT, SQL_C_TCHAR, SQL_CHAR, lstrlen(job)*sizeof(TCHAR), 0, (SQLPOINTER)job, lstrlen(job)*sizeof(TCHAR), NULL); checkSQLErr(envHnd, conHnd, stmtHnd, rc); // Step 3: Execute rc = SQLExecute(stmtHnd); checkSQLErr(envHnd, conHnd, stmtHnd, rc);例 G-5 長いデータ(CLOB)の更新と取得
ここでは、CLOB
などの長いデータをOracle Databaseで更新および取得する最も複雑な例を示します。データ長は常にBYTE
長であるため、BYTE
長を導出するには式lstrlen(TCHAR data)*sizeof(TCHAR)
が必要です。
/* ** Execute SQL, bind columns, and Fetch. ** Procedure: ** ** SQLPrepare ** SQLBindParameter ** SQLExecute ** SQLParamData ** SQLPutData ** ** SQLExecDirect ** SQLFetch ** SQLGetData */ static SQLTCHAR *sqlStmt1 = _T("INSERT INTO clobtbl(clob1) VALUES(?)"); static SQLTCHAR *sqlStmt2 = _T("SELECT clob1 FROM clobtbl"); SQLTCHAR clobdata[1001]; SQLTCHAR resultdata[1001]; SQLINTEGER ind = SQL_DATA_AT_EXEC; SQLTCHAR *bufp; int clobdatalen, chunksize, dtsize, retchklen; _tprintf(_T("Insert CLOB1 using SQLPutData.../n[%s]/n"), sqlStmt1); // Set CLOB Data { int i; SQLTCHAR ch; for (i=0, ch=_T('A'); i< sizeof(clobdata)/sizeof(SQLTCHAR); ++i, ++ch) { if (ch > _T('Z')) ch = _T('A'); clobdata[i] = ch; } clobdata[sizeof(clobdata)/sizeof(SQLTCHAR)-1] = _T('/0'); } clobdatalen = lstrlen(clobdata); // length of characters chunksize = clobdatalen / 7; // 7 times to put // Step 1: Prepare rc = SQLPrepare(stmtHnd, sqlStmt1, SQL_NTS); checkSQLErr(envHnd, conHnd, stmtHnd, rc); // Step 2: Bind Parameter with SQL_DATA_AT_EXEC rc = SQLBindParameter(stmtHnd, 1, SQL_PARAM_INPUT, SQL_C_TCHAR, SQL_LONGVARCHAR, clobdatalen*sizeof(TCHAR), 0, (SQLPOINTER)clobdata, clobdatalen*sizeof(TCHAR), &ind); checkSQLErr(envHnd, conHnd, stmtHnd, rc); // Step 3: Execute rc = SQLExecute(stmtHnd); checkSQLErr(envHnd, conHnd, stmtHnd, rc); // Step 4: ParamData (initiation) rc = SQLParamData(stmtHnd, (SQLPOINTER*)&bufp); // set value checkSQLErr(envHnd, conHnd, stmtHnd, rc); for (dtsize=0, bufp = clobdata; dtsize < clobdatalen; dtsize += chunksize, bufp += chunksize) { int len; if (dtsize+chunksize<clobdatalen) len = chunksize; else len = clobdatalen-dtsize; // Step 5: PutData rc = SQLPutData(stmtHnd, (SQLPOINTER)bufp, len*sizeof(TCHAR)); checkSQLErr(envHnd, conHnd, stmtHnd, rc); } // Step 6: ParamData (temination) rc = SQLParamData(stmtHnd, (SQLPOINTER*)&bufp); checkSQLErr(envHnd, conHnd, stmtHnd, rc); rc = SQLFreeStmt(stmtHnd, SQL_CLOSE); _tprintf(_T("Finished Update/n/n")); rc = SQLAllocStmt(conHnd, &stmtHnd); if (rc != SQL_SUCCESS) { _tprintf(_T("Failed to allocate STMT/n")); goto exit2; } // Clear Result Data memset(resultdata, 0, sizeof(resultdata)); chunksize = clobdatalen / 15; // 15 times to put // Step 1: Prepare rc = SQLExecDirect(stmtHnd, sqlStmt2, SQL_NTS); // select checkSQLErr(envHnd, conHnd, stmtHnd, rc); // Step 2: Fetch rc = SQLFetch(stmtHnd); checkSQLErr(envHnd, conHnd, stmtHnd, rc); for(dtsize=0, bufp = resultdata; dtsize < sizeof(resultdata)/sizeof(TCHAR) && rc != SQL_NO_DATA; dtsize += chunksize-1, bufp += chunksize-1) { int len; // len should contain the space for NULL termination if (dtsize+chunksize<sizeof(resultdata)/sizeof(TCHAR)) len = chunksize; else len = sizeof(resultdata)/sizeof(TCHAR)-dtsize; // Step 3: GetData rc = SQLGetData(stmtHnd, 1, SQL_C_TCHAR, (SQLPOINTER)bufp, len*sizeof(TCHAR), &retchklen); } if (!_tcscmp(resultdata, clobdata)) { _tprintf(_T("Succeeded!!/n/n")); } else { _tprintf(_T("Failed!!/n/n")); }
この項では、次の項目について説明します。
ODBCアプリケーションのパフォーマンスを向上させるには、次のプログラミング・ガイドラインを適用してください。
SQLExecute
ごとに文を準備するよりも、1つの文を1回準備して複数回実行する方が効率的です。
LONG
列をSELECT
文に含めないでください。LONG
列がSELECT
文に含まれていると、データベース・サーバー・プロトコルの特性上、アプリケーションが列をバインドするかSQLGetData
操作を実行するかに関係なく、Oracle ODBC Driverでは列全体をフェッチする必要があるためです。
SQLSetConnectAttr
関数のSQL_ATTR_ACCESS_MODE
属性をSQL_MODE_READ_ONLY
に設定します。
SQLSetConnectAttr
関数またはSQLSetStmtAttr
関数のSQL_ATTR_NOSCAN
属性をtrueに設定します。
SQLFetch
関数のかわりにODBCのSQLFetchScroll
関数を使用します。
この項では、次のODBCデータソース構成オプションを使用した場合のパフォーマンスへの影響を説明します。
このオプションによって、プロシージャ・コールから結果セット(RefCursor
など)を返すサポートが有効になります。デフォルトでは、結果セットを返すサポートが有効です。
Oracle ODBC Driverでは、RefCursor
パラメータが存在するかどうかを判断するために、データベース・サーバーを問い合せてプロシージャのパラメータ・セットとそのデータ型を判別する必要があります。この問合せによって、最初にプロシージャが準備完了になり実行されると、追加のネットワーク・ラウンドトリップが発生します。
このオプションによって、LOBを挿入および更新するサポートが有効になります。デフォルトではこのサポートが有効です。
Oracle ODBC Driverでは、LOBパラメータがあるかどうかを判断するために、データベース・サーバーを問い合せて、INSERT
文またはUPDATE
文の各パラメータのデータ型を判別する必要があります。この問合せによって、最初にINSERT
またはUPDATE
が準備完了になり実行されると、追加のネットワーク・ラウンドトリップが発生します。
SQL_TIMESTAMP
パラメータを適切なOracle Databaseデータ型としてバインドします。このオプションをTRUE
に設定すると、SQL_TIMESTAMP
はOracleのDATE
データ型としてバインドされます。このオプションをFALSE
に設定すると、SQL_TIMESTAMP
はOracleのTIMESTAMP
データ型としてバインドされます。これがデフォルトです。
ODBC関数SQLFreeStmt
のSQL_CLOSE
オプションは、関連するカーソルを文とともにクローズし、保留中のすべての結果を廃棄します。アプリケーションでは文を再度実行することによってカーソルを再オープンでき、SQLPrepareを再度実行する必要はありません。このオプションを使用する典型的な例は、しばらくの間アイドル状態になり、後で同じSQL文を再利用するアプリケーションの場合です。この場合、アプリケーションがアイドル状態になっている間、関連するサーバー・リソースを解放できます。
Oracle ODBC Driverが位置するOCIでは、カーソルをクローズする機能をサポートしていません。したがって、デフォルトでは、SQL_CLOSE
オプションはOracle ODBC Driverに影響を与えません。カーソルおよび関連するリソースはデータベース上でオープンしたままです。
このオプションを有効にすると、データベース・サーバー上の関連するカーソルがクローズします。ただし、その結果、SQL文の解析コンテキストが失われます。ODBCアプリケーションは文を再度実行でき、SQLPrepare
をコールする必要はありません。ただし、内部的には、Oracle ODBC Driverは文全体を準備して実行する必要があります。このオプションを有効にすると、文を1回準備して繰り返し実行するアプリケーションのパフォーマンスに大きな影響を与えます。
このオプションは、サーバー上のリソースを解放する必要がある場合のみ有効にしてください。
odbc.ini
ファイルのフェッチ・バッファ・サイズ(FetchBufferSize
)を、バイト単位で指定した値に設定します。この値は、Oracle ODBC DriverがOracle Databaseからクライントのキャッシュに1回にプリフェッチするデータの行数を決定するのに必要なメモリー量で、アプリケーション・プログラムが1回の問合せで要求する行数とは関係ありません。これによって、パフォーマンスが向上します。
また、1回にフェッチするデータが20行未満であることが多いアプリケーションで、特に、速度の遅いネットワーク接続である場合や負荷の大きいサーバーからフェッチする場合は、アプリケーションの応答時間が改善されます。この設定値が高すぎると、応答時間に悪影響が生じたり、大量のメモリーが消費される可能性があります。デフォルトは64,000バイトです。アプリケーションに対して最適な値を選択してください。
LONG
およびLOB
データ型がある場合、Oracle ODBC Driverがプリフェッチする行数はこのフェッチ・バッファ・サイズで決まりません。LONG
およびLOB
データ型が含まれると、パフォーマンスの向上は最小限になり、過剰にメモリーが使用される可能性があります。Oracle ODBC Driverはこのフェッチ・バッファ・サイズを無視し、LONG
およびLOB
データ型が存在する場合は一定の行数のみプリフェッチします。
WHERE
句でデータベースのDATE
列が使用され、その列に索引がある場合は、パフォーマンスに影響を与える可能性があります。たとえば、次のように入力します。
SELECT * FROM EMP WHERE HIREDATE = ?
この例では、HIREDATE
列の索引を使用して、問合せを迅速に実行できます。ただし、HIREDATE
がDATE
値で、Oracle ODBC Driverはこのパラメータ値をTIMESTAMP
として提供しているため、Oracle Databaseの問合せオプティマイザは変換関数を適用する必要があります。誤った結果(パラメータ値に0以外の小数秒がある場合に発生する可能性があります)を防ぐために、オプティマイザは、HIREDATE
列に変換を適用するため、次のような文になります。
SELECT * FROM EMP WHERE TO_TIMESTAMP(HIREDATE) = ?
ただし、この場合はHIREDATE
列で索引を使用できなくなります。かわりに、サーバーでは表の順次スキャンを実行します。したがって、表に多数の行がある場合は時間がかかる可能性があります。このような場合の次善策として、Oracle ODBC DriverにはTIMESTAMP
をDATE
としてバインドする接続オプションが用意されています。このオプションを有効にすると、Oracle ODBC Driverは、SQL_TIMESTAMP
パラメータをOracleのTIMESTAMP
データ型ではなくOracleのDATE
データ型としてバインドします。これによって、問合せオプティマイザはDATE
列で索引を使用できます。
エラーが発生すると、Oracle ODBC Driverは、システム固有のエラー番号、SQLSTATE
(ODBCエラー・コード)およびエラー・メッセージを返します。ドライバは、ドライバが検出したエラーおよびOracle Databaseから返されたエラーの両方からこれらの情報を導出します。
データソースでエラーが発生した場合、Oracle ODBC Driverは、Oracle Databaseから返されたシステム固有のエラーを返します。Oracle ODBC DriverまたはDriver Managerがエラーを検出した場合、Oracle ODBC Driverはシステム固有のエラー番号として0(ゼロ)を返します。
データソースでエラーが発生した場合、Oracle ODBC Driverは、返されたシステム固有のエラーを適切なSQLSTATE
にマップします。Oracle ODBC DriverまたはDriver Managerがエラーを検出した場合は、適切なSQLSTATE
を生成します。
データソースでエラーが発生した場合、Oracle ODBC Driverは、Oracle Databaseから返されたメッセージに基づいてエラー・メッセージを返します。Oracle ODBC DriverまたはDriver Managerでエラーが発生した場合、Oracle ODBC Driverは、SQLSTATE
に関連付けられたテキストに基づいてエラー・メッセージを返します。
エラー・メッセージの書式は次のとおりです。
[vendor] [ODBC-component] [data-source] error-message
大カッコ([ ])内の接頭辞によって、エラーの発生場所を識別します。次の表に、Oracle ODBC Driverから返される接頭辞の値を示します。データソースでエラーが発生した場合は、vendor
およびODBC-component
接頭辞によって、データソースからエラーを受け取ったODBCコンポーネントのベンダーと名前を識別します。
エラーの発生場所 | 接頭辞 | 値 |
---|---|---|
Driver Manager |
[data-source] |
該当なし |
Oracle ODBC Driver |
[data-source] |
該当なし |
Oracle Database |
[data-source] |
[Oracle OCI] |
たとえば、次の書式に示すように、エラー・メッセージにOra
接頭辞が付いていない場合、そのエラーはOracle ODBC Driverのエラーであることがわかります。
[Oracle][ODBC]Error message text here
また、次の書式に示すように、エラー・メッセージにOra
接頭辞が付いている場合、このエラーはOracle ODBC Driverのエラーではありません。
[Oracle][ODBC][Ora]Error message text here
エラー・メッセージのテキストがORA-
接頭辞で始まっている場合、そのエラーの詳細はOracle Databaseのドキュメントを参照してください。