[번역] 호스트배열 (Host Arrays) - 03

Language/Pro*C 2015. 5. 20. 17:22

목 차
▣ 왜 배열을 사용하는가?
▣ 호스트 배열의 선언
▣ SQL문에서의 배열사용
▣ 배열로 Select하기
▣ 배열과 Insert하기
▣ 배열과 Update하기
▣ 배열과 Delete하기
▣ FOR절 사용
▣ WHERE절 사용
▣ 배열 구조체
▣ Mimicking CURRENT OF

8) FOR절 사용
: DELETE,EXECUTE,FETCH,INSERT OPEN UPDATE문의 의해서 처리되는 배열의 크기를
설정하기위해서 FOR절을 사용할 수 있다.
FOR절은 특히 UPDATE,INSERT,DELETE문에서 특히 유용하게 사용될 수 있다.
이런문을 사용할때 전체배열크기를 사용하기를 원하지 않을경우 FOR절은 사용할
크기를 제한 할 수 있다. 아래에 예제에서 이를 보여주고 있다.

char emp_name[100][20];
float salary[100];
int rows_to_insert;
/* 호스트 배열을 채운다. */
rows_to_insert = 25; /* FOR절 변수를 설정 */
EXEC SQL FOR :rows_to_insert /* 25개행을 처리할 것이다. */
INSERT INTO emp (ename, sal)
VALUES (:emp_name, :salary);

아래의 예제는 허용되지 않는 표현으로 FOR절을 설정한 경우이다.
EXEC SQL FOR :rows_to_insert + 5 /* illegal */
INSERT INTO emp (ename, empno, sal)
VALUES (:emp_name, :emp_number, :salary);

8-1) FOR절에대한 제한사항
: SELECT문에서 FOR절 혹은 CURRENT OF절과 함께 사용할 수는 없다.
8-1-1) SELECT 문에서의 사용
: SELECT문에서 FOR절을 사용하면, 에러메세지를 얻는다.
그것의 의미가 불분명하기때문에 FOR절은 SELECT문에 허용되는 않는다.
"SELECT문을 n번 실행할 것인가?" 혹은 "SELECT문을 한번 실행하고 n개 행을 얻을
것인가?"하는 두가지 의미로 해석될 수 있다.
전자는 각실행이 여러행을 리턴할 경우이며, 후자의 경우는, 커서를 선언하여 사용
하는것이 낮고 FETCH문에 FOR절을 사용하는 예제는 아래와같다 :

EXEC SQL FOR :limit FETCH emp_cursor INTO ...

8-1-2) CURRENT OF절과 함께 사용
: 아래의 예제는 FETCH문에의해서 리턴된 최근의 행을 참조하기위해서 UPDATE/
DELETE문에서 CURRENT OF 절을 사용할 수는 예제이다.

EXEC SQL DECLARE emp_cursor CURSOR FOR
SELECT ename, sal FROM emp WHERE empno = :emp_number;
...
EXEC SQL OPEN emp_cursor;
...
EXEC SQL FETCH emp_cursor INTO :emp_name, :salary;
...
EXEC SQL UPDATE emp SET sal = :new_salary
WHERE CURRENT OF emp_cursor;

그러나,CURRENT OF 절과 FOR절을 함께 사용할 수 없다.
아래의 문장은 유효하지 않은 예이다.

EXEC SQL FOR :limit UPDATE emp SET sal = :new_salary
WHERE CURRENT OF emp_cursor;
...

EXEC SQL FOR :limit DELETE FROM emp
WHERE CURRENT OF emp_cursor;

9) WHERE절의 사용
: 오라클은 n개의 배열크기를 가진 호스트 배열이 포함된 SQL문을 SQL이 n번 실행되어야
한다고 처리한다. 하지만 전처리기는 의미가 모호할 경우 에러 메세지를 발생시킨다.
예를들어, 아래의 선언이 있다고 가정해보자.
int mgr_number[50];
char job_title[50][20];
EXEC SQL SELECT mgr INTO :mgr_number FROM emp
WHERE job = :job_title;

위의 문장은 아래의 문장으로 간주된다.

for (i = 0; i < 50; i++)
SELECT mgr INTO :mgr_number[i] FROM emp
WHERE job = :job_title[i];

여러행이 WHERE절에 만나게 되기 때문에 에러 메세지가 발생하게 된다.

반면, 아래의 문은 모호하지 않다.

EXEC SQL UPDATE emp SET mgr = :mgr_number
WHERE empno IN (SELECT empno FROM emp
WHERE job = :job_title);

위의 문장은 아래의 문 처럼 처리한다.

for (i = 0; i < 50; i++)
UPDATE emp SET mgr = :mgr_number[i]
WHERE empno IN (SELECT empno FROM emp
WHERE job = :job_title[i]);

10) 배열 구조체
: 배열구조체는 여러컬럼을 포함하는 한행 연산을 처리할 수 있도록 해준다.
10-1) 배열구조체 사용시 제한사항
- PL/SQL Block내에서는 사용 불가
- WHERE,FROM 절에 사용불가
- UPDATE문의 SET절에 사용불가
10-2) PRO*C/C++에서 배열구조체 사용시 유의 사항
- 구조체 TAG을 반드시 선언해서 사용할것
- 구조체의 MEMBER는 CHAR과 VARCHAR을 제외하고 배열이 있어서는 않된다.
- CHAR나 VARCHAR는 다자원배열을 사용할 수 없다.

아래의 예제는 사용할 수 없는 경우의 선언이다.

struct { /* 구조체 tag이 선언되지 않음 */
int empno[15]; /* 구조체 member는 배열이 될 수 없음 */
char ename[15][10]; /* CHAR는 2차원 배열이 될 수 없다 */
struct nested {
int salary; /* 구조체 배열에 구조체가 사용될 수 없다 */
} sal_struct;
} bad[15];

아래의 예제는 배열이 합법적인 것과 비합법적인것을 보여주고 있다.

int empno[15];
exec sql var empno[3] is integer(4); /* illegal */
myint empno[15]; /* legal */

10-3) 배열구조체의 포인터 선언
: 몇몇의 경우에는 배열구조체의 포인터를 선언해서 사용해야 바람직한 경우가
있다. 직접적으로 SQL문에 사용하는 경우나 혹은 다른 함수에 인자로 넘기는
경우 배열 포인터를 사용 할 수 있다.

SELECT문에 FOR절을 사용할 수 없기때문에, 구조체배열 포인터에 값을 추출하기
위해서 명시적인 커서,FETCH문이 FOR절에 사용되어야만 한다.

10-4) 예제
: 아래와같이 구조체를 선언한다.

struct department {
int deptno;
char dname[15];
char loc[14];
} my_dept[4];

위의 구조체를 이용해서 다음과 같이 사용할 수 있다.

exec sql select * into :my_dept from dept;

exec sql insert into dept values (:my_dept);

지시자 변수를 사용하기위해서 아래와 같은 구조체를 선언해서 사용할 수 있다.

struct deptartment_ind {
short deptno_ind;
short dname_ind;
short loc_ind;
} my_dept_ind[4];

지시자변수의 사용법은 아래와 같다.

exec sql select * into :my_dept indicator :my_dept_ind from dept;
exec sql insert into dept values (:my_dept indicator :my_dept_ind);

배열구조체와 SCALAR ARRAY(첨자가숫자로 주어지는 배열)의 혼용예제를
살펴보자.
구조체와 SCALAR ARRAY를 아래와 같이 선언한다.

struct employee {
int empno;
char ename[11];
} emp[14];

float sal[14];
float comm[14];

위와같이 혼용하기위해서는 SCALARY ARRAY와 구조체 배열의 크기가 같아야한다.
SELECT시 예제는 아래와 같다.

exec sql select empno, ename, sal, comm into :emp, :sal, :comm from emp;

지시자의 선언은 아래와 같이 사용하면 된다.

short comm_ind[14];
...

exec sql select empno, ename, sal, comm
into :emp, :sal, :comm indicator :comm_ind from emp;

커서를 사용한 여러개의 구조체 배열사용예에 대해서 살펴보자.

예를 들어, 아래와 같이 구조체가 선언되어 있다고 가정하자.

struct employee {
int empno;
char ename[11];
char job[10];
} emp[14];

struct compensation {
int sal;
int comm;
} wage[14];

struct compensation_ind {
short sal_ind;
short comm_ind;
} wage_ind[14];

위의 구조체를 사용하는 예제는 다음과 같다.

exec sql declare c cursor for
select empno, ename, job, sal, comm from emp;

exec sql open c;
exec sql whenever not found do break;
while(1)
{
exec sql fetch c into :emp, :wage indicator :wage_ind;

... process batch rows returned by the fetch ...
}

printf("%d rows selected.n", sqlca.sqlerrd[2]);

exec sql close c;

다음으로 구조체 포인터에 대해서 사용하는 예를 살펴 보도록 하자.
아래와 같이 구조체를 선언한다.

typedef struct dept {
int deptno;
char dname[15];
char loc[14];
} dept;

아래의 예제는 함수에 인자로 구조체배열포인터를 넘겨 처리하는 과정을
보여주고 있다.

void insert_data(d, n)
dept *d;
int n;
{
exec sql for :n insert into dept values (:d);
}
void fetch_data(d, n)
dept *d;
int n;
{
exec sql declare c cursor for select deptno, dname, loc from dept;
exec sql open c;
exec sql for :n fetch c into :d;
exec sql close c;
}

위의 함수는 아래의 예처럼 사용할 수 있다.

dept d[4];
dept *dptr = &d[0];
const int n = 4;
fetch_data(dptr, n);
insert_data(d, n); /* We are treating ’&d[0]’ as being equal to ’d’ */

구조체배열 포인터에 정보를 취득하기위해서 아래와 같이 사용할 수 있다.

exec sql for :n insert into dept values (:dptr);

위의 문장에서 가장 중요한 것은 FOR절의 사용임을 명심해야한다.

11) Mimicking CURRENT OF(CURRENT OF의 모방(?) )
: 커서를 사용해서 FETCH한 최근의 행에서 UPDATE나 DELETE에 CURRENT OF CURSOR절을
사용한다. 그라나 호스트배열과 CURRENT OF 절은 사용할 수 없으며 대신, 각행의
ROWID를 선택하고, UPDATE나 DELETE하는 동안 현재행을 인식하기위해서 그값을 사용할
수 있다.
예제를 보면 다음과 같다.
char emp_name[20][10];
char job_title[20][10];
char old_title[20][10];
char row_id[20][19];
...
EXEC SQL DECLARE emp_cursor CURSOR FOR
SELECT ename, job, rowid FROM emp;
...
EXEC SQL OPEN emp_cursor;
EXEC SQL WHENEVER NOT FOUND do break;
for (;;)
{
EXEC SQL FETCH emp_cursor
INTO :emp_name, :job_title, :row_id;
...
EXEC SQL DELETE FROM emp
WHERE job = :old_title AND rowid = :row_id;
EXEC SQL COMMIT WORK;
}
그러나 FETCH된 행은 FOR UPDATE OF 절이 사용되지 않았기 때문에 LOCK이 걸리지 않는다.
(CURRENT OF없이 FOR UPDATE OF절을 사용할 수 없다.) 그래서 정보에 대해서 일치되지
않은 결과를 얻을 수 도 있다.



출처 - http://younbok.egloos.com/9342716

: