[ Oracle - Pro*C ]   

 

 

 Pro*C 

 

 
 
-. 오라클 데이타베이스 내의 테이블에 존재하는 레코드들을 조회,수정 등의 데이타 처리를 하고, 
   C프로그램으로 전환하기 위해 사용함.
-. C와의 차이점 
   -. 외형상의 차이 : 확장자가 .pc로 끝난다.
   -. 기본적으로 C문법을 따르며, C프로그램코드에 DB에 접속하여 레코드들을 처리하는 과정이 추가되어 
      작성된다.
      즉, C로 작성된 프로그램코드에 오라클의 SQL의 DML or DDL명령문이 'EXEC'구문과 결합된 형태가
       추가된 것임을 알 수 있다.
   -. 변수선언에서 C언어에서는 사용하지 않는, .arr과 .len이라는 변수가 컴파일된 C source코드에 
      생성되어 있는 것을 확인할 수 있다.

-. 컴파일 과정
   -. filename.pc -> filename.c -> filename.o -> filename*(생성된 오브젝트 파일을 링크해서 
      실행파일을  만드는 과정이다.)
 
-. 실제 컴파일
 -. makefile 위치 
 $ORACLE_HOME에 proc/에 보면 proc.mk파일이 있다.
 
 이중에서 
 1) USERID = uinfo/mation
  -. 오라클 DB에 접속할때 사용할 사용자 아이디와 비밀번호 지정
  
 2) DEST = /home/elfsun/simple/bin
 -. 컴파일후 실행파일을 복사할 디렉토리
 
 3) COMM_OBJ = /home/elfsun/simple/lib/lib1.o\
        /home/elfsun/simple/lib/lib2.o\
        /home/elfsun/simple/lib/lib3.o\
        /home/elfsun/simple/lib/lib4.o
 -. 컴파일시 참조로할 오브젝트 파일들.
 \(백슬레시)로 구분함.
 
 4) all : $(PROGRAM)
 -. 프로그램의 이름을 지정할 수 있다. 이것은 프로그램의 소스 이름이 되며, 컴파일후 생성될 
    실행 파일의 이름이 되기도 한다.
 
 5) install :
 -. 컴파일후 생성된 실행 파일을 복사해주는 작업을 수행
 
 6) clean : 
 -. 컴파일 과정에서 생성되는 C파일과 오브젝트 파일들을 삭제할때 수행
 
 이들중 수행할 각 명령들은 '@'바로 뒤에 작성해 줄 수 있다.
 
 [실제 컴파일 수행 - makefile이름이 proc.mk인 경우]
 
 1) 실제 컴파일을 하기 위해
  $ make -f proc.mk
 
 2) 컴파일한 실행 파일을 ~/bin/ 디렉토리에 복사하기 위해 
  $ make -f proc.mk install
 
 3) 컴파일 후 생성되는 오브젝트 파일과 c파일을 삭제하기 위해
  $ make -f proc.mk clean
  
-. SQLCA관련 변수들...
 -. sqlca.sqlerr[0] : fetch를 사용하여 쿼리된 레코드의 건수를 리컨시켜 주는 역할을 하는 
                      변수이다.
 -. sqlca.sqlwarn : 경고에 대한 정보들을 지니고 있는 변수이다.
 -. sqlca.sqlerrm.sqlerrmc : sql 명령수행중 에러가 발생했을때, 발생한 에러에 대한 내용과 오류 
                            코드번호에 관한 정보를 출력시켜 준다.
    ---------------------------------------
 -. sqlca.sqlcode값        내 용
    ---------------------------------------
    0  sql문이 정상적으로 수행
    
    1403  유일하게 양수값을 갖는 에러코드임.
      조건에 맞는 데이타가 하나도 없을때.
    
    -1405 조회된 값중 null값이 있을때
    ---------------------------------------


-. host변수 선언
 
 -. 호스트변수는 데이타베이스에서 데이타 처리 결과를 저장하기 위해 사용되며, 응용 프로그램내에서는 
    변수로서의 역할을 한다. 따라서 호스트 변수를 선언할 때에는 데이타베이스 내에서 사용되는 칼럼의
    데이타 타입과 그 크기를 고려하여 선언되어야 하며, 프리컴파일 과정을 통해 응용프로그램내에서도
    사용가능한 형태이어야 한다.
 ex)
 EXEC SQL BEGIN DECLARE SECTION;
  varchar username[16], userarea[10];
  int usrno, usrage;
 EXEC SQL END DECLARE SECTION;    
 
 [주의] 
 1) 호스트 변수 선언시에 오라클 예약어를 사용하지 말것.
 2) 호스트 변수에서는 null처리를 해주지 않기 때문에 응용 프로그램에서 반드시 null처리를 해주어야 한다. 
    단, 호스트 변수가 숫자형인 경우에는 예외이다.
   
-. DB접속
 방법1>
 EXEC SQL CONNECT elfsun
  INDENTIFIED BY 1234;
  
 방법2>
 EXEC SQL BEGIN DECLARE SECTION;
  varchar userid[8], passwd[8];
 EXEC SQL END DECALRE SECTION;    
 
 strncpy(userid.arr,"elfsun",6);
 strncpy(passwd.arr,"1234",4);
 
 EXEC SQL CONNECT :userid;
  IDENTIFIED BY :passwd;
 
 -. 일반적으로 방법2를 더 선호함.
 -. 방법2를 쓸때에 변수명 앞에는 반드시 :를 붙여서 사용하고 있는 변수가 호스트 변수임을 
    명시해야 한다.
 
-. CURSOR이용 : 쿼리문으로 조회되는 레코드가 한 건 이상일때 사용함.

 형식> 
 1) EXEC SQL PREPARE statement FROM variable;  (쿼리하고자 하는 sql문장 정의)
 2) EXEC SQL DECLARE cursor_name CURSOR FOR statement; ( 사용할 커서 선언 )
 3) EXEC SQL OPEN cursor_name;   ( 선언한 커서 열기 )  
 4) EXEC SQL FETCH cursor_name INTO :host_variable1, :host_variable2, :host_variable3, ....;  
                                 ( 열어둔 커서 실행 )
 5) EXEC SQL CLOSE cursor_name;  ( 열었던 커서 닫음 )
 

 1) EXEC SQL PREPARE statement FROM variable;  (쿼리하고자 하는 sql문장 정의)
  ex)
  char SqlCmd[100];    (변수 SqlCmd를 선언)
  memset(SqlCmd,0x00,sizeof(SqlCmd));  ( memset함수를 이용하여 초기화 작업함)
  sprintf(SqlCmd,"SELECT usrno, usrname, usrarea FROM forumlist");  (쿼리문을 SqlCmd에 대입함)
  EXEC SQL PREPARE st_1 from :SqlCmd; (EXEC..문장을 이용하여 쿼리할 구조를 정의함)
 2) EXEC SQL DECLARE cursor_name CURSOR FOR statement; ( 사용할 커서 선언 )
  형식1>
  EXEC SQL DECLARE cursor_name CURSOR FOR statement;
  형식2>
  EXEC SQL DECLARE cursor_name CURSOR FOR select 문장;
  
  -. cursor_name : 사용할 커서명
  ex)
  EXEC SQL DECLARE CUR_1 CURSOR FOR ST_1; (ST_1은 PREPARE에서 정의한것과 동일해야 함)
  EXEC SQL DECLARE CUR_1 CURSOR FOR select usrno, username, userarea From forumlist;

 3) EXEC SQL OPEN cursor_name;   ( 선언한 커서 열기 ) 
  ex)
  EXEC SQL OPEN CUR_1;
  
 4) EXEC SQL FETCH cursor_name INTO :host_variable1, :host_variable2, :host_variable3, ....;  
                      ( 열어둔 커서 실행 )
  ex)
  EXEC SQL BEGIN DECLARE SECTION;
   varchar usrname[16], usrarea[10];
   int usrno;
  EXEC SQL END DECLARE SECTION;
   ......
   
  EXEC SQL FETCH CUR_1 INTO :usrno, :usrname, :userarea;
  
  -. 여기서 사용하는 조회된 칼럼들과 1대1로 대응되어야 한다.
  또한 사용될 호스트 변수들은 이미 이전에 선언이 되어 있어야만 사용이 가능하다. 
  칼럼의 데이타 타입과 크기가 일치해야함에도 주의.

 5) EXEC SQL CLOSE cursor_name;  ( 열었던 커서 닫음 )
  ex) 
  EXEC SQL CLOSE CUR_1;

-. 오류 처리
 1) whenever sqlerror 구문 
  형식)
  ① EXEC SQL WHENEVER SQLERROR GOTO stop_st;
  
  ② EXEC SQL WHENEVER SQLERROR GOTO STOP;
  
  ③ EXEC SQL WHENEVER SQLERROR GOTO CONTINUE;
   
  -. 위 문장들은 SQL문장의 수행 결과 리턴되는 sqlca.sqlcode의 값이 음수로서, 에러를 발생시키는 경우, 더이상
  프로그램을 진행시키지 않도록 stop_st루틴(①번에 해당)으로 분기하여 프로그램의 에러처리를 하도록 하는 문장임.
  stop_st대신 STOP나 CONTINUE를 쓰기도 한다.
  
  -. 또한 실행시켰던 SQL문장들이 레코드의 값 자체의 변경과 관련이 있는 문장들, 즉 INSERT, DELETE, UPDATE와 같은
  SQL문장들이었다면, 'EXEC SQL ROLLBACK WORK'명령을 이용하여 그 트렌잭션을 취소시키는 과정을 추가해 주어야 한다.
  
 2) whenever not found 구문
  형식)
  EXEC SQL WHENEVER NOT FOUND GOTO stop_st;
  
  EXEC SQL WHENEVER NOT FOUND STOP;
  
  EXEC SQL WHENEVER NOT FOUND CONTINUE;
  
  -. 위 문장들은 SQL문장 실행 결과 sqlca.sqlcode값이 1403으로 조건에 맞는 레코드 건수가 
     하나도 없는 경우이다.
  
-. ProC내에서의 select 문장
 ex1)
 -------------------------------------------------------------
 EXEC SQL BEGIN DECALRE SECTION;
  varchar usrname[16], usrarea[10];
  int usrno;
 EXEC SQL END DECLARE SECTION;
 
 EXEC SQL SELECT usrno, usrname, usrarea
  INTO : usro, :usrname, :usrarea
  FROM forumlist
  WHERE usrno=5;
 
 EXEC SQL WHENEVER SQLERROR GOTO stop_st;
 
 EXEC SQL COMMIT WORK;
 
 printf("\n 회원의 고유번호 =%d",usrno);
 printf("\n 회원의 이름  =%d",usrname.arr);
 printf("\n 회원의 이름(크기) =%d BYTE", usrname.len);
 printf("\n 회원의 거주지역 =%s", usrarea.arr);
 printf("\n 회원의 거주지역(크기) =%d BYTE", usrarea.len);
 
 stop_st :
 printf("\n 에러메시지 =%s", sqlca.sqlerrm.sqlerrmc);
 -------------------------------------------------------------
 -. into 구문은 쿼리 결과에 만족하는 데이타를 검색해 와서 into구문 뒤의 변수에 각각 할당한다.
 -. 호스트 변수명 앞에 (:)을 붙여 줌으로써 조회할 테이블이 칼럼명과 구분을 지어 준다.
 
 ex2)
 -------------------------------------------------------------
 EXEC SQL BEGIN DECALRE SECTION;
  varchar usrname[16], usrarea[10];
  int usrno;
  char SqlCmdp100];
 EXEC SQL END DECLARE SECTION;
 
 memset(SqlCmd,0x00,sizeof(SqlCmd));
 sprintf(Sqlcmd,"select usrno, usrname, usrarea from forumlist");
 EXEC SQL PREPARE ST_1 FROM :SqlCmd;
 EXEC SQL DECLARE CUR_1 FOR ST_1;
 EXEC SQL OPEN CUR_1;
 EXEC SQL FETCH SUR_1 INTO :usrno, :usrname, :usrarea;
 if (sqlca.sqlcode < && sqlca.sqlcode != -1405)
 {
  printf("\n 에러메시지 = %s", sqlca.sqlerrm.sqlerrmc);
  printf("\n 레코드 조회시 에러 발생!");
  EXEC SQL CLOSE CUR_1;
  EXEC SQL ROLLBACK WORK;
  exit();
 } /* error */
 else if(sqlca.sqlcode == 1403)
 {
  printf("\n 에러메시지 ="%s", sqlca.sqlerrm.sqlerrmc);
  printf('\n 조건에 맞는 데이타가 없슴다!");
  EXEC SQL CLOSE CUR_1;
  EXEC SQL ROLLBACK WORK;
  exit();
 } /* not exist */
 while(sqlca.sqlcode ==0)
 {
  printf("\n 회원의 고유번호 =%d",usrno);
  printf("\n 회원의 이름  =%d",usrname.arr);
  printf("\n 회원의 이름(크기) =%d BYTE",usrname.len);
  printf("\n 회원의 거주지역 =%s", usrarea.arr);
  printf("\n 회원의 거주지역(크기) =%d BYTE", usrarea.len);
  EXEC SQL FETCH CUR_1 INTO :usrno, :usrname, :usrarea;
 } /* while */
 printf("\n 오라클 메시지 = %s",sqlca.sqlerrm.sqlerrmc);
 printf("\n 레코드가 성공적으로 조회되었습니다.");
 EXEC SQL CLOSE CUR_1;
 EXEC SQL COMMIT WORK;
 -------------------------------------------------------------
 
-. PROC속의 UPDATE문장
 -----------------------------------------------------------------------------
 ex1)
 EXEC SQL BEGIN DECLARE SECTION;
  varchar username[16], usrarea[10];
  int usrno;
 EXEC SQL END DECLARE SECTION; 
 
 EXEC SQL SELECT usrno, username, usrarea
  into :usrno, :usrname, :usrarea
  from forumlist
  where usrno=3;
 EXEC SQL WHENEVER SQLERROR GOTO stop_st;
 
 printf("\n 변경전 회원[%d]번의 거주지역 =[%s]",usrno,usrarea.arr);
 EXEC SQL COMMIT WORK;
 strcpy(userarea.arr,"SEOUL");
 usrarea.len =5;
 
 EXEC SQL UPDATE FORUMLIST
  SET usrarea =:usrarea
  WHERE usrno =3;
 
 EXEC SQL WHENEVER SQLERROR GOTO stop_st;
 
 EXEC SQL COMMIT WORK;
 printf("\n 오라클 메시지 =%s",sqlca.sqlerrm.sqlerrmc);
 
 EXEC SQL SELECT usrno,usrarea
  INTO :usrno, :usrarea
  FROM forumlist
  WHERE usrno=3;
 
 EXEC SQL WHENEVER SQLERROR GOTO stop_st;
 
 printf("\n 변경후의 회원 [%d]번의 거주지역 =[%s]", usrno.usrarea.arr);
 EXEC SQL COMMIT WORK;
 exit();
 
 stop_st:
 EXEC SQL ROLLBACK WORK;
 printf("\n 에러발생 =%s",sqlca.sqlerrm.sqlerrmc);
 -----------------------------------------------------------------------------
 
 -----------------------------------------------------------------------------
 ex2) 
 EXEC SQL BEGIN DECLARE SECTION;
  varchar usrname[16], usrarea[10];
  int usrno;
 EXEC SQL END DECLARE SECTION;
 char SqlCmd[100];
 
 EXEC SQL select usrno,username,usrarea
  into :usrno, :usrname, :usrarea
  from forumlist
  where usrno=3;
 EXEC SQL WHENEVER SQLERROR GOTO stop_st;
 
 printf("\n 변경전의 [%d]번째 회원의 거주지역 =[%s]", usrno, usrarea.arr);
 EXEC SQL COMMIT WORK;
 
 memset(SqlCmd,0x00,sizeof(SqlCmd));
 sprintf(SqlCmd,"UPDATE FORUMLIST set usrarea=%s \ (한 문장이 끝나지 않았기 때문에 \(역슬래시)를
  사용함)
    where usrno=3","seoul");
 EXEC SQL EXCUTE IMMEDIATE :SqlCmd;
 EXEC SQL WHENEVER SQLERROR GOTO stop_st;
 EXEC SQL COMMIT WORK;
 
 EXEC SQL select usrno, usrarea
  into :usrno, :usrarea
  from forumlist
  where usrno=3;
 EXEC SQL WHENEVER SQLERROR GOTO stop_st;
 
 printf("\n 변경후의 [%d]번째 회원의 거주지역 =[%s]", usrno,usrarea.arr);
 EXEC SQL COMMIT WORK;
 exit();
 
 stop_st:
 EXEC SQL ROLLBACK WORK;
 printf("\n 에러 메시지 =%s", sqlca.sqlerrm.sqlerrmc);
 -----------------------------------------------------------------------------
 -. 위의 update문을 약간 수정하면
  sprintf(SqlCmd,"update forumlist set userarea=%s where usrno=%d","seoul",3);로 해도 됨.

-. ProC속의 Insert문장
 -----------------------------------------------------------------------------
 ex1)
 EXEC SQL BEGIN DECLARE SECTION;
  varchar usrname[16], usrarea[10], usrkindcode[4];
  int usrno ;
 EXEC SQL END DECLARE SECTION;
 int tot;
 
 tot =0;
 usrno=10;
 strncpy(usrname.arr,"LEE-Y-S",Strlen("LEE-Y-S"));
 strncpy(usrarea.arr,"seoul",strlen("seoul"));
 strncpy(usrkindcode,"1004",strlen("1004"));
 
 EXEC SQL INSERT INTO FORUMLIST(usrno, usrname, usrarea, usrkindcode)
  values(:usrno, :usrname, :usrarea, :usrkindcode);
 EXEC SQL WHENEVER SQLERROR GOTO stop_st;
 printf("\n insert success!!");
 printf("\n 오라클 메시지 = %s", sqlca.sqlerrm.sqlerrmc);
 
 EXEC SQL SELECT COUNT(*)
  INTO :TOT
  FROM FORUMLIST
 EXEC SQL WHENEVER SQLERROR GOTO stop_st;
 
 printf("\n forumlist 테이블에 존재하는 총 회원 수 =%d", tot);
 EXEC SQL COMMIT WORK;
 
 EXEC SQL SELECT usrno, usrarea
  INTO :usrno, :usrarea
  FROM FORUMLIST
  WHERE usrno=10;
 EXEC SQL WHENEVER SQLERROR GOTO stop_st;
 
 printf("\n 추가된 회원 [%d]번의 거주지역 =[%s]", usrno, usrarea.arr);
 EXEC SQL COMMIT WORK;
 
 stop_st;
 EXEC SQL ROLLBACK WORK;
 printf("\n 오라클 에러=%d",sqlca.sqlerrm.sqlerrmc);
 -----------------------------------------------------------------------------
 ex1은 insert문장에 함께 기술해 줄 호스트 변수들을 선언해 주었지만...
 ex2에서는 선언하지 않았다.
 ex2의 첨에 있는 usrarea는 그 다음에 수행할 select명령에 필요한 선언 문장이다.
 -----------------------------------------------------------------------------
 ex2)
 EXEC SQL BEGIN DECLEARE SECTION;
  varchar usrarea[10];
  int usrno;
 EXEC SQL END DECLARE SECTION;
 char SqlCmd[100];
 int tot;
 
 tot=0;
 memset(SqlCmd,0x00,sizeof(SqlCmd));
 sprintf(SqlCmd,"insert into forumlist(usrno, usrname, usrarea, \
   usrkindcode) values(%d, %s, %s, %s)", \
   10,"lee-k-y","seoul","1004");
 EXEC SQL EXECUTE IMMEDIATE :SqlCmd;
 EXEC SQL WHENEVER SQLERROR GOTO stop_st;
 
 printf("\n Insert Success!");
 
 EXEC SQL SELECT count(*)
  into :tot;
  from forumlist
 EXEC SQL WHENEVER SQLERROR GOTO stop_st;
 
 printf("\n forumlist 테이블에 존재하는 총 회원 수 = %d",tot);
 EXEC SQL COMMIT WORK;
 
 EXEC SQL SELECT usrno, usrarea
  into :usrno, :usrarea
  from forumlist
  where usrno=10;
 EXEC SQL WHENEVER SQLERROR GOTO stop_st;
 printf("\ 추가된 회원 [%d]번의 거주지역 =[%s]",usrarea.arr);
 EXEC SQL COMMIT WORK;
 
 stop_st:
 EXEC SQL ROLLBACK WORK;
 printf("\n 오라클 에러 = %d", sqlca.sqlerrm,sqlerrmc);
 -----------------------------------------------------------------------------
-. Proc내에서의 delete문장.
 
 -----------------------------------------------------------------------------
 ex1) 
 EXEC SQL BEGIGN DECLRAE SECTION;
  varchar usrarea[10];
  int usrno;
 EXEC SQL END DECLARE SECTION;
 int tot;
 
 tot=0;
 EXEC SQL DELETE FORUMLIST WHERE USRNO=10;
 
 EXEC SQL WHENEVER SQLERROR GOTO stop_st;
 EXEC SQL COMMIT WORK;
 
 printf("\n Delete 문장 성공!!");
 
 EXEC SQL SELECT count(*)
  into :tot;
  from forumlist
 EXEC SQL WHENEVER SQLERROR GOTO stop_st;
 
 printf("\n forumlist 테이블에 존재하는 총 회원 수 =%d",tot);
 EXEC SQL COMMIT WORK;
 
 stop_st:
 EXEC SQL ROLLBACK WORK;
 printf("\n Oracle Error =%d",sqlca.sqlerrm.sqlerrmc);
 -----------------------------------------------------------------------------
 -. 어떠한 호스트 변수도 사용하지 않음.
 
 -----------------------------------------------------------------------------
 ex2) 
 EXEC SQL BEGIN DECLARE SECTION;
  varchar usrarea[10];
  int usrno;
 EXEC SQL END DECLARE SECTION;
 char SqlCmd[100];
 int tot;
 tot=0;
 memset(SqlCmd,0x00,sizeof(SqlCmd));
 sprintf(SqlCmd,"Delete forumlist where usrno=%d",10);
 EXEC SQL EXCUTE IMMEDIATE :SqlCmd;
 
 EXEC SQL WHENEVER SQLERROR GOTO stop_st;
 EXEC SQL COMMIT WORK;
 
 prinf("\n Delete Success!!");
 
 EXEC SQL SELECT COUNT(*)
  INTO :TOT;
  FROM FORUMLIST
 EXEC SQL WHENEVER SQLERROR GOTO stop_st;
 
 printf("\n forumlist 테이블에 존재하는 총 회원 수=%d",tot);
 EXEC SQL COMMIT WORK;
 
 stop_st;
 EXEC SQL ROLLBACK WORK;
 printf("\n Oracle Error : =%d",sqlca.sqlerrm.sqlerrmc);
 -----------------------------------------------------------------------------
 -. where절에서 회원의 고유번호값을 지정해주었다.는 점이 ex1과 다른점이다.
 -. delete 부분을 다음과 같이 수정해도 된다.
  usrnum=10;
  sqlprintf(SqlCmd,"delete forumlist where usrno=%d",usrnum);
  이처럼 변수를 먼저 선언해준 후 값을 대입해서 삭제 하는 방법도 있다.

-. 트랜잭션의 제어
 
 -. EXEC SQL COMMIT WORK;
     : DML 명령인 INSERT, DELETE ,UPDATE 문장을 수행한 경우에 유용하다.
     : DDL 명령은 자동 COMMIT 되므로 사용안해도 됨.
 
 -. EXEC SQL ROLLBACK WORK;    
     
-----------------------------------------------------------------------------------------------
? memset :

 

Posted by 농부지기
,