위 링크처럼 EXEC가 아닌 CALL로 호출하는데 {}로 감싸줘야한다. 이거때문에 개고생했네
Enabling Result Sets (Programming)
more..
Oracle reference cursors (Result Sets) allow an application to retrieve data using stored procedures and stored functions. The following information identifies how to use reference cursors to enable Result Sets through ODBC.
The ODBC syntax for calling stored procedures must be used. Native PL/SQL is not supported through ODBC. The following identifies how to call the procedure or function without a package and within a package. The package name in this case is RSET.
The PL/SQL reference cursor parameters may be omitted when calling the procedure, however, if one reference cursor parameter is specified, all must be specified. For example, assume procedure Example2 is defined to have four parameters. Parameters 1 and 3 are reference cursor parameters and parameters 2 and 4 are character strings. The call can be specified in the following two ways:
{CALL RSET.Example2("Literal 1", "Literal 2")} In the following example, parameters 1 and 3 contain reference cursor parameters. {CALL RSET.Example2(?,"Literal 1", ?,"Literal 2")}Note: In versions of the Oracle8 ODBC Driver previous to release 8.1.5.3.0, each reference cursor parameter for a procedure or function had to contain parameter marker(s) on the call statement. The reference cursor parameter(s) did not need to be bounded by the calling application. If the calling application did bind the parameter, the Oracle8 ODBC Driver ignored it and bound the reference cursor parameter(s) internally.The following example application shows how to return a Result Set using the Oracle8 ODBC Driver:/** Sample Application using Oracle reference cursors via 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 ASTYPE 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 ASPROCEDURE EmpCurs(Ename IN OUT ename_cur, Mgr IN OUT mgr_cur, pjob IN VARCHAR2)ASBEGINIF NOT Ename%ISOPENTHENOPEN Ename for SELECT ename from emp;END IF;IF NOT Mgr%ISOPENTHENOPEN Mgr for SELECT mgr from emp where job = pjob;END IF;END;END;/** End PL/SQL for Reference Cursor.*//** Include Files*/#include <windows.h>#include <stdio.h>#include <sql.h>#include <sqlext.h>/** Defines*/#define JOB_LEN 9#define DATA_LEN 100#define SQL_STMT_LEN 500/** Procedures*/
oid 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*/lstrcpy( (char *) pUserName, DefUserName );lstrcpy( (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'){lstrcpy( (char *) pUserName, (char *) DefUserName );}/** Password*/printf ( "\nEnter Password Default [%s]\n", pPassWord);charptr = gets ((char *)PassWord);if (*charptr == '\0'){lstrcpy( (char *) pPassWord, (char *) DefPassWord );}/** Connection to the database*/rc = SQLConnect( hDbc,pServerName,(SQLSMALLINT) lstrlen((char *)pServerName),pUserName,(SQLSMALLINT) lstrlen((char *)pUserName),pPassWord,(SQLSMALLINT) lstrlen((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*/lstrcpy( (char *) pSqlStmt, "DROP PACKAGE ODBCRefCur");rc = SQLExecDirect(hStmt, pSqlStmt, lstrlen((char *)pSqlStmt));/** Create the Package Header*/lstrcpy( (char *) pSqlStmt, "CREATE PACKAGE ODBCRefCur AS\n");lstrcat( (char *) pSqlStmt, " TYPE ename_cur IS REF CURSOR;\n");lstrcat( (char *) pSqlStmt, " TYPE mgr_cur IS REF CURSOR;\n\n");lstrcat( (char *) pSqlStmt, " PROCEDURE EmpCurs (Ename IN OUT ename_cur,");lstrcat( (char *) pSqlStmt, "Mgr IN OUT mgr_cur,pjob IN VARCHAR2);\n\n");lstrcat( (char *) pSqlStmt, "END;\n");rc = SQLExecDirect(hStmt, pSqlStmt, lstrlen((char *)pSqlStmt));if (rc != SQL_SUCCESS){DisplayError(SQL_HANDLE_STMT, hStmt, "SQLExecDirect");}/** Create the Package Body*/lstrcpy( (char *) pSqlStmt, "CREATE PACKAGE BODY ODBCRefCur AS\n");lstrcat( (char *) pSqlStmt, " PROCEDURE EmpCurs (Ename IN OUT ename_cur,");lstrcat( (char *) pSqlStmt, "Mgr IN OUT mgr_cur, pjob IN VARCHAR2)\n AS\n BEGIN\n");lstrcat( (char *) pSqlStmt, " IF NOT Ename%ISOPEN\n THEN\n");lstrcat( (char *) pSqlStmt, " OPEN Ename for SELECT ename from emp;\n");lstrcat( (char *) pSqlStmt, " END IF;\n\n");lstrcat( (char *) pSqlStmt, " IF NOT Mgr%ISOPEN\n THEN\n");lstrcat( (char *) pSqlStmt, " OPEN Mgr for SELECT mgr from emp where job = pjob;\n");lstrcat( (char *) pSqlStmt, " END IF;\n");lstrcat( (char *) pSqlStmt, " END;\n");lstrcat( (char *) pSqlStmt, "END;\n");rc = SQLExecDirect(hStmt, pSqlStmt, lstrlen((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*/lstrcpy( (char *) pSqlStmt, "{CALL ODBCRefCur.EmpCurs(?)}");rc = SQLExecDirect(hStmt, pSqlStmt, lstrlen((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*/lstrcpy( (char *) pSqlStmt, "DROP PACKAGE ODBCRefCur");rc = SQLExecDirect(hStmt, pSqlStmt, lstrlen((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*/
oid DisplayError( SWORD HandleType, SQLHANDLE hHandle, char *Module )
하지만 이는 2003 이후나온 2005, 2008버전에서는 동작하지 않는다. 소스가 오픈되어있기에 누군가 나와 같은 생각을 했을거라 생각하고 CodeProject의 댓글을 뒤져보고 검색해본 결과 Google Code에서 2005, 2008에서 설치 가능한 KingsTools를 찾을 수 있었다.
fatal error C1189: #error : Building MFC application with /MD[d] (CRT dll version) requires MFC shared dll version. Please #define _AFXDLL or do not use /MD[d]
Debug와 Release 모드에서 서로 다른 프로젝트 옵션 설정으로 인한 오류
프로젝트의 속성을 서로 같게 설정하면 된다.
또는 "프로젝트 기본값 - MFC 사용(Project Defaults-Use of MFC)"의 속성이 다를 경우에도 나타난다.
Ambien and memory loss. Generic ambien. Ambien and the menstrual cycle. Buy ambien online cod. Ambien overnight. Ambien and hot flashes. Ambien ld-50. Ambien.
출처 : http://myhome.shinbiro.com/~fivenine/project/odbc/CDatabase.html
CDatabaseCDatabase class를 이용한 방법
Visual C++에서 대부분의 데이터베이스 프로그램을 개발한 경우에는
CRecordset class를 사용하게 된다. 왜냐하면 CRecordset class를 사용하면
테이블의 각 필드를 하나의 변수로 생각하고 프로그램을 하면 되기 때문이다.
그리고, 각각의 레코드를 처리하기가 매우 용이하다.
그러나, 레코드단위의 처리가 필요하지 않는 일종의 batch성 작업이면
CDatabase class로 보다 빠르게 처리할 수 있다. 예를 들어 10000건의 레코드가
존재하는데 그중 어떤 필드가 특정값인 레코드만 모두 삭제하고자 한다면 SQL문으로는
할 줄로 가능한 일인테도 CRecordset class를 사용하면 매우 길고 속도가 느린
프로그램이 된다.
아래의 예를 보면 보다 자세히 할 수 있다.
DELETE FROM MYTABLE WHERE AGE='19'
위와 같은 작업을 하는 프로그램을 ODBC를 이용해서 작성한다고 가정해보자.
SQL*PLUS로 접속해서 단 한줄만 입력하면 작업이 완료된다. 그러나, CRecordset
class를 이용하면 매우 긴 코드를 작성해야 한다. 하나의 레코드를 가져와서
AGE 필드이 값이 19인지를 확인하고 레코드를 삭제한다. 그리고, 다음 레코드를
가져와서 같은 작업을 하게 된다. 만약 레코드의 개수가 10000개이면 수행시간은
다음과 같다.
(레코드 읽는 메소드 + 비교문장 + 레코드삭제 메소드) * 10000 = ?
최소한 레코드를 읽는 메소드(함수와 같은 의미로 사용)와 레코드를
삭제하는 메소드는 각각 10000번씩 호출되어야 하고 if문을 이용하여 AGE 필드의
값을 비교하는 비교문도 10000번이 실행되어야 한다.
위의 예제의 특징을 살펴보면 프로그램내부에서 사용할 목적으로 레코드를
읽어오는것이 아니라는 점이다. 즉, 레코드의 값이 직접적으로 필요하지 않은
경우에는 보다 간단하고 고속으로 SQL문을 처리할 수 있는 방법이 있다.
Visual C++에서 ODBC를 이용하여 SQL문을 바로 실행할 수 있게 해주는
것이 CDatabase class이다.
CDatabase class는 data source와의 연결, SQL실행, 종료의 순서로 동작한다.
Data source와 연결하기 위해서는 Open메소드를 호출한다. 데이터베이스에
연결하기 위해서는 최소한 DSN,UID,PWD정보를 제공해야 한다. 데이터베이스이
이름과 사용자아이디, 비밀번호만 있으면 데이터베이스에 접속히 가능하다. SQL*PLUS를 이용하여 오라클에 연결할 때 입력하는 정보와 동일하다.
다만, SQL*PLUS에서는 DSN이 아니라 Oracle Alias를 이용한다는 것만 다르다.
Data source와의 연결을 해제하기 위해서는 Close 메소드를 호출한다.
이 메소드가 호출되면 데이터베이스와의 연결이 끊긴다. 더이상 데이터베이스를
사용하지 않겠다는 의미이다. 이 CDatabase와 관련된 CRecordset 객체를 먼저
Close해주어야 한다. 객체가 소멸하는 것은 아니기 때문에 다시 Open할 수 있다. 대기중인 AddNew와 Edit는 모두 취소된다. Transaction은 모두 rollback된다.
CDatabase::Close
virtual void Close( );
예제
m_dbEmployee.Close( );
완성된 소스코드
CDatabase m_dbEmployee; CString strSQL = "DELETE FROM MYTABLE WHERE AGE='19'";