커서를 사용한 프로그램을 본다면 아주 잘 된 프로그램 같아 보인다. 실제로도 아주 훌륭하게 동작하고 별 무리 없이 사용할 수 있는 프로그램이다. 그러나 여기에도 한가지 문제점이 있다. 바로 네트웍 트레픽을 잡아 먹고 그에 따른 시간이 많이 걸린다는 것이다. 이것은 어떻게 본다면 별 문제가 아닌거 같지만 패치할 행이 아~~~ 주 많은 경우에는 프로그램 성능면에서 상당한 문제를 일으킨다. 바로 전체 크기가 같더라도 큰 파일 하나를 복사하는 것 보다 수많은 작은 파일들을 복사하는 것이 더 오래걸리는 것과 같은 이유일 것이다. 이런 문제를 해결 할수 있는 가장 훌륭하다고 할 수 있는 프로그램이 바로 Array Processing이다. 이름을 봐서도 알 수 있듯이 한번에 한 행을 패치 해 오는 것이 아니라 한번에 여러 행을 패치해 오는 것이다. 예제를 통해서 자세히 보도록 하자.
/* --------------------------------------------------------------------------------
파일 이름 : array_select.pc
개발 일자 : 2002-10-28
작성자 : 류명환
-------------------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
/*
* $ORACLE_HOME/precomp/public/sqlca.h 를 포함해 주기 위해서
*/
exec sql include sqlca;
/*
* 호스트 변수 선언
*/
exec sql begin declare section;
/*
* 사용자 ID와 패스워드
*/
char *username;
char *password;
/*
* emp 테이블의 칼럼 리스트
* 받아올 테이블의 행의 수만큼 배열을 설정한다.
*/
int empno [14];
int mgr [14];
int sal [14];
int comm [14];
int deptno [14];
char ename [14][11];
char job [14][10];
char hiredate [14][10];
/*
* emp 테이블 칼럼들의 Indicator 변수
* 받아올 테이블의 행의 수만큼 배열을 설정한다.
*/
short ind_empno [14];
short ind_mgr [14];
short ind_sal [14];
short ind_comm [14];
short ind_deptno [14];
short ind_ename [14];
short ind_job [14];
short ind_hiredate [14];
exec sql end declare section;
/*
* SQL 에러 발생시 실행할 함수
* 에러코드를 프린트 하고 롤백 후에 접속을 종료한다.
* sqlca로 에러 검사.
*/
void sql_error (void)
{
printf ("SQL Error Code : %d\n", sqlca.sqlcode);
printf ("SQL Error Message : %s\n", sqlca.sqlerrm.sqlerrmc);
exec sql whenever sqlerror continue;
exec sql rollback work release;
exit (EXIT_FAILURE);
}
int main (void)
{
int i = 0;
char buf [256] = {'\0', };
username = (char *)strdup ("scott");
password = (char *)strdup ("tiger");
/*
* scott 사용자 계정으로 접속 시도
* 접속 시도 후에 성공 여부를 위해 에러를 검사한다.
* sqlca로 에러 검사.
*/
exec sql
connect :username identified by :password;
if (sqlca.sqlcode != 0)
{
sql_error ();
}
printf ("Connected to Oracle Database\n");
/*
* 해당 사원의 사원 정보를 가져온다.
* Indicator 변수를 붙여서 NULL이 있는지 검사한다.
* 이때 호스트 변수들이 전부 배열로 지정 되면 그 배열의 수만큼 자동으로 가져온다.
* 한번의 SQL문 실행으로 14개의 행을 모두 가져온다.
* sqlca를 통해서 에러를 검사한다.
*/
exec sql
select empno, ename, job, mgr, hiredate, sal, comm, deptno
into :empno:ind_empno, :ename:ind_ename, :job:ind_job, :mgr:ind_mgr,
:hiredate:ind_hiredate, :sal:ind_sal, :comm:ind_comm, :deptno:ind_deptno
from emp;
if (sqlca.sqlcode != 0)
{
sql_error ();
}
/*
* 칼럼의 이름을 프린트 하고 패치한 행의 수만큼 돌려서 프린트한다.
*/
printf ("EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO\n");
for (i = 0; i < 14; i ++)
{
/*
* Indicator 변수를 검사하여 -1 인 경우는 실제 데이터가 NULL 인
* 경우 이므로 숫자인 경우 -1을 문자인 경우 NULL을 찍어 준다.
*/
printf ("%5d %10s %9s %4d %9s %7d %7d %2d\n",
(ind_empno [i] == -1) ? -1 : empno [i],
(ind_ename [i] == -1) ? "NULL" : ename [i],
(ind_job [i] == -1) ? "NULL" : job [i],
(ind_mgr [i] == -1) ? -1 : mgr [i],
(ind_hiredate [i] == -1) ? "NULL" : hiredate [i],
(ind_sal [i] == -1) ? -1 : sal [i],
(ind_comm [i] == -1) ? -1 : comm [i],
(ind_deptno [i] == -1) ? -1 : deptno [i]);
}
/*
* 데이터베이스와 접속을 종료한다.
* sqlca 로 에러 검사.
*/
exec sql
rollback work release;
if (sqlca.sqlcode != 0)
{
sql_error ();
}
printf ("Disconnect from Oracle Database\n");
exit (EXIT_SUCCESS);
}
처음에 봤던 select 문과 다른점이 없다. 단지 호스트 변수를 선언할 때 배열로 선언하여 배열의 크기를 한번에 패치할 행의 수로 지정해 주면 된다. 별다른 네트웍 트래픽 없이 고도의 생산성 향상과 프로그램의 성능 향상을 가질 수 있다. 위의 경우 emp 는 총 14 행으로 구성되어 있으므로 배열 크기는 14이다. 만약에 테이블의 행의 수가 너무 많다면 배열로 선언 한 후에 다시 커서를 선언하여 한번에 여러행으로 패치해 오는 형식으로 하면 될 것이다. 커서와 Array Processing을 섞어서 사용하면 된다. 특별하게 어려울 것 없이 구성해 낼 수 있다. 커서를 사용할 경우 패치가 끝나게 되면 sqlca.sqlerrd [2] 를 셋팅하고 sqlcode를 1403으로 셋팅하게 된다. 여기서는 위에서 본 경우와 달리 마지막 패치가 성공하면서 sqlcode를 1403으로 셋팅하고 sqlerrd [2]의 값도 마지막 패치할때의 행의 수가 아니라 패치를 실행 할수록 행의 수는 쌓이게 된다. 그러므로 프로그램을 작성할 때 주의 하도록 하라. 실제 예제 코드가 아래에 있다.
/* --------------------------------------------------------------------------------
파일 이름 : array_cursor.pc
개발 일자 : 2002-10-28
작성자 : 류명환
-------------------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
/*
* $ORACLE_HOME/precomp/public/sqlca.h 를 포함해 주기 위해서
*/
exec sql include sqlca;
/*
* 호스트 변수 선언
*/
exec sql begin declare section;
/*
* 사용자 ID와 패스워드
*/
char *username;
char *password;
/*
* emp 테이블의 칼럼 리스트
* 받아올 테이블의 행의 수만큼 배열을 설정한다.
*/
int empno [5];
int mgr [5];
int sal [5];
int comm [5];
int deptno [5];
char ename [5][11];
char job [5][10];
char hiredate [5][10];
/*
* emp 테이블 칼럼들의 Indicator 변수
* 받아올 테이블의 행의 수만큼 배열을 설정한다.
*/
short ind_empno [5];
short ind_mgr [5];
short ind_sal [5];
short ind_comm [5];
short ind_deptno [5];
short ind_ename [5];
short ind_job [5];
short ind_hiredate [5];
exec sql end declare section;
/*
* SQL 에러 발생시 실행할 함수
* 에러코드를 프린트 하고 롤백 후에 접속을 종료한다.
*/
void sql_error (void)
{
printf ("SQL Error Code : %d\n", sqlca.sqlcode);
printf ("SQL Error Message : %s\n", sqlca.sqlerrm.sqlerrmc);
exec sql whenever sqlerror continue;
exec sql rollback work release;
exit (EXIT_FAILURE);
}
int main (void)
{
int i = 0;
char buf [256] = {'\0', };
username = (char *)strdup ("scott");
password = (char *)strdup ("tiger");
/*
* scott 사용자 계정으로 접속 시도
* 접속 시도 후에 성공 여부를 위해 에러를 검사한다.
*/
exec sql
connect :username identified by :password;
if (sqlca.sqlcode != 0)
{
sql_error ();
}
printf ("Connected to Oracle Database\n");
/*
* 해당 사원의 사원 정보를 가져오는 커서를 선언한다.
* sqlcode를 통해서 에러를 검사한다.
*/
exec sql
declare emp_cur cursor for
select empno, ename, job, mgr, hiredate, sal, comm, deptno
from emp;
if (sqlca.sqlcode != 0)
{
sql_error ();
}
/*
* 커서를 열고 sqlca로 에러를 검사한다.
*/
exec sql
open emp_cur;
if (sqlca.sqlcode != 0)
{
sql_error ();
}
/*
* 칼럼의 이름을 프린트 하고 패치한 행의 수만큼 돌려서 프린트한다.
*/
printf ("EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO\n");
while (1)
{
/*
* 한번에 5 행씩 패치해낸다.
* Indicator 변수를 붙여서 NULL이 있는지 검사한다.
* 패치한 후 sqlca를 통해 에러를 검사한다.
*/
exec sql
fetch emp_cur
into :empno:ind_empno, :ename:ind_ename, :job:ind_job, :mgr:ind_mgr,
:hiredate:ind_hiredate, :sal:ind_sal, :comm:ind_comm,
:deptno:ind_deptno;
if (sqlca.sqlcode != 1403 && sqlca.sqlcode != 0)
{
sql_error ();
}
/*
* 만약에 sqlcode 가 1403 으로 셋팅 된경우
* 마지막으로 패치한 행의 수를 계산하여 프린트 하고
* 루프를 종료한다.
*/
if (sqlca.sqlcode == 1403)
{
for (i = 0; i < sqlca.sqlerrd [2] % 5; i ++)
{
/*
* Indicator 변수를 검사하여 -1 인 경우는 실제 데이터가 NULL 인
* 경우 이므로 숫자인 경우 -1을 문자인 경우 NULL을 찍어 준다.
*/
printf ("%5d %10s %9s %4d %9s %7d %7d %2d\n",
(ind_empno [i] == -1) ? -1 : empno [i],
(ind_ename [i] == -1) ? "NULL" : ename [i],
(ind_job [i] == -1) ? "NULL" : job [i],
(ind_mgr [i] == -1) ? -1 : mgr [i],
(ind_hiredate [i] == -1) ? "NULL" : hiredate [i],
(ind_sal [i] == -1) ? -1 : sal [i],
(ind_comm [i] == -1) ? -1 : comm [i],
(ind_deptno [i] == -1) ? -1 : deptno [i]);
}
break;
}
else
{
for (i = 0; i < 5; i ++)
{
/*
* Indicator 변수를 검사하여 -1 인 경우는 실제 데이터가 NULL 인
* 경우 이므로 숫자인 경우 -1을 문자인 경우 NULL을 찍어 준다.
*/
printf ("%5d %10s %9s %4d %9s %7d %7d %2d\n",
(ind_empno [i] == -1) ? -1 : empno [i],
(ind_ename [i] == -1) ? "NULL" : ename [i],
(ind_job [i] == -1) ? "NULL" : job [i],
(ind_mgr [i] == -1) ? -1 : mgr [i],
(ind_hiredate [i] == -1) ? "NULL" : hiredate [i],
(ind_sal [i] == -1) ? -1 : sal [i],
(ind_comm [i] == -1) ? -1 : comm [i],
(ind_deptno [i] == -1) ? -1 : deptno [i]);
}
}
}
/*
* 데이터베이스와 접속을 종료한다.
*/
exec sql
rollback work release;
if (sqlca.sqlcode != 0)
{
sql_error ();
}
printf ("Disconnect from Oracle Database\n");
exit (EXIT_SUCCESS);
}
Array로 Select를 할 수 있다면 역시 Insert, Update, Delete도 할 수 있는 것이다. 지금까지 보아왔던 것처럼 Select보다는 DML작업이 더 쉬웠다. 이것도 단순히 배열을 만들고 값을 넣고 SQL문을 수행하기만 하면 된다. 아래에 예제가 있다. 5개의 행을 가진 배열을 만든 후 이것을 emp테이블에 Insert하고 Commit한다. 그 다음 Insert한 사원정보에서 급여를 20% 인상하고 역시 Commit한다. 마지막으로 Insert한 모든 사원을 Delete하고 Commit한다. 단순히 배열로 호스트 변수를 만들고 사용한 것 밖에는 없으니 천천히 살펴보도록 하라. 중간중간에 getchar()함수를 넣어 놓았다. 이 시점에 다른 터미널에서 sqlplus로 들어가 emp 테이블을 살펴보라. 데이터가 적용된 것을 볼 수 있을 것이다.
/* --------------------------------------------------------------------------------
파일 이름 : array_dml.pc
개발 일자 : 2002-10-28
작성자 : 류명환
-------------------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
/*
* $ORACLE_HOME/precomp/public/sqlca.h 를 포함해 주기 위해서
*/
exec sql include sqlca;
/*
* 호스트 변수 선언
*/
exec sql begin declare section;
/*
* 사용자 ID와 패스워드
*/
char *username;
char *password;
/*
* emp 테이블의 칼럼 리스트
* 작업할 행의 수만큼 배열을 설정한다.
*/
int empno [5] = {9000, 9001, 9002, 9003, 9004};
int mgr [5] = {7788, 7788, 7788, 7788, 7788};
int sal [5] = {3000, 2500, 2500, 2500, 2500};
int comm [5] = {300, 500, 500, 0, 0};
int deptno [5] = {40, 40, 40, 40, 40};
char ename [5][11] = {"Ryu", "Kim", "Lee", "Seo", "Son"};
char job [5][10] = {"Developer", "Developer", "Developer",
"Developer", "Developer"};
/*
* emp 테이블 칼럼들의 Indicator 변수
* 작업할 행의 수만큼 배열을 설정한다.
*/
short ind_empno [5] = {0, };
short ind_mgr [5] = {0, };
short ind_sal [5] = {0, };
short ind_comm [5] = {0, 0, 0, -1, -1};
short ind_deptno [5] = {0, };
short ind_ename [5] = {0, };
short ind_job [5] = {0, };
exec sql end declare section;
/*
* SQL 에러 발생시 실행할 함수
* 에러코드를 프린트 하고 롤백 후에 접속을 종료한다.
*/
void sql_error (void)
{
printf ("SQL Error Code : %d\n", sqlca.sqlcode);
printf ("SQL Error Message : %s\n", sqlca.sqlerrm.sqlerrmc);
exec sql whenever sqlerror continue;
exec sql rollback work release;
exit (EXIT_FAILURE);
}
int main (void)
{
int i = 0;
char buf [256] = {'\0', };
username = (char *)strdup ("scott");
password = (char *)strdup ("tiger");
/*
* scott 사용자 계정으로 접속 시도
* 접속 시도 후에 성공 여부를 위해 에러를 검사한다.
*/
exec sql
connect :username identified by :password;
if (sqlca.sqlcode != 0)
{
sql_error ();
}
printf ("Connected to Oracle Database\n");
/*
* 해당 사원의 사원 정보를 Insert 한다.
* Indicator 변수를 붙여서 NULL이 있는지 검사한다.
* 이때 호스트 변수들이 전부 배열로 지정 되면
* 그 배열의 수만큼 자동으로 Insert한다.
* 한번의 SQL문 실행으로 5개의 행을 모두 Insert 한다.
* sqlcode를 통해서 에러를 검사한다.
*/
exec sql
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (:empno:ind_empno, :ename:ind_ename, :job:ind_job, :mgr:ind_mgr,
sysdate, :sal:ind_sal, :comm:ind_comm, :deptno:ind_deptno);
if (sqlca.sqlcode != 0)
{
sql_error ();
}
/*
* Insert한 데이터를 Commit 한다.
*/
exec sql
commit work;
if (sqlca.sqlcode != 0)
{
sql_error ();
}
printf ("%d Array Inserting Succeeded\n", sqlca.sqlerrd [2]);
printf ("Press any key to continue\n");
getchar ();
/*
* 위에서 Insert 한 행의 급여를 20% 인상하여 업데이트 한다.
*/
exec sql
update emp
set sal = sal * 1.2
where empno = :empno;
if (sqlca.sqlcode != 0)
{
sql_error ();
}
/*
* Update한 데이터를 Commit한다.
*/
exec sql
commit work;
if (sqlca.sqlcode != 0)
{
sql_error ();
}
printf ("%d Array Updating Succeeded\n", sqlca.sqlerrd [2]);
printf ("Press any key to continue\n");
getchar ();
/*
* 위에서 Insert 한 행을 모두 Delete 한다.
*/
exec sql
delete from emp
where empno = :empno;
if (sqlca.sqlcode != 0)
{
sql_error ();
}
/*
* Delete한 데이터를 Commit한다.
*/
exec sql
commit work;
if (sqlca.sqlcode != 0)
{
sql_error ();
}
printf ("%d Array Deleting Succeeded\n", sqlca.sqlerrd [2]);
printf ("Press any key to continue\n");
getchar ();
/*
* 데이터베이스와 접속을 종료한다.
*/
exec sql
rollback work release;
if (sqlca.sqlcode != 0)
{
sql_error ();
}printf ("Disconnect from Oracle Database\n");
exit (EXIT_SUCCESS);
}
Array Processing
Recent Posts
Archive Posts
Tags