Upload
hubert-hampton
View
217
Download
0
Embed Size (px)
Citation preview
Oracle Embedded SQLOracle Embedded SQL
Pro*C : Chap 7Pro*C : Chap 7
작 성 자 : 김선영
메 일 : [email protected]
버 전 : 1.15
Copyright by SunYoung Kim
Copyright by SunYoung Kim <sunyzero (at) gmail (dot) com>
PrefacePreface
Oracle 은 Oracle 의 등록상표입니다 .
Pro*C 는 Oracle 의 제품입니다 .
예제는 지면 제약상 한줄에 여러라인이 코딩될 수 있습니다 .
본 자료에 대한 질문사항은 연락처를 참고해주시기 바랍니다 .
E-mail address
sunyzero (at) gmail (dot) com
sunyzero (at) empal (dot) com
Copyright by SunYoung Kim <sunyzero (at) gmail (dot) com>
Pro*C Dyanamic SQLPro*C Dyanamic SQL 특징
Static SQL 문과 달리 작성시에는 statement, variable type, # of variable 의 개수를 알 수 없는 경우에 사용함 .
반복적인 구문의 경우 , cache 로 올려 작업할 수 있게 함 .
» cache 에 올려지게 되면 SQL 구문을 해석하는 시간이 생략됨
시행 순서
» SQL 문 작성
» PREPARE : 구문을 해석해서 cache 로 올림
» EXECUTE : cache 에 올린 구문에 데이터를 맵핑하여 실행함
Copyright by SunYoung Kim <sunyzero (at) gmail (dot) com>
Pro*C Dyanamic SQL ( 계속 )Pro*C Dyanamic SQL ( 계속 ) select list item
SELECT 구문에서 지정한 출력 컬럼 리스트
» 아래의 경우는 NAME, AGE 가 해당함
» e.g. SELECT NAME, AGE FROM ...
placeholder
실제 SQL 구문이 실행될때 다른 호스트변수에 의해서 대체됨
입력 매개변수 (parameter) 로 사용됨
이름은 특별히 규칙이 없음 . 아래의 경우 v1, v2 가 해당함 .
» e.g. SELECT NAME, AGE FROM ... WHERE NAME like :v1 AGE > :v2
Copyright by SunYoung Kim <sunyzero (at) gmail (dot) com>
MethodsMethods Dynamic SQL 문은 4 가지의 method 를 지원함
Method 1 : SELECT 구문이 아닌 1 회성 실행을 하는 경우
» c.f.) cache 에 올라가는 작업이 아님
Method 2 : SELECT 구문이 아닌 반복적인 실행을 요하는 경우
Method 3 : 정적인 SELECT 구문의 반복적인 실행을 요하는 경우
Method 4 : 동적인 SELECT 구문의 반복적인 실행을 요하는 경우
Copyright by SunYoung Kim <sunyzero (at) gmail (dot) com>
Method 1Method 1 Method 1
SELECT 구문이 아닌 1 회성 실행을 하는 경우
EXECUTE IMMEDIATE 명령으로 즉시 실행하도록 함
» EXECUTE IMMEDIATE < host var. with SQL statement >
while (1) {
printf("input table name to remove (^d to exit): ");
fflush(stdout);
memset(c_str, 0, sizeof(c_str));
if (fgets(c_str, sizeof(c_str), stdin) == NULL) {
exit(EXIT_SUCCESS);
}
sprintf(sqlstmt, "DROP TABLE %s CASCADE CONSTRAINT", c_str);
EXEC SQL EXECUTE IMMEDIATE :sqlstmt;
}
while (1) {
printf("input table name to remove (^d to exit): ");
fflush(stdout);
memset(c_str, 0, sizeof(c_str));
if (fgets(c_str, sizeof(c_str), stdin) == NULL) {
exit(EXIT_SUCCESS);
}
sprintf(sqlstmt, "DROP TABLE %s CASCADE CONSTRAINT", c_str);
EXEC SQL EXECUTE IMMEDIATE :sqlstmt;
}
Copyright by SunYoung Kim <sunyzero (at) gmail (dot) com>
Method 2Method 2 Method 2
SELECT 구문이 아닌 반복적인 실행을 요하는 경우
» e.g.) INSERT, UPDATE 구문
select-item-list 와 placeholder 를 미리 지정함
PREPARE, EXECUTE 명령을 사용
» PREPARE statement FROM < SQL literal | host_var >;
» EXECUTE statment [ USING <input_host_var>;
Copyright by SunYoung Kim <sunyzero (at) gmail (dot) com>
Method 2 (con't)Method 2 (con't) "pr7_method2.pc"
text file 로부터 raw data 를 REGEX 로 추출하여 table 에 insert 하는 예제
#include "myproc.h"
#define MAX_EXPR_SUB_MATCH 10
#define DEF_FILENAME "regdata.txt"
#define REGEX_STR "^([a-zA-Z ]+)[|,]([0-9 ]+)[|,]([a-zA-Z ]+)[|,]+([a-zA-Z ]+)"
#include "sqlca.h"
#include "oraca.h"
EXEC ORACLE OPTION (ORACA=YES);
#define DB_USER "scott"
#define DB_PASS "xkdlrj"
typedef struct my_record {
unsigned short sno;
char name[30+1];
unsigned short gender;
unsigned short age;
char locale[20+1];
} MY_RECORD;
#include "myproc.h"
#define MAX_EXPR_SUB_MATCH 10
#define DEF_FILENAME "regdata.txt"
#define REGEX_STR "^([a-zA-Z ]+)[|,]([0-9 ]+)[|,]([a-zA-Z ]+)[|,]+([a-zA-Z ]+)"
#include "sqlca.h"
#include "oraca.h"
EXEC ORACLE OPTION (ORACA=YES);
#define DB_USER "scott"
#define DB_PASS "xkdlrj"
typedef struct my_record {
unsigned short sno;
char name[30+1];
unsigned short gender;
unsigned short age;
char locale[20+1];
} MY_RECORD;
Copyright by SunYoung Kim <sunyzero (at) gmail (dot) com>
Method 2 (con't)Method 2 (con't)
int prep_stmt(void);
int set_record(MY_RECORD *, const char *, const regmatch_t *);
int insert_rec(const MY_RECORD *s);
int main(int argc, char **argv) {
char *p_filename;
regex_t re_expr; /* posix regex patern buffer */
regmatch_t rm_matchtab[MAX_EXPR_SUB_MATCH]; /* pattern matching table */
int i, ret;
FILE *fp;
char errbuf[0xff], buf[0xff];
MY_RECORD a_rec;
if (argc != 2) {
printf("Using default filename!\n");
p_filename = DEF_FILENAME;
} else {
p_filename = strdup(argv[1]);
}
int prep_stmt(void);
int set_record(MY_RECORD *, const char *, const regmatch_t *);
int insert_rec(const MY_RECORD *s);
int main(int argc, char **argv) {
char *p_filename;
regex_t re_expr; /* posix regex patern buffer */
regmatch_t rm_matchtab[MAX_EXPR_SUB_MATCH]; /* pattern matching table */
int i, ret;
FILE *fp;
char errbuf[0xff], buf[0xff];
MY_RECORD a_rec;
if (argc != 2) {
printf("Using default filename!\n");
p_filename = DEF_FILENAME;
} else {
p_filename = strdup(argv[1]);
}
Copyright by SunYoung Kim <sunyzero (at) gmail (dot) com>
Method 2 (con't)Method 2 (con't)
if ((ret = regcomp(&re_expr, REGEX_STR, REG_EXTENDED|REG_NEWLINE))) {
regerror(ret, &re_expr, errbuf, sizeof(errbuf));
printf("Error regcomp() : %s\n", errbuf);
exit(EXIT_FAILURE);
}
printf("regcomp : %s\n", REGEX_STR);
/* practice2: remove WEHNEVER statement */
EXEC SQL WHENEVER SQLERROR DO branch_ora_err();
if (get_dbconn(DB_USER, DB_PASS, NULL)) {
fprintf(stderr, "Fail: get_dbconn\n");
exit(EXIT_FAILURE);
}
oraca.orastxtf = ORASTFERR; /* save SQL literal at error */
prep_stmt();
/* 파일 열기 부분은 생략됨 */
if ((ret = regcomp(&re_expr, REGEX_STR, REG_EXTENDED|REG_NEWLINE))) {
regerror(ret, &re_expr, errbuf, sizeof(errbuf));
printf("Error regcomp() : %s\n", errbuf);
exit(EXIT_FAILURE);
}
printf("regcomp : %s\n", REGEX_STR);
/* practice2: remove WEHNEVER statement */
EXEC SQL WHENEVER SQLERROR DO branch_ora_err();
if (get_dbconn(DB_USER, DB_PASS, NULL)) {
fprintf(stderr, "Fail: get_dbconn\n");
exit(EXIT_FAILURE);
}
oraca.orastxtf = ORASTFERR; /* save SQL literal at error */
prep_stmt();
/* 파일 열기 부분은 생략됨 */
Copyright by SunYoung Kim <sunyzero (at) gmail (dot) com>
Method 2 (con't)Method 2 (con't)
while (!feof(fp)) { /* Loop: extract raw data from file */
if (fgets(buf, sizeof(buf), fp) == NULL) break;
memset(rm_matchtab, 0x00, sizeof(rm_matchtab));
if (regexec(&re_expr, buf, MAX_EXPR_SUB_MATCH, rm_matchtab, 0)) {
printf("fail to match: (%.30s ...)\n", buf);
} else {
printf("* All Match offset:(%1$d->%2$d), len(%3$d) : %4$.*3$s\n",
rm_matchtab[0].rm_so, rm_matchtab[0].rm_eo,
rm_matchtab[0].rm_eo - rm_matchtab[0].rm_so,
&buf[rm_matchtab[0].rm_so]);
for (i=1; i<MAX_EXPR_SUB_MATCH; i++) {
if (rm_matchtab[i].rm_so == -1) break;
printf("\tSubmatch[%d] offset:(%2$d->%3$d), len(%4$d):%5$.*4$s\n",i,
rm_matchtab[i].rm_so, rm_matchtab[i].rm_eo,
rm_matchtab[i].rm_eo - rm_matchtab[i].rm_so,
&buf[rm_matchtab[i].rm_so]);
}
while (!feof(fp)) { /* Loop: extract raw data from file */
if (fgets(buf, sizeof(buf), fp) == NULL) break;
memset(rm_matchtab, 0x00, sizeof(rm_matchtab));
if (regexec(&re_expr, buf, MAX_EXPR_SUB_MATCH, rm_matchtab, 0)) {
printf("fail to match: (%.30s ...)\n", buf);
} else {
printf("* All Match offset:(%1$d->%2$d), len(%3$d) : %4$.*3$s\n",
rm_matchtab[0].rm_so, rm_matchtab[0].rm_eo,
rm_matchtab[0].rm_eo - rm_matchtab[0].rm_so,
&buf[rm_matchtab[0].rm_so]);
for (i=1; i<MAX_EXPR_SUB_MATCH; i++) {
if (rm_matchtab[i].rm_so == -1) break;
printf("\tSubmatch[%d] offset:(%2$d->%3$d), len(%4$d):%5$.*4$s\n",i,
rm_matchtab[i].rm_so, rm_matchtab[i].rm_eo,
rm_matchtab[i].rm_eo - rm_matchtab[i].rm_so,
&buf[rm_matchtab[i].rm_so]);
}
Copyright by SunYoung Kim <sunyzero (at) gmail (dot) com>
Method 2 (con't)Method 2 (con't)
if (set_record(&a_rec, buf, rm_matchtab)) {/* make tuple */
fprintf(stderr, "[%s:%d] FAIL: set_record()\n", __FILE__, __LINE__);
break;
}
if (insert_rec(&a_rec)) { /* insert to table */
fprintf(stderr, "[%s:%d] FAIL: insert_rec()\n", __FILE__, __LINE__);
break;
}
if (commit_work()) { /* commit work */
fprintf(stderr, "[%s:%d] FAIL: commit_work()\n", __FILE__, __LINE__);
exit(EXIT_FAILURE);
}
}
}
regfree(&re_expr); /* free memory for REGEX pattern buffer */
if (close_dbconn(1)) {
fprintf(stderr, "[%s:%d] FAIL: close_dbconn()\n", __FILE__, __LINE__);
}
return 0;
}
if (set_record(&a_rec, buf, rm_matchtab)) {/* make tuple */
fprintf(stderr, "[%s:%d] FAIL: set_record()\n", __FILE__, __LINE__);
break;
}
if (insert_rec(&a_rec)) { /* insert to table */
fprintf(stderr, "[%s:%d] FAIL: insert_rec()\n", __FILE__, __LINE__);
break;
}
if (commit_work()) { /* commit work */
fprintf(stderr, "[%s:%d] FAIL: commit_work()\n", __FILE__, __LINE__);
exit(EXIT_FAILURE);
}
}
}
regfree(&re_expr); /* free memory for REGEX pattern buffer */
if (close_dbconn(1)) {
fprintf(stderr, "[%s:%d] FAIL: close_dbconn()\n", __FILE__, __LINE__);
}
return 0;
}
Copyright by SunYoung Kim <sunyzero (at) gmail (dot) com>
Method 2 (con't)Method 2 (con't)
int prep_stmt(void)
{
EXEC SQL PREPARE S_EMPLIST FROM INSERT INTO EMPLIST VALUES
(SEQ_EMPLIST.NEXTVAL, :v1, :v2, :v3, :v4);
return 0;
}
int insert_rec(const MY_RECORD *s) {
EXEC SQL EXECUTE S_EMPLIST USING
:s->name, :s->gender, :s->age, :s->locale;
return SQLCODE;
}
#define COPY_RMTAB(dest, src, matchtab) memcpy(dest, &src[matchtab.rm_so], \
matchtab.rm_eo - matchtab.rm_so); \
dest[matchtab.rm_eo - matchtab.rm_so] = 0x0
int prep_stmt(void)
{
EXEC SQL PREPARE S_EMPLIST FROM INSERT INTO EMPLIST VALUES
(SEQ_EMPLIST.NEXTVAL, :v1, :v2, :v3, :v4);
return 0;
}
int insert_rec(const MY_RECORD *s) {
EXEC SQL EXECUTE S_EMPLIST USING
:s->name, :s->gender, :s->age, :s->locale;
return SQLCODE;
}
#define COPY_RMTAB(dest, src, matchtab) memcpy(dest, &src[matchtab.rm_so], \
matchtab.rm_eo - matchtab.rm_so); \
dest[matchtab.rm_eo - matchtab.rm_so] = 0x0
Copyright by SunYoung Kim <sunyzero (at) gmail (dot) com>
Method 2 (con't)Method 2 (con't)
int set_record(MY_RECORD *d, const char *sbuf, const regmatch_t *rmtab)
{
char buf[40];
/* name : 1st field => rmtab[1] */
COPY_RMTAB(d->name, sbuf, rmtab[1]);
/* gender: 3rd field => rmtab[3] */
COPY_RMTAB(buf, sbuf, rmtab[3]);
if (strncmp(buf, "Male", 4) == 0) { /* has logical error! */
d->gender = 1;
} else {
d->gender = 2; /* always '2'. what's problem? */
}
/* age : 2nd field => rmtab[2] */
COPY_RMTAB(buf, sbuf, rmtab[2]);
d->age = atoi(buf);
/* locale: 4th field => rmtab[4] */
COPY_RMTAB(d->locale, sbuf, rmtab[4]);
return 0;
}
int set_record(MY_RECORD *d, const char *sbuf, const regmatch_t *rmtab)
{
char buf[40];
/* name : 1st field => rmtab[1] */
COPY_RMTAB(d->name, sbuf, rmtab[1]);
/* gender: 3rd field => rmtab[3] */
COPY_RMTAB(buf, sbuf, rmtab[3]);
if (strncmp(buf, "Male", 4) == 0) { /* has logical error! */
d->gender = 1;
} else {
d->gender = 2; /* always '2'. what's problem? */
}
/* age : 2nd field => rmtab[2] */
COPY_RMTAB(buf, sbuf, rmtab[2]);
d->age = atoi(buf);
/* locale: 4th field => rmtab[4] */
COPY_RMTAB(d->locale, sbuf, rmtab[4]);
return 0;
}
Copyright by SunYoung Kim <sunyzero (at) gmail (dot) com>
Method 3Method 3 Method 3
select-list-item 과 input host variable 의 수를 지정하여 SELECT 구문을 실행
CURSOR 를 이용함
PREPARE, DECLARE CURSOR, OPEN CURSOR, FETCH, CLOSE CURSOR
» PREPARE statement FROM < SQL literal | host_var >;
» DECLARE <cursor_identifier> CURSOR FOR <sql | statememt>;
» OPEN <cursor_identifier> USING host_variable_list;
» FETCH <cursor_identifier> INTO <host_var> [ INDICATOR <ind_var> ];
» CLOSE <cursor_identifier>;
Copyright by SunYoung Kim <sunyzero (at) gmail (dot) com>
Method 3 (con't)Method 3 (con't) proc precompiler option
prefetch # : Cursor 를 open 했을때 prefetch 할 row 의 개수
» array 사용시 array 사용하는 크기와 같은 것이 좋음
sqlca.sqlerrd[2]
cumulative fetched row 가 기록됨
array-fetch 를 할때 중요하게 사용됨
Copyright by SunYoung Kim <sunyzero (at) gmail (dot) com>
Method 3 (con't)Method 3 (con't) "pr6_method3.pc"
#define MAX_ITERATION 50
struct e_member {
int idnum; char name[21]; short age; char desc[513]; int uniqid;
};
typedef struct e_member E_MEMBER;
void pr_em(const E_MEMBER *s); /* pr_em은 생략되었으니 앞의 예제를 참고합시다 */
int main(int argc, char *argv[])
{
int i, i_age; E_MEMBER p_em; char c_str[50], sqlstmt[1024];
if (argc != 3) {
printf("Usage : %s <username> <passwd>\n",argv[0]); return EXIT_SUCCESS;
}
/* get database connection */
if (get_dbconn(argv[1], argv[2], NULL)) {
return EXIT_FAILURE;
}
EXEC SQL WHENEVER SQLERROR DO branch_ora_err();
#define MAX_ITERATION 50
struct e_member {
int idnum; char name[21]; short age; char desc[513]; int uniqid;
};
typedef struct e_member E_MEMBER;
void pr_em(const E_MEMBER *s); /* pr_em은 생략되었으니 앞의 예제를 참고합시다 */
int main(int argc, char *argv[])
{
int i, i_age; E_MEMBER p_em; char c_str[50], sqlstmt[1024];
if (argc != 3) {
printf("Usage : %s <username> <passwd>\n",argv[0]); return EXIT_SUCCESS;
}
/* get database connection */
if (get_dbconn(argv[1], argv[2], NULL)) {
return EXIT_FAILURE;
}
EXEC SQL WHENEVER SQLERROR DO branch_ora_err();
Copyright by SunYoung Kim <sunyzero (at) gmail (dot) com>
Method 3 (con't)Method 3 (con't)
sprintf(sqlstmt, "SELECT IDNUM, NAME, AGE, DESCRIPT, UNIQID "
"FROM E_MEMBERS WHERE AGE < :v1");
/* declare cursor with statement */
EXEC SQL PREPARE S FROM :sqlstmt;
EXEC SQL DECLARE C CURSOR FOR S;
/* practice2: 정확한 수치를 찾는 것과 , >, < 에 대한 처리를 추가합시다 .
e.g. 사용자가 '>20' 을 입력하면 20 세 보다 큰 레코드를 검색
e.g. 사용자가 '20' 을 입력하면 정확하게 20 세인 경우만 검색 */
while (1) {
printf("* Search for less than your input age(^d to exit): ");
fflush(stdout);
memset(c_str, 0, sizeof(c_str));
if (fgets(c_str, sizeof(c_str), stdin) == NULL) {
exit(EXIT_SUCCESS);
}
i_age = atoi(c_str);
sprintf(sqlstmt, "SELECT IDNUM, NAME, AGE, DESCRIPT, UNIQID "
"FROM E_MEMBERS WHERE AGE < :v1");
/* declare cursor with statement */
EXEC SQL PREPARE S FROM :sqlstmt;
EXEC SQL DECLARE C CURSOR FOR S;
/* practice2: 정확한 수치를 찾는 것과 , >, < 에 대한 처리를 추가합시다 .
e.g. 사용자가 '>20' 을 입력하면 20 세 보다 큰 레코드를 검색
e.g. 사용자가 '20' 을 입력하면 정확하게 20 세인 경우만 검색 */
while (1) {
printf("* Search for less than your input age(^d to exit): ");
fflush(stdout);
memset(c_str, 0, sizeof(c_str));
if (fgets(c_str, sizeof(c_str), stdin) == NULL) {
exit(EXIT_SUCCESS);
}
i_age = atoi(c_str);
Copyright by SunYoung Kim <sunyzero (at) gmail (dot) com>
Method 3 (con't)Method 3 (con't)
/* open cursor */
EXEC SQL OPEN C USING :i_age;
/* practice1: use indicator for uniqid */
for(i=0; i<MAX_ITERATION; i++) {
/* practice3: using array-fetch (require proc prefetch option) */
EXEC SQL FETCH C
INTO :p_em->idnum,:p_em->name,:p_em->age,:p_em->desc,:p_em->uniqid;
if (SQLCODE == 1403) break;
pr_em(p_em);
}
printf("* fetched row counter: %d\n", sqlca.sqlerrd[2]);
EXEC SQL CLOSE C; /* deallocate cursor space */
}
close_dbconn(1); /* commit work and disconnect from DB */
return EXIT_SUCCESS;
}
/* open cursor */
EXEC SQL OPEN C USING :i_age;
/* practice1: use indicator for uniqid */
for(i=0; i<MAX_ITERATION; i++) {
/* practice3: using array-fetch (require proc prefetch option) */
EXEC SQL FETCH C
INTO :p_em->idnum,:p_em->name,:p_em->age,:p_em->desc,:p_em->uniqid;
if (SQLCODE == 1403) break;
pr_em(p_em);
}
printf("* fetched row counter: %d\n", sqlca.sqlerrd[2]);
EXEC SQL CLOSE C; /* deallocate cursor space */
}
close_dbconn(1); /* commit work and disconnect from DB */
return EXIT_SUCCESS;
}
Copyright by SunYoung Kim <sunyzero (at) gmail (dot) com>
Method 3 (con't)Method 3 (con't) adv. example: "pr6a_method3.pc"
앞의 예제에 다음 기능을 추가해봅시다 .
5 개씩 prefetch 하기 위해서 array 를 사용해봅시다 .
indicator 를 지정할 구조체도 동일하게 5 개씩 array 를 통해서 지정합시다 . (hint!)
#define N_REC_ARRAY 5
...
EXEC SQL OPEN C USING :i_age;
for(i=0, cnt_processed=0; i<MAX_ITERATION; i++) {
EXEC SQL FETCH C
INTO :p_em:p_em_ind;
for(j=0; j<N_REC_ARRAY && j<(sqlca.sqlerrd[2]-cnt_processed); j++) {
pr_em(&p_em[j], &p_em_ind[j]);
}
if (SQLCODE == 1403) break;
cnt_processed = sqlca.sqlerrd[2];
}
EXEC SQL CLOSE C;
#define N_REC_ARRAY 5
...
EXEC SQL OPEN C USING :i_age;
for(i=0, cnt_processed=0; i<MAX_ITERATION; i++) {
EXEC SQL FETCH C
INTO :p_em:p_em_ind;
for(j=0; j<N_REC_ARRAY && j<(sqlca.sqlerrd[2]-cnt_processed); j++) {
pr_em(&p_em[j], &p_em_ind[j]);
}
if (SQLCODE == 1403) break;
cnt_processed = sqlca.sqlerrd[2];
}
EXEC SQL CLOSE C;
Copyright by SunYoung Kim <sunyzero (at) gmail (dot) com>
Method 4Method 4 Method 4
select-list-item 과 input host variable 의 수를 모르는 경우
DESCRIBE 문을 이용함 : select-list-item 와 bind ( 변수저장 ) 정보를 SQLDA 를 이용하여 알아내고 이를 해석하여 데이터를 가져옴
장점
특정 위치로 스크롤이 가능함
memory 의 할당 및 모든 과정이 프로그래머에게 허용됨 .
단점
가장 복잡한 형태의 dynamic SQL
가장 사용 빈도수가 적음