실무에서 사용하고 있는 Pro*C!
C의 확장자는. c이고, Pro*C의 확장자는. pc이다.
Pro*C라는 건 일하게 되면서 처음 접하게 되었는데..
다음 프로젝트를 진행할때 참고할 수 있도록 제대로 정리를 해두어야겠다!
여러 사이트 정보를 참고하여 정리했지만, 틀린 정보일수 있음! > 틀린정보는 수정할예정!
Pro*C
Pro*C는 오라클 데이터베이스와 연동할 수 있는 C 프로그램이며, C 프로그램으로 처리할 수 있는 모든 기능을 Pro*C로도 구현할 수 있다
SQL 문은 절차형 언어가 아니기 때문에 오라클을 포함한 많은 데이터베이스는 PL/SQL이라는 절차형 언어를 제공한다. PL/SQL은 오라클 내부라는 한정된 공간에서 실행되는 도구인데, 이런 고민을 해결하기 위해서 대부분의 DBMS 벤더는 외부 C 프로그램과 결합할 수 있는 선행 컴파일러를 제공하고 있고, Pro*C는 오라클의 선행 컴파일러이다.
Pro*C는 혼자서 실행 파일을 만들어 낼 수없는 Pre Compiler(선행 컴파일러) 이다. 때문에 선행 컴파일을 통해서 실행 파일이 아닌 C 컴파일러가 인식할 수 있는 출력 파일 (확장자가 .c인 파일) 을 생성한다.
그렇게 생성된 C 프로그램은 C 프로그램의 통상적인 방법으로 컴파일된다.
기본 명령어
테이블 구조체 선언
EXEC SQL INCLUDE SQLCA.H
날짜 가져오기
char trans_dt[9]; // 날짜를 담을 변수
EXEC SQL SELECT TO_CHAR(SYSDATE-1, 'YYYYMMDD') INTO :trans_dt FROM DUAL
호스트 변수선언
- Pro*C에서 SQL문에 placeholder로 사용한 Host 변수를 선언한다.
- Pro*C 컴파일시 옵션 MODE=ORACLE 을 추가하면 DECLARE SECTION 없이 host 변수를 선언할 수 있다.
- 이때 이 데이터타입은 테이블을 정의할 때 사용되는 Oracle 데이터 타입과 일치할 필요는 없다.
호스트 변수의 조건
- 선언절에서 명시적으로 선언한다.
- SQL문 앞에 콜론 (:)을 붙인다.
- C문에서는 콜론을 붙이지 않는다.
- SQL예약어를 사용할 수없다.
호스트변수 사용 예시 - SELECT (단건 조회)
EXEC SQL BEGIN DECLARE SECTION;
int bnk_cd;
char bnk_nm[10];
EXEC SQL END DECLARE SECTION;
EXEC SQL SELECT BNK_CD
,BNK_NM
INTO :bnk_cd, :bnk_nm
FROM TM_BNK;
CURSOR 와 FETCH
CURSOR 와 FETCH
- CURSOR : SELECT 문을 통한 결괏값들을 저장할 저장할 메모리 공간. 모든 SQL문은 커서를 소유한다.
- FETCH : 커서에서 원하는 결과값을 추출하는 것
CURSOR
- 복수의 튜플들에 접근하는것이 가능하다. (단일 행은 into 절을 이용하여 변수에 저장하여 처리할 수 있다.)
- 첫 번째 튜플에 대한 포인터로 생각할 수 있다.
- 질의 결과로 반환되는 튜플들을 한 번에 하나씩 차례대로 처리할 수 있다.
- 단점 : 많은 데이터를 처리할 경우, SQL 의 성능을 떨어뜨린다.
CURSOR 명령어
- DECLARE : 커서를 정의한다.
- OPEN : 커서를 연다. 커서안의 검색이 실행된다. 아무런 데이터행을 추출하지 못해도 에러가 발생하진 않는다.
- FETCH : 현재 데이터 행을 OUTPUT 변수에 반환하여 한 라인씩 데이터를 FETCH 한다.
select 문의 컬럼의 수와, output 변수의 수가 동일해야 한다. - CLOSE : 사용을 마친 커서는 반드시 닫아주어야 한다.
CURSOR이용
char buf[500]; // 쿼리를 저장할 변수
EXEC SQL PREPARE statement FROM buf; // 쿼리하고자 하는 sql문장 정의
EXEC SQL DECLARE cursor_name CURSOR FOR statement; // 사용할 커서 선언
EXEC SQL OPEN cursor_name; // 선언한 커서 오픈
EXEC SQL FETCH cursor_name INTO :host_variable1, :host_variable2, :host_variable3, ....; // 열어둔 커서 실행
EXEC SQL CLOSE cursor_name; // 열었던 커서 닫음
- 예시
EXEC SQL INCLUDE SQLCA.H
EXEC SQL BEGIN DECLARE SECTION;
// 변수 선언
EXEC SQL END DECLARE SECTION;
char buf[QUERY_LENGTH];
// Query 세팅
sprintf(buf, 쿼리 ,TABLE_NAME);
EXEC SQL PREPARE PRE_TABLE_NAME FROM :buf;
EXEC SQL DECLARE RES_TABLE_NAME CURSOR FOR PRE_TABLE_NAME;
EXEC SQL OPEN RES_TABLE_NAME;
커서 닫기
EXEC SQL CLOSE cur_data;
- 커서는 열었다면 반드시 닫아주어야 한다.
단건조회와 대량처리
단건조회
EXEC SQL BEGIN DECLARE SECTION;
VARCHAR uid[80];
CHAR name[10];
CHAR job[32];
INT age;
EXEC SQL END DECLARE SECTION;
......
EXEC SQL SELECT NAME, JOB, AGE
INTO :name, :job, :age
FROM PERSON
WHERE UID = :uid;
if(sqlca.sqlcode != 0) { // 오류일경우
if(sqlca.sqlcode == 1403) {
// 조건에 맞는 데이터가 없는경우
return 0;
} else {
//에러 메세지 출력
fprintf(stderr, "%.*s\n", sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc);
return -1;
}
}
커서 FETCH - 대량처리
char buf[1024]; // 여러 건을 동시에 커리하여 DB의 데이터를 배열 형태로 가져오기 위한 변수.
EXEC SCL FETCH cur_data INTO :buf;
- while문으로 반복하여 사용한다.
EXEC SQL BEGIN DECLARE SECTION;
VARCHAR uid[80];
CHAR name[10];
CHAR job[32];
INT age;
EXEC SQL END DECLARE SECTION;
......
EXEC SQL AT :db_con1 DECLARE emp_cursor CURSOR FOR
SELECT NAME, JOB, AGE
FROM PERSON
WHERE UID = :uid;
EXEC SQL OPEN emp_cursor;
if(sqlca.sqlcode != 0) {
fprintf(stderr, "CURSOR OPEN ERROR: %.*s\n", sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc);
return -1;
}
while(1) {
EXEC SQL FETCH emp_cursor
INTO :name, :job, :age;
if(sqlca.sqlcode != 0) { // 오류일경우
if(sqlca.sqlcode == 1403) {
// 조건에 맞는 데이터가 없는경우
return 0;
} else {
//에러 메세지 출력
fprintf(stderr, "%.*s\n", sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc);
return -1;
}
}
printf("NAME: %s \t JOB: %ld \t AGE: %f\n", name, job, age);
}
sqlca.sqlcode
sqlca.sqlcode | 의미 |
sqlca.sqlcode == 0 | 정상 처리 |
sqlca.sqlcode == 1403 | 조회된 데이터가 없다 |
sqlca.sqlcode == 100 | insert 된 데이터가 없다 |
sqlca.sqlcode < 0 | 오류 |
sqlca.sqlerrd
sqlca.sqlerrd | 의미 |
sqlca.sqlerrd[2] | select 조회 건수 fetch 누적 조회 건수 insert, update, delete 건수 를 리턴 |
sqlca.sqlerrd[4] | sql문 오류가 발생한 위치 를 리턴 |
sqlca.sqlerrm
sqlca.sqlerrm | 의미 |
sqlca.sqlerrm.sqlerrml | 오류메시지의 길이 sqlca.sqlerrm.sqlerrmc에 저장된 문자열의 길이 |
sqlca.sqlerrm.sqlerrmc | 오류메시지 내용. 오류메시지는 Null terminate 문자열이 아니므로 반드시 sqlca.sqlerrm.sqlerrml로 크기를 체크해야 한다. |
오류처리
EXEC SQL WHENEVER ~를 이용한 오류 처리
EXEC SQL WHENEVER <condition> <action>;
- <condition> : Event 종류 상수로 SQLWARNING, SQLERROR, NOT FOUND 등이 있다.
- <action> : Event에 대한 처리 방법
EXEC SQL WHENEVER NOT FOUND CONTINUE;
- 데이터가 없다고 하더라도 계속해서 작업을 진행하라는 의미이다.
문자열 저장, 출력 함수
printf(), fprintf(), sprintf()의 차이
함수 | 의미 | 출력 위치 |
printf(서식문자열, 인자1 ....인자N) | 인자값을 포맷 스트링으로 표준출력한다 |
화면 (console) |
fprintf(파일, 서식문자열, 인자1 ...인자N) | 지정한 파일 (file) | |
sprintf(버퍼, 서식문자열, 인자1 ... 인자N) | 지정한 버퍼(buf) |
printf("%.*s", parameter1, parameter2);
- parameter1으로 지정한 길이만큼 parameter2 문자열을 출력
+ 추가
Fetch ~ into
EXEC SQL DECLEAR cur_resel_org CURSOR FOR
SELECT BNK_CD
,BNK_NM
FROM TM_BNK;
의 경우
EXEC SQL FETCH cur_resel_org
INTO :bnk_cd
,:bnk_nm;
으로 받을 수 있다.
IT 개발자 Note :: Pro*C 6. 대량처리 (배열처리) (it-note.kr)
랄라라 :: [번역] 런타임 에러 처리 (Handling Runtime Errors) (tistory.com)
C - C언어 함수 printf, fprintf, sprintf 의 차이점 알아보기 : 네이버 블로그 (naver.com)
pro*c 란 - 초보자를 위한 개념 설명 (tistory.com)
Oracle Pro*C 실무 프로젝트 활용서_2 - 하루에 하나씩 (itshinestar.co.kr)
https://lazyjin.tistory.com/entry/ProC01
https://blocked.tistory.com/396
https://jink1982.tistory.com/110
https://snowinblue.tistory.com/9
https://kikook.tistory.com/499
'DataBase > Oracle' 카테고리의 다른 글
[Oracle] 자릿수 채우기, 공백 채우기 - LPAD, RPAD (0) | 2022.11.20 |
---|---|
[Oracle] 조회 순번 매기기 - ROWNUM, ROW_NUMBER() (0) | 2022.11.20 |
[Oracle] NULL값을 치환해주는 함수 - NVL, NVL2, NULLIF (0) | 2022.11.20 |
[Oracle] 문자열 관련 함수 - SUBSTR, INSRT, REPLACE, TRANSLATE, CONCAT (0) | 2022.08.11 |
[Oracle] DML과 DECODE, CASE (0) | 2022.06.29 |