상세검색
'create'에 대한 검색결과 총 1,157 건
AID (Altibase Information Directory)
-
- 4. Creating Altibase Service Containerㅣ 2020-08-28
- create a container, use it as follows. Emacsbash# docker run -it -e MODE=shell --name altibase_test altitest:0.0 bash----------------------------------------------------------------- Altibase Client Query utility. Release Version 7.1.0.1.0 Copyright 2000, ALT
- [Technical Documents(English)]
-
- Can table data be saved on disk and only indexes can be created in memory?ㅣ 2021-03-11
- Phenomenon Since indexes are basically created in the same type as the table, indexes of disk tables cannot be created in the memory tablespace, and an ERR-311EC error is displayed. Create test table T1 in disk tablespace iSQL>create table t1 (c1 char(10), c2
- [FAQ(English)]
QnA
-
- sysdba로 create database가 안됩니다. ㅣ 2017-09-28
- create database할때 권한 오류보다 현재 상태에서 안된다는 오류가 나옵니다. startup process가 되야할 이슈인것 같은데 개발이라서 우분투 16입니다. 지원db 목록에 없어서 centos로 변경해서 작업하려고
-
미리보기
create database할때 권한 오류보다 현재 상태에서 안된다는 오류가 나옵니다. startup process가 되야할 이슈인것 같은데 개발이라서 우분투 16입니다. 지원db 목록에 없어서 centos로 변경해서 작업하려고
-
- create 쿼리 에서 ERR-41088 발생합니다. ㅣ 2021-04-21
- create utf8 utf16; 으로 DB를 생성하라고 가이드 했는데, 중간에 멈추어 있다고 함. 이에 라이센스를 발급받았냐고 하니, 알티베이스 web에서 라이센스를 다운받았는데, ethernet mac address가 아니라 local ser
-
미리보기
create utf8 utf16; 으로 DB를 생성하라고 가이드 했는데, 중간에 멈추어 있다고 함. 이에 라이센스를 발급받았냐고 하니, 알티베이스 web에서 라이센스를 다운받았는데, ethernet mac address가 아니라 local ser
기술문서
-
- Altibase Quick Install & Start for UNIX ㅣ 2012-09-26
- Created Reviews Date Name (Position) Distribution Name Location ALTIBASE Quick Install UNIX 3 page of 18 목차 개요 ..............................................................................................................................
-
미리보기
Copyright ⓒ 2000~2013 ALTBASE Corporation. All Rights Reserved. Real Alternative DBMS ALTIBASE, Since 1999 ALTIBASE Quick Install UNIX ALTIBASE 5 2010. 03 ALTIBASE Quick Install UNIX 2 page of 18 Document Control Change Record Date Author Change Reference 2010-03-19 lim272 Created Reviews Date Name (Position) Distribution Name Location ALTIBASE Quick Install UNIX 3 page of 18 목차 개요 .................................................................................................................................................... 4 설치 .................................................................................................................................................... 5 사전 절차 ..................................................................................................................................... 5 패키지 압축 해제 ....................................................................................................................... 5 라이센스 설치 ............................................................................................................................. 6 프로퍼티 설정 ............................................................................................................................. 7 32비트 제품의 설치 .................................................................................................................. 8 DB 생성 및 구동 .............................................................................................................................. 9 DB생성 ......................................................................................................................................... 9 ALTIBASE 구동 ........................................................................................................................ 11 ALTIBASE 종료 ........................................................................................................................ 13 간단한 사용 예 ............................................................................................................................... 14 Altibase Technical Center............................................................................................................ 14 iSQL ............................................................................................................................................ 14 DB 사용자의 생성 및 삭제 .................................................................................................... 15 테이블스페이스의 생성 및 삭제 ........................................................................................... 16 테이블의 생성 및 삭제 ........................................................................................................... 16 ALTIBASE Quick Install UNIX 4 page of 18 개요 본 문서는 ALTIBASE 제품을 설치하고 구동/종료하는 방법에 대해 기본적인 내용을 설명한다. 기본적인 설치 이후 성능 개선을 위한 프로퍼티의 변경 및 개선은 별도의 문서로 제공되니 해당 문서를 참고하도록 한다. 참고할 문서 1. 디스크IO 병목을 고려한 볼륨 구성 가이드 2. ALTIBASE 용량 산정 가이드 3. 효율적인 이중화 구성 가이드 4. 각 운영체제 별 사전 환경 설정과 관련된 가이드 문서 5. ALTIBASE 설정 파일 가이드 ALTIBASE Quick Install UNIX 5 page of 18 설치 사전 절차 1. 시스템 내에 ALTIBASE 설치를 위한 사용자 계정이 있어야 한다. 2. 해당 사용자 계정에 최소한 다음의 환경변수를 설정해야 한다. ALTIBASE_HOME=/home/altibase // 이 경로는 임의의 예시임. PATH=$ALTIBASE_HOME/bin:$PATH LD_LIBRARY_PATH=$ALTIBASE_HOME/lib:$LD_LIBRARY_PATH 위 환경변수는 사용자 계정의 쉘의 종류에 따라 다를 수 있음으로 환경에 맞게 적절하게 설정하도록 한다. 위 환경변수를 제대로 설정하였다면 프롬프트 상 다음의 결과들이 보여야 한다. Shell> echo $ALTIBASE_HOME /home/altibase // 사용자가 지정한 경로가 보여야 함. Shell> echo $PATH /home3/lim272/pkgs/jdk1.6.0_10/bin:/home/altibase/bin:/usr/java/bin Shell> echo LD_LIBRARY_PATH /home/altibase/lib:/home3/lim272/unixODBC/lib: 패키지 압축 해제 사용자는 ATC(http://atc.altibase.com) 또는 ALTIBASE로부터 다음과 같은 형태의 패키지를 받을 수 있다. 해당 패키지에는 운영체제의 정보 및 컴파일 비트, 제품의 버전 정보 등이 직관적으로 기술되어 있음으로 적합한 제품 패키지를 요청하거나 다운받도록 한다. ATC에는 최신 패키지만 존재하기 때문에 만일 사용자가 얻고자 하는 특정 버전 또는 특정 운영체제의 패키지가 없을 경우 아래로 연락을 취하면 된다. (02) 2082-1114 또는 support@altibase.com 정확한 제품 패키지의 제공을 위해 사용자는 운영체제, CPU, Memory Size, 컴파일러 및 진행 프로젝트에 대한 정보를 제공해야 한다. 제품 패키지의 파일명의 예 altibase-XEON_LINUX_redhat_Enterprise_AS4-64bit-5.3.3.2-release-GCC3.4.6.tgz ALTIBASE Quick Install UNIX 6 page of 18 압축 해제 방법 Shell> gzip -dc altibase-XXX.tgz | tar xvf - README admin/ admin/adminview.sql admin/showTables.sql admin/showReplications.sql admin/showProcBody.sql admin/tracelog.sql ……생략….. ALTIBASE를 설치할 적절한 디렉토리에 위의 패키지 파일을 위치시킨 후 위와 같이 gzip 및 tar 명령을 통해 압축을 해제하는 것만으로 ALTIBASE 설치는 완료된다. 본 문서는 기본 설치 및 구동에 대한 부분임으로 설치 후 사용자가 확인할 주요 디렉토리들만 설명한다. (상세한 것은 매뉴얼을 참고하도록 한다.) 디렉토리 설명 admin 성능뷰를 쉽게 볼 수 있는 예제 SQL 및 View 생성 파일 bin ALTIBASE 실행 파일 및 유틸리티 include 응용 프로그램 개발을 위해 제공되는 헤더파일 install 응용 프로그램 개발을 위한 makefile prefix 예 lib 응용 프로그램 개발을 위해 제공되는 라이브러리 sample 응용 프로그램의 예제 프로그램 소스 trc ALTIBASE 상태 정보가 기록되는 Trace 로그파일 위치 conf ALTIBASE 설정 파일의 위치 및 예제 파일, 라이센스 파일의 위치 logs 트랜잭션 로그파일의 기본 경로 (압축 해제 시 빈 디렉토리) dbs 데이터파일의 기본 경로 (압축 해제 시 빈 디렉토리) 라이센스 설치 압축 해제 후 DB생성 및 제품의 구동을 위해서는 반드시 라이센스를 필요로 한다. 라이센스의 요청은 다음과 같이 할 수 있다. 1. http://atc.altibase.com 에서 신청하여 받는 방법 2. support@altibase.com 으로 라이센스 요청 ALTIBASE Quick Install UNIX 7 page of 18 라이센스 발급을 위해서는 사용자의 시스템 정보 및 설치할 ALTIBASE의 버전 정보를 반드시 제공해야 한다. Shell> ulimit -a Shell> altibase -v 기타 CPU종류 및 Clock, 메모리의 크기 정보를 제공해야 함. 위와 같은 방법을 통해 받은 라이센스 파일은 확장자 없이 “license”라는 파일 이름으로 $ALTIBASE_HOME/conf/ 디렉토리 밑에 복사하면 된다. 프로퍼티 설정 ALTIBASE 설정 파일은 $ALTIBASE_HOME/conf/altibase.properties 라는 파일로 저장한다. 패키지를 압축 해제하면 $ALTIBASE_HOME/conf/altibase.properties.sample이라는 파일이 존재하는데 해당 파일을 “altibase.properties” 라는 이름으로 동일 디렉토리에 복사하면 된다. 확인 사항 Shell> ls $ALTIBASE_HOME/conf altibase.properties.sample altibase.properties license DB생성 전에 먼저 altibase.properties파일에 수정할 사항이 몇 가지 존재하는데 본 문서에서는 PORT_NO만 수정하고 나머지 항목은 별도의 문서로 제공하기 때문에 해당 문서를 참고하도록 한다. (『ALTIBASE 설정 파일 가이드』) 다음의 항목을 vi 와 같은 편집기를 통해 수정하고 저장하면 된다. Shell> vi $ALTIBASE_HOME/conf/altibase.properties … …. PORT_NO = 20300 … PORT_NO를 시스템 내에 중복되지 않는 번호로 적절하게 설정한다. (변경하지 않아도 되며 이후에라도 사용자가 변경 가능하다.) 위와 같은 작업까지 마무리 하면 모든 설치 작업은 완료된다. 작업 요약 1. 사용자 계정 생성 및 최소 환경변수 설정 2. 설치 파일 준비 및 ALTIBASE 사용자 계정의 $ALTIBASE_HOME 디렉토리에 압축 해제 3. 라이센스 파일 준비 후 $ALTIBASE_HOME/conf/에 복사 4. $ALTIBASE_HOME/conf/altibase.properties 파일 생성 ALTIBASE Quick Install UNIX 8 page of 18 32비트 제품의 설치 ALTIBASE는 공식적으로 32비트 DBMS 제품을 제공하지 않는다. 단, 클라이언트 제품에 대해 개발을 위한 패키지를 제공한다. 클라이언트의 제품은 패키지 이름 앞에 “altibase-client” 라는 이름으로 시작한다. 제품 패키지에는 컴파일러 정보가 포함되기 때문에 gcc/g++ 을 사용할 경우 해당 컴파일러의 버전을 사전에 확인하도록 한다. 클라이언트 제품 역시 위에서 설명한 것과 같이 gzip/tar를 통해 압축을 해제하는 것으로 설치가 완료된다. ALTIBASE Quick Install UNIX 9 page of 18 DB 생성 및 구동 본 장에서는 DB를 생성하고 ALTIBASE를 구동/종료하는 방법을 설명한다. 문서 내에 “$ALTIBASE_HOME/bin” 이라고 표기된 부분은 환경변수인 $ALTIBASE_HOME이 설정되어 있고 PATH에 잡혀 있는 경우 생략이 가능하다. DB생성 설치 작업까지 완료되었다면 DB를 생성하도록 한다. 본 문서는 사용자가 이해하기 쉬운 형태로 설명한다. (여기서는 ALTIBASE 5.3 기준으로 설명한다.) Shell> $ALTIBASE_HOME/bin/server create MS949 UTF8 Shell> $ALTIBASE_HOME/bin/server create US7ASCII UTF8 ALTIBASE는 사용자가 쉽게 DB를 생성할 수 있도록 “server” 라는 쉘 스크립트를 제공하고 있다. 해당 스크립트는 생성 시에 2개의 인자를 입력 받도록 되어 있다. 첫 번째는 DB전체의 문자셋을 의미하며 두 번째는 NVARCHAR의 유니코드 문자셋을 지정한다. 일반적으로 한글을 사용할 경우라면 MS949/KO16KSC5601을 첫 번째 인자로 사용하고 유니코드 사용이 필요하다면 UTF8을 사용한다. 두 번째 인자 값은 UTF8/UTF16 중에 하나를 선택한다. 만일, 사용자가 $ALTIBASE_HOME/conf/altibase.properties 안에 “DB_NAME”을 바꾼 경우라면 해당 스크립트는 동작하지 않는다. 이 경우에는 $ALTIBASE_HOME/bin/server 스크립트를 vi와 같은 편집기를 통해 “mydb”라고 된 부분을 설정 파일에서 바꾼 “DB_NAME”의 값으로 동일하게 변경한 후 사용하면 된다. 정상적으로 DB생성 작업이 수행되면 다음의 리두로그 파일 및 데이터 파일들이 생성되어야 한다. 경로 파일 명 $ALTIBASE_HOME/logs loganchor0 loganchor1 loganchor2 logfile0 logfile1 logfile2 logfile3 logfile4 logfile5 $ALTIBASE_HOME/dbs SYS_TBS_MEM_DIC-0-0 SYS_TBS_MEM_DIC-0-1 SYS_TBS_MEM_DATA-0-0 SYS_TBS_MEM_DATA-0-0 System001.dbf undo001.dbf temp001.dbf dwfile0.dwf dwfile1.dwf 정식적인 DB생성 방법은 『ALTIBASE DB생성 가이드 문서』를 참고하여도 된다. 정상적으로 생성되는 경우는 다음과 같이 화면에 나타난다. Shell> server create MS949 UTF8 ----------------------------------------------------------------- Altibase Client Query utility. Release Version 5.3.3.5 ALTIBASE Quick Install UNIX 10 page of 18 Copyright 2000, ALTIBASE Corporation or its subsidiaries. All Rights Reserved. ----------------------------------------------------------------- ISQL_CONNECTION = UNIX, SERVER = 127.0.0.1, PORT_NO = 27584 [ERR-910FB : Connected to idle instance] // 이 에러는 무시하도록 한다. Connecting to the DB server... Connected. TRANSITION TO PHASE : PROCESS Command execute success. DB Info (Page Size = 32768) (Page Count = 257) (Total DB Size = 8421376) (DB File Size = 1073741824) Creating MMDB FILES [SUCCESS] Creating Catalog Tables [SUCCESS] Creating DRDB FILES [SUCCESS] [SM] Rebuilding Indices [Total Count:0] [SUCCESS] DB Writing Completed. All Done. Create success. Shell> _ 만일, 라이센스가 정상적으로 생성하지 않은 경우 DB생성 및 구동 단계에서 다음과 같은 오류가 발생한다. Shell> server create MS949 UTF8 Altibase Client Query utility. Release Version 5.3.3.5 Copyright 2000, ALTIBASE Corporation or its subsidiaries. All Rights Reserved. ----------------------------------------------------------------- ISQL_CONNECTION = UNIX, SERVER = 127.0.0.1, PORT_NO = 27584 [ERR-910FB : Connected to idle instance] Connecting to the DB server... Connected. TRANSITION TO PHASE : PROCESS License File(/home3/lim272/work/altibase_home/conf/license) does not exist HostID->007f0100 [FAILURE] License File does not exist. Startup Failed.... [ERR-91015 : Communication failure.] Shell> _ ALTIBASE Quick Install UNIX 11 page of 18 이미 다른 이유로 기존에 DB가 생성된 경우는 다음과 같은 오류가 발생함으로 이때에는 기존 데이터파일들을 삭제하거나 별도의 경로를 지정하여 DB를 생성하여야 한다. (기존 사용 중인 파일을 삭제할 경우는 신중하게 결정하도록 한다.) [ERR-11136: The LogAnchor file already exists (File Name: /home3/lim272/work/altibase_home/logs/loganchor0).] // 로그앵커 파일 중복 또는, [ERR-11025: The data file already exists (File Name: /home3/lim272/work/altibase_home/dbs/SYS_TBS_MEM_DIC-0-0).] // 데이터 파일 중복 오류 앞에서 설명한 프로퍼티 파일을 설정하지 않을 경우 접속에서 다음과 같이 “PORT_NO=0”으로 표기되면서 접속 시도조차 하지 못하게 된다. 이 경우에는 본 문서의 “프로퍼티 설정” 부분을 참고하도록 한다. Shell> server create MS949 UTF8 ----------------------------------------------------------------- Altibase Client Query utility. Release Version 5.3.3.5 Copyright 2000, ALTIBASE Corporation or its subsidiaries. All Rights Reserved. ----------------------------------------------------------------- Write PortNo (default:20300) : ISQL_CONNECTION = UNIX, SERVER = 127.0.0.1,PORT_NO = 0 [ERR-910FB : Connected to idle instance] ALTIBASE 구동 위와 같이 DB 생성이 완료되어야 정상적인 ALTIBASE 구동이 가능하다. ALTIBASE 구동 방법은 다음과 같다. Shell> $ALTIBASE_HOME/bin/server start 만일, 앞의 과정들을 모두 수행했음에도 구동이 되지 않는 경우 $ALTIBASE_HOME/trc/altibase_boot.log 를 열어 어떤 오류가 발생하였는지 확인할 수 있다. 일반적으로 라이센스 파일의 오류나 DB생성에 문제가 있는 경우 구동이 되지 않을 수 있음으로 이를 먼저 점검하고 문제가 없다면 support@altibase.com 으로 altibase_boot.log를 첨부하여 이메일을 통해 문의하면 된다. 서버를 구동하는 방법은 위의 쉘 명령 외에 다음 방법으로도 가능하다. Shell> isql -sysdba // DBA 권한의 DB user ID/Password를 입력해야 함. iSQL> startup service; 정상적인 구동 시 다음과 같이 진행 과정이 나타난다. Shell> server start ----------------------------------------------------------------- Altibase Client Query utility. Release Version 5.3.3.5 Copyright 2000, ALTIBASE Corporation or its subsidiaries. ALTIBASE Quick Install UNIX 12 page of 18 All Rights Reserved. ----------------------------------------------------------------- ISQL_CONNECTION = UNIX, SERVER = 127.0.0.1, PORT_NO = 27584 [ERR-910FB : Connected to idle instance] Connecting to the DB server...... Connected. TRANSITION TO PHASE : PROCESS TRANSITION TO PHASE : CONTROL TRANSITION TO PHASE : META [SM] Recovery Phase - 1 : Preparing Database : Dynamic Memory Version => Parallel Loading [SM] Recovery Phase - 2 : Loading Database ….. …..중간 생략….. TRANSITION TO PHASE : SERVICE [CM] Listener started : TCP on port 27584 [CM] Listener started : UNIX [RP] Initialization : [PASS] --- STARTUP Process SUCCESS --- Command execute success. Shell> _ 만일, 앞서 설명한 환경변수 이 제대로 설정되어 있지 않으면 다음과 같이 오류가 발생한다. Shell> server start /bin/sql: No such file or directory 라이센스가 없는 경우는 위에 “DB생성”과 유사한 오류가 발생한다. DB생성을 하지 않은 경우는 다음과 같은 오류가 발생한다. Shell> server start ISQL_CONNECTION = UNIX, SERVER = 127.0.0.1, PORT_NO = 27584 [ERR-910FB : Connected to idle instance] Connecting to the DB server...... Connected. TRANSITION TO PHASE : PROCESS TRANSITION TO PHASE : CONTROL [FAILURE] The log anchor file does not exist or it is not valid. Startup Failed.... [ERR-91015 : Communication failure.] ALTIBASE Quick Install UNIX 13 page of 18 ALTIBASE 종료 ALTIBASE의 종료는 다음과 같이 수행한다. Shell> server stop 서버를 종료하는 방법은 위의 쉘 명령 외에 정식적으로 3가지 방법을 제공하고 있다. Shell> isql -sysdba // DBA 권한의 DB user ID/Password를 입력해야 함. iSQL> shutdown abort; iSQL> shutdown immediate; iSQL> shutdown normal; 옵션 설명 abort 현재 접속된 모든 세션을 강제 종료시키고 즉시 구동을 중지한다. 프로세스를 중지하기 때문에 정상적인 shutdown과정 없이 즉시 종료한다. (이후 데이터 복구에는 아무런 문제가 없음.) immediate 현재 접속된 모든 세션을 강제 종료시키고 정상적인 shutdown과정을 거쳐 중지한다. normal 현재 접속된 모든 세션이 정상 종료하기를 대기한 이후에 정상적인 shutdown과정을 거쳐 중지한다. “normal” 옵션을 사용하는 경우 세션이 종료하기를 기다려야 하기 때문에 이런 이유를 인지하지 못한 채 서버를 중지하면 shutdown과정이 진행되지 않는 것처럼 착각할 수 있음으로 사용에 주의를 필요로 한다. 정상적인 종료 시 다음과 같이 진행 과정이 나타난다. Shell> server stop ----------------------------------------------------------------- Altibase Client Query utility. Release Version 5.3.3.5 Copyright 2000, ALTIBASE Corporation or its subsidiaries. All Rights Reserved. ----------------------------------------------------------------- ISQL_CONNECTION = UNIX, SERVER = 127.0.0.1, PORT_NO = 27584 Ok..Shutdown Proceeding.... TRANSITION TO PHASE : Shutdown Altibase [RP] Finalization : PASS shutdown immediate success. Shell> _ 서버 종료 단계에서는 메모리DB의 이미지를 디스크로 내리는 과정 등이 포함되어 있기 때문에 디스크I/O성능이 느리거나 직전 트랜잭션이 대량의 변경 작업을 수행한 경우에는 트랜잭션 롤백(Rollback) 작업 등을 수행해야 하기 때문에 느리게 진행될 수 있음을 감안해야 한다. ALTIBASE Quick Install UNIX 14 page of 18 간단한 사용 예 본 장에서는 DB사용자의 생성 및 테이블스페이스의 생성이나 테이블의 생성 정도만을 간단하게 예로 설명한다. ALTIBASE는 SQL92표준을 준수하기 때문에 다른 DBMS와 구문상의 큰 차이는 없다. 자세한 SQL문의 사용과 같은 사용법에 대해서는 ALTIBASE가 제공하는 매뉴얼을 참조하도록 한다. Altibase Technical Center ALTIBASE 테크니컬 웹사이트인 http://atc.altibase.com에서 매뉴얼을 확인할 수 있으며 사용 중 궁금한 사항에 대한 답변 및 기술적인 자료를 확인할 수 있다. iSQL ALTIBASE는 사용자가 SQL문을 터미널에서 사용할 수 있도록 제공하는 유틸리티 프로그램이다. DB를 생성한 시점에는 “sys” 라는 DBA권한의 사용자만 존재하기 때문에 $ALTIBASE_HOME/bin/is 만 실행해도 가능하다. (“is”는 isql을 쉽게 사용하도록 만든 쉘 스크립트이다.) Shell> $ALTIBASE_HOME/bin/is Shell> isql -u sys -p manager -s 127.0.0.1 -port 20300 각 입력 옵션은 다음과 같다. 입력 옵션 설명 -u DB 사용자의 계정 이름 -p DB 사용자의 계정 비밀번호 -s 접속 대상 DB가 위치하는 네트웍 IP 주소 -port 접속 대상 DB에 설정한 PORT_NO 만일, DB문자셋을 US7ASCII로 설정하지 않은 경우에는 DB를 생성할 때 지정하였던 문자셋을 환경변수로 잡아야 한다. 다음과 같이 하면 된다. DB생성 시점에 아래와 같이 MS949를 문자셋으로 하였다면 Shell> server create MS949 UTF8 Shell> export ALTIBASE_NLS_USE=MS949 라고 명시적으로 문자셋을 환경변수에 지정하여야만 한글을 입/출력하는데 있어 올바르게 처리가 된다. 만일, 위 환경변수를 설정했음에도 한글이 깨진 글자체로 보일 경우가 발생하는데 그런 경우는 사용하는 터미널의 환경을 한글 사용이 가능하도록 설정하여야 한다. ALTIBASE Quick Install UNIX 15 page of 18 접속 후의 간단한 SQL실행 결과 Shell> isql -u sys -p manager -s 127.0.0.1 -port 27584 ----------------------------------------------------------------- Altibase Client Query utility. Release Version 5.3.3.5 Copyright 2000, ALTIBASE Corporation or its subsidiaries. All Rights Reserved. ----------------------------------------------------------------- ISQL_CONNECTION = TCP, SERVER = 127.0.0.1, PORT_NO = 27584 iSQL> select sysdate from dual; SYSDATE --------------- 19-MAR-2010 1 row selected. iSQL> _ DB 사용자의 생성 및 삭제 ALTIBASE는 기본적으로 “sys” 계정이라는 DBA권한을 가진 사용자가 생성되어 있다. 이 계정으로 접속하여 새로운 사용자를 만들 수 있다. iSQL> CREATE USER new_user IDENTIFIED BY new_user DEFAULT TABLESPACE sys_tbs_disk_data ACCESS sys_tbs_mem_data ON; 사용한 각 입력 부분은 다음과 같다. 입력 항목 설명 CREATE USER 이후 새롭게 만들 사용자의 이름을 기술한다. IDENTIFIED BY 이후 사용자의 비밀번호를 기술한다. DEFAULT TABLESPACE 이후 이 부분부터는 기술하지 않아도 사용자는 생성 가능하다. 다만, 접근 가능한 테이블스페이스를 명시해 주는 절이며 “ALTER USER” 구문을 통해 변경이 가능하다. ACCESS ~ ON ACCESS ~ ON 사이에 기술된 테이블스페이스에 접근 가능하도록 권한을 부여한다. 사용자의 삭제는 다음과 같이 한다. iSQL> DROP USER new_user CASCADE; ALTIBASE Quick Install UNIX 16 page of 18 테이블스페이스의 생성 및 삭제 디스크 테이블스페이스의 생성 예로 설명한다. iSQL> CREATE TABLESPACE test_tbs1 DATAFILE ‘test001.dbf’ SIZE 10M AUTOEXTENT ON MAXSIZE 100M; 정상적으로 위 구문과 같은 예로 수행하면 “$ALTIBASE_HOME/dbs/test001.dbf” 라는 데이터파일이 생성되며 해당 테이블스페이스는 초기 10M부터 시작하여 100M까지 자동 확장되어 사용 가능한 공간으로 설정된다. 디스크 테이블스페이스의 삭제는 다음과 같이 한다. iSQL> DROP TABLESPACE test_tbs1 INCLUDING CONTENTS AND DATAFILES; 해당 구문은 해당 테이블스페이스뿐 아니라 포함된 객체 및 데이터파일까지 삭제한다. 만일, 정상적으로 삭제되지 않는다면 해당 테이블스페이스 접근 중인 DB세션이 존재하는 경우임으로 해당 세션을 찾아 정리한 후 삭제 구문을 재 수행하면 된다. 테이블의 생성 및 삭제 테이블의 생성은 다음과 같이 한다. iSQL> CREATE TABLE test1 ( c1 INTEGER , c2 CHAR(10) , PRIMARY KEY (c1) ) TABLESPACE test_tbs1; 테이블의 삭제는 다음과 같이 한다. iSQL> DROP TABLE test1; 테이블을 생성한 후 정상적인 테이블의 존재 여부를 확인하려면 다음과 같이 한다. iSQL> DESC test1; [ TABLESPACE : SYS_TBS_MEM_DATA ] [ ATTRIBUTE ] ------------------------------------------------------------------------------ NAME TYPE IS NULL ------------------------------------------------------------------------------ A INTEGER FIXED NOT NULL B INTEGER FIXED [ INDEX ] ------------------------------------------------------------------------------ NAME TYPE IS UNIQUE COLUMN ------------------------------------------------------------------------------ ALTIBASE Quick Install UNIX 17 page of 18 __SYS_IDX_ID_162 BTREE UNIQUE A ASC [ PRIMARY KEY ] ------------------------------------------------------------------------------ iSQL>_ ALTIBASE Quick Install UNIX 18 page of 18 알티베이스㈜ 서울특별시 구로구 구로3동 182-13 대륭포스트 2차 1008호 02-2082-1000 http://www.altibase.com 대전사무소 대전광역시 서구 둔산동 921 주은리더스텔 901호 042-489-0330 기술지원본부 서울특별시 구로구 구로3동 182-13 대륭포스트 2차 908호 02-2082-1000 솔루션센터 02-2082-1114 http://support.altibase.com Copyright ⓒ 2000~2013 ALTIBASE Corporation. All Rights Reserved. 이 문서는 정보 제공을 목적으로 제공되며, 사전에 예고 없이 변경될 수 있습니다. 이 문서는 오류가 있을 수 있으며, 상업적 또는 특정 목적에 부합하는 명시적, 묵시적인 책임이 일체 없습니다. 이 문서에 포함된 ALTIBASE 제품의 특징이나 기능의 개발, 발표 등의 시기는 ALTIBASE 재량입니다. ALTIBASE는 이 문서에 대하여 관련된 특허권, 상표권, 저작권 또는 기타 지적 재산권을 보유할 수 있습니다.
-
- [기사] 실시간 이중화를 활용한 홈트레이딩 시스템 구축하기 ㅣ 2012-09-26
- CREATE REPLICATION REPL_NAME WITH SERVER_IP, SERVER_PORT FROM LOCAL_TABLE_1 TO REMOTE_TABLE_1, … FROM LOCAL_TABLE_n TO REMOTE_TABLE_n; REPL_NAME은 이중화 객체의 이름을 정의하고, WITH 절의 SERVER_IP 와 SERVER_PORT는 각각 ...
-
미리보기
고속 트랜잭션 처리를 위한 효율만점 DB, 메인 메모리 데이터베이스 구축 (3) 실실시시간간 이이중중화화를를 활활용용한한 응응용용 구구축축 이번 호에서는 이중화 기능을 설명하고, 이를 활용하여 금융사와 통신사에서 구축한 사례 를 바탕으로 단순화하여 모델링을 하고, 이를 구현하는 프로그램을 통해 DB 갱신을 실시간 으로 처리하는 방법을 소개한다. 또한, 이중화를 통한 서버간의 로드 밸런싱과 페일-오버 기능을 제공하는 시스템의 구성 방법을 알아보고, 이를 통해 시스템의 전체적인 가용성을 높일 수 있는 응용 프로그램 구조 설계와 개발 방법에 대해 살펴본다. 연재순서 2002/02 메인메모리 데이터베이스 이해하기 2002/03 메인메모리 데이터베이스 설치와 응용 프로그램 작성 2002/04 실시간 이중화를 활용한 응용 구축 2002/05 기존 데이터베이스와 연동 응용 구축 지난 호까지의 연재를 통하여 메인메모리DB의 가장 큰 장점은 고성능 트랜잭션 처리에 있음을 알아보았다. 또한, 제공되는 다양한 프로그래밍 인터페이스를 활용함으로써, 기존 프 로그래밍 환경의 큰 변화 없이 응용 프로그램을 작성하는 방법을 설명하였다. 이번 호에서 는 앞서 설명된 내용을 바탕으로, 업무 시스템 구축과 응용 프로그램 작성에 어떻게 활용할 수 있는지를 구축 사례를 통해 설명한다. 여기에는 실시간 요구가 큰 금융 시스템과 통신 시스템에서 업무 영역의 특성에 맞게 시스템을 구성하는 방법이 포함된다. 다만, 실제 구축 사례를 다루기에는 몇 가지 제약사항이 있으므로, 요구사항을 단순화하여 모델링을 하고 데 이터베이스 구성 방법과 응용 프로그램 작성 방법을 살펴본다. 데데이이터터 베베이이스스 실실시시간간 이이중중화화 실시간 요구가 큰 금융권이나 통신장비의 경우 높은 시스템 안정성을 아울러 요구하는 것 이 일반적이다. 이를 위해서는 시스템을 백업가능한 체계로 구축해야 하며, 서버 문제가 발 생하면 페일-오버를 통해 서비스를 끊임없이 지원할 수 있어야 한다. 또한, 시스템의 운영 효율을 높이기 위해서는 서버간의 역할 분담을 포함하여 로드-밸런싱을 어떻게 할 것인지 에 대한 고려도 필요하다. 데이터베이스 차원에서 백업 시스템을 구축하는 것은 다양한 방 법이 있을 수 있으나, 비용이나 운영 환경을 고려할 때, 최근에는 데이터베이스 자체의 이 중화 기술을 도입하여 구축하는 경향이 확산되고 있다. 데이터베이스 이중화는 복수개의 DB를 상호 연결하여 동일한 DB를 유지할 수 있도록 복제하는 기술이다. 이중화 효율을 높이기 위해 저장장치 레벨에서 로그 기반으로 이중화를 수행하는데, 그림 1과 같이 DB에 요구되는 연산 중에 SELECT 연산은 이중화에 영향을 주 지 않기 때문에 검색 트랜잭션은 필터링되고, 이중화 관리자는 변경 연산(INSERT, UPDATE, DELETE)에 대한 로그만을 XLOG로 변환하여 송신한다. 상대 DB의 이중화 관리자는 전달 받은 XLOG를 다시 변환하여 레코드 고유의 특성을 추출하여 트랜잭션을 발생시켜 반영한 다. [그림 1] 이중화 방식 이중화는 다수의 DB를 묶어 사용자로 하여금 하나의 DB 그룹으로 인식할 수 있도록 해준다. 하나의 DB 그룹에 속한 DB는 동일한 데이터를 유지하고 있으므로, 사용자는 어느 DB를 접근해도 된다. N-WAY 이중화를 지원하기 때문에, 하나의 DB 그룹에 2개 이상의 DB를 포함시킴으로써 가용성을 한층 더 높일 수 있다. N-WAY 이중화를 적용했을 때, 하나 의 DB에서 발생한 트랜잭션은 N-1 개의 다른 DB에 반영되어야 한다. 단순히 각 DB에 N- 1번 반복하여 트랜잭션을 반영하거나 릴레이 식으로 반영한다면, 마지막 차례의 DB는 첫번 째 DB보다 상대적으로 큰 지연이 발생하게 되는데, 이는 데이터 불일치를 발생시키는 원인 이 될 수 있다. 따라서, N-WAY 이중화를 지원하기 위해서는 세심하게 이중화 토폴로지를 구성해야 한 다. 포인트-포인트(point-to-point) 방식의 이중화를 지원함으로써 이러한 전송 지연을 방 지할 수 있다. 즉, N-1개 DB에 각각 개별적으로 자신의 트랜잭션만을 반영시킴으로써, 거 의 동시에 트랜잭션이 전파되는 효과를 볼 수 있다. 예를 들어, 그림 2와 같이 4-WAY 이 중화 구조에서 DB1의 트랜잭션은 동시에 각각 DB2, DB3, DB4에 전달된다. 같은 방식으로 DB2, DB3, DB4의 트랜잭션은 각각 다른 DB에 전달된다. 이로써, 4개의 DB 모두는 동일한 데이터를 유지할 수 있게 된다. 4-WAY 이중화를 적용하면, 하나의 DB가 전체 업무량의 1/4 만을 담당하도록 구성함 으로써 로드 밸런싱을 할 수 있다. 또한, 시스템 가용성 측면을 살펴보면, 동시에 4개의 DB 가 모두 다운될 확률은 훨씬 줄어들게 된다. 따라서, DB 그룹내의 DB 가 다운되더라도 다 른 DB를 활용할 수 있으므로 시스템 가용성은 높아지게 된다. [그림 2] 4-WAY 이중화 이이중중화화 관관련련 DDBB 프프로로퍼퍼티티 이중화를 운영하기 위해서는 몇 가지 DB 서버의 이중화 관련 데이터베이스 프로퍼티를 지정할 필요가 있다. 표 1에, DB간의 데이터 전송과 수신을 위한 통신 환경을 지정하고, 효 율적인 이중화 기능을 지원하기 위한 프로퍼티가 설명되어 있다. 특히 변경연산에서 발생할 수 있는 데이터 충돌을 해결하는 방법을 지정하는 프로퍼티가 있다. 프로퍼티 설명 REPLICATION_PORT_NO 지역서버에서 이중화 연결을 할 때 지역서버의 이중화 포 트 번호 REPLICATION_MAX_LOGFILE 이중화 시작후 변경된 내용이 저장될 최대 로그 파일의 개 수 REPLICATION_UPDATE_REPL ACE 이중화 작업중 변경작업 충돌(update conflict) 해소 정책 지정. 값이 0 이면 충돌이 있을 경우 반영하지 않고, 1일 경우 충돌을 무시하고 반영한다. REPLICATION_CONNECT_TIM EOUT 서버 구동시 이중화를 수행하기 위해 대상 호스트에 대한 이중화 연결시도의 시간 한계 지정 [표 1] 이중화 프로퍼티 이이중중화화 운운영영 방방식식 이중화 구성에는 시스템 요구에 따라 단순 백업 시스템을 구성할 지, 또는 서버 운영 효율을 극대화하기 위한 동시 운영 시스템을 구성할지 결정할 수 있는데, 이를 지원하기 위 해 운영-대기(ACTIVE-STANDBY) 방식과 운영-운영(ACTIVE-ACTIVE)방식이 있다. ○ 1 운영-대기 방식: 하나의 DB를 백업 전용으로 사용하는 것으로 운영 DB를 복제하다 가 운영 DB가 다운되었을 경우 페일-오버 하여 대기 서버를 운영 서버로 전환하는 방식으로, 운영 DB에서만 이중화 기능을 시작한다. 그림 3은 최초에 DB1을 운영 서버로, DB2를 대기서버로 운영중인 상황이다. DB1의 변경 내용은 이중화된 DB2로 복제되고, 각 응용 프로그램은 운영 DB인 DB1을 접근하여 서비스한다. DB1이 다운 이 되면, 페일-오버 기능에 의해, DB2가 운영 DB로 전환되고 DB1은 복구 작업후에 대기 DB로 전환된다. 이제, 각 응용 프로그램은 운영 DB인 DB2로 접근하여 DB작 업을 하게 된다. 이때, DB2의 변경내용은 DB1으로 이중화된다. DB2 DB1 응용 응용 이중화 대기 DB 운영 DB (A) DB1이 운영중이고 DB2가 대기중 이중화 DB1 DB2 응용 응용 운영 DB 대기 DB (B) DB1이 다운되어 DB2가 운영DB로 전환 [그림 3] 운영-대기 방식 ○ 2 운영-운영 방식: 모든 DB를 운영 DB로 사용하는 것으로, 상호 백업에 의해 DB를 일치시킨다. 이 방식에서는 DB 각각이 상대의 백업 역할을 하여 다운이 발생하면 페일-오버를 수행한다. 모든 DB에서 이중화 기능을 시작한다. 그림 4는 DB1과 DB2 모두 운영서버로 설정된 상태를 보여준다. 응용 프로그램은 로드-밸런싱 규칙 에 따라 DB1 또는 DB2를 접근하여 서비스 할 수 있다. 각 DB의 변경 내용은 상호 이중화에 의해 복제된다. 이 운영 방식은 두 DB가 동시에 운영이 되므로, 운영-대 기 방식에 비해 서버 운영 효율이 훨씬 좋다. 또한, DB 간의 페일-오버 기능이 단 순화 되는 장점이 있다. 즉, 한 DB가 다운되면 앞단의 로드-밸런싱이나 페일-오버 기능을 담당하는 모듈에 의해 다운된 DB를 제외하고 SQL문을 디스패치하면 된다. 이중화 DB2 DB1 응용 응용 운영 DB 운영 DB [그림 4] 운영-운영 방식 이이중중화화 운운영영 절절차차 DB 서버의 안정성을 높이기 위하여 이중화 도입을 결정하였으면, DB간의 이중화 기능 을 정의하고 운영하기 위해서 다음과 같은 절차를 따라야 한다. ○ 1 요구사항을 분석하여 이중화를 적용할 DB를 어떻게 구성할 것인지 결정한다. 이때, 이중화 운영 방식을 결정한다. ○ 2 이중화에 포함될 각 DB의 이중화 프로퍼티를 지정한다. ○ 3 이중화에 포함될 각 DB에 이중화 객체를 정의한다. 이 정의에는 상대 DB의 이중화 포트와 테이블명 등의 정보가 명시된다. ○ 4 결정된 이중화 운영 방식에 따라, 운영(ACTIVE) DB에서 이중화 기능을 시작한다. ○ 5 시스템 관리등의 요구에 따라 일시적으로 이중화 기능을 정지하고 다시 시작한다. ○ 6 이중화 기능을 제거하기 위해서는 이중화 기능을 정지하고 이중화 객체를 삭제한다. 이이중중화화 기기능능 명명령령 이중화 객체를 정의하고 이중화 기능을 시작하고 정지하는 등의 모든 명령은 SQL92 표준을 확장한 SQL문을 사용하여 할 수 있다. ○ 1 이중화 객체 생성 CREATE REPLICATION REPL_NAME WITH SERVER_IP, SERVER_PORT FROM LOCAL_TABLE_1 TO REMOTE_TABLE_1, … FROM LOCAL_TABLE_n TO REMOTE_TABLE_n; REPL_NAME은 이중화 객체의 이름을 정의하고, WITH 절의 SERVER_IP 와 SERVER_PORT는 각각 상대 DB의 IP 와 포트를 지정한다. FROME 절은 지역 테이 블과 상대 테이블을 지정하는 것으로 USER.TABLE_NAME 형식으로 지정해야 한다. ○ 2 이중화 동작 ALTER REPLICATION REPL_NAME START | SYNC | QUICKSTART | STOP; 각각 이중화 기능을 시작하고, 동기화하고, 빠른 시작, 정지하는 기능을 명령한다. ○ 3 이중화 객체 삭제 DROP REPLICATION REPL_NAME; 반드시 이중화 객체 삭제를 하기 위해서는 이중화 기능이 정지되어 있어야 한다. 이이중중화화를를 활활용용한한 시시스스템템 구구성성 증권회사에서 제공하는 홈트레이딩 시스템을 사용해본 독자들은 관심 종목의 시세를 보 다 빨리 제공받는 것이 얼마나 중요한지 느껴본 적이 있을 것이다. 최근 10단계 호가의 제 공 등의 기능 강화에 의해 처리해야 할 데이터의 양이 3-4배 증가하게 되었음을 고려할 때, 홈트레이딩 시스템에서 주식시세를 실시간으로 받기 위해서는 증권사의 주식시세 시스템의 트랜잭션 처리 능력이 매우 중요하다. 주식시세 시스템에 메인 메모리DB를 적용함으로써 실시간으로 주식시세 수집 및 분석 기능을 제공하는 솔루션을 실제로 구축한 사례를 바탕으로 단순화하여 모델링을 해보자. 먼 저 요구되는 업무 특성을 살펴보면 다음과 같다. ○ 1 초당 1000 2000 건의 시세 데이터를 수집할 필요가 있다 ○ 2 시스템의 성격상 고도의 가용성이 요구된다. ○ 3 수집된 시세 데이터를 다양한 각도로 분석하기 위해서는 고성능의 트랜잭션 처리 능 력이 보장되어야 한다. 그림 5에서, 주식 시세 모델은 외부로부터 시세 데이터를 받는 부분과 데이터를 가공하 여 DB 조작을 하는 부분으로 크게 구성된다. 주요 컴포넌트로는 시세 수신기, 데이터 큐, 데이터 처리기와 데이터베이스가 있다. ○ 1 시세 수신기 : UDP로 전송되는 시세 데이터를 수신하여 데이터의 타입을 구분하고, 미리 정해진 규칙에 의해 해당 큐에 데이터를 저장하는 기능을 한다. 이때, 시세 수 신기는 전송되는 데이터의 손실 없이 안전하게 큐에 저장해야 한다. ○ 2 데이터 큐 : 시세 데이터의 타입별로 수신받은 데이터를 임시 저장하는 버퍼 역할을 한다. 주의할 점은 각 데이터 큐의 크기는 제한되므로, 큐에 저장하는 것과 꺼내오 는 모듈의 상대적인 성능이 매우 중요한 역할을 한다는 점이다. ○ 3 처리기 : 각 타입별로 데이터를 처리하는 업무 로직으로 구성되며, 해당 큐에서 데 이터를 읽어 백-엔드의 데이터베이스에 저장하는 역할을 수행한다. ○ 4 데이터베이스 : DB1과 DB2는 고가용성과 로드 밸런싱을 위하여 이중으로 구성되며, 데이터를 일치시키기 위하여 이중화 기능으로 묶인다. 이때, 두 DB는 서버 다운을 대비한 페일-오버 기능을 제공하도록 구성된다. 이러한 기능을 이용하여 데이터 처 리기는 DB1 또는 DB2로 데이터를 저장할 수 있게 된다. 큐 (타입N) 큐 (타입1) 처리기 (타입N) 시세 데이터 처리기 (타입1) 시세 수신기 DB 2 DB 1 [그림 5] 주식시세 시스템 모델링 그림 5에서 성능의 병목점이 될 수 있는 곳은 큐와 DB이다. 만일 큐가 꽉 차있다면, 시세 수신기는 데이터를 저장하기 위하여 대기하게 되어 연속해서 들어오는 다음 데이터를 잃게 된다. 또한, DB의 트랜잭션 처리 속도가 느리다면, 처리기는 트랜잭션이 처리될 때 까 지 대기하게 된다. 결국 큐에서 데이터를 빼내오는 속도가 시세 데이터를 수신받는 속도보 다 느리게 되어 데이터 유실을 초래하게 된다. 즉, 그림 5와 같은 시스템 구성에서 최대 병 목점은 백-엔드의 DB가 되는 셈이다. 데데이이터터 수수신신 프프로로그그램램 먼저, 시세 수신기 프로그램을 살펴보자. 논의를 큐 처리에 집중하기 위하여 프로그램 의 다른 부분은 생략하였다. 설명의 편의를 위해, 2가지 타입의 데이터 처리만 가정하여 프 로그램에서는 2개의 메시지 큐를 생성하여 사용한다. get_data함수는 UDP 프로토콜로 데이 터를 수신한다. 수신된 데이터는 process_data함수를 호출하여 각 데이터 타입에 따라 메 시지를 생성하여, 해당되는 메시지 큐에 저장한다. 여기에서 문제가 되는 부분이 바로 process_data 함수 이다. 만일 해당 큐가 꽉 차있어서, msgsnd 시스템콜에서 대기하게 되 면, 계속 날아오고 있는 데이터를 받을 수 없게 된다. 같은 이유로 이 모듈에서 직접 데이 터베이스를 액세스 하는 것은 바람직하지 않다. 데이터베이스 서버의 업무량에 따라 단위 처리 시간이 순간적으로 길어질 수 있는데 이런 경우에도 전송되는 데이터를 유실할 수 있 기 때문이다. 즉, 수신기는 전송되는 데이터를 받아 바로 큐에 저장하는 단순 작업만 반복 적으로 수행함으로써, 데이터를 안전하게 데이터베이스에 저장할 수 있도록 완충역할을 하 는 것이다. #include #include #include #include int main(int argc , char **argv) { … /* 메시지 큐 생성 */ if ( (mq1 = msgget(MSG_KEY1, PERMS | IPC_CREATE) ) < 0 ) { err_sys(“Receiver: Can’t get message queue 1”); } if ( (mq2 = msgget(MSG_KEY2, PERMS | IPC_CREATE) ) < 0 ) { err_sys(“Receiver: Can’t get message queue 2”); } while (1) { /* 시세 데이터 수신 */ get_data(data); /* 데이터를 큐에 저장 */ pr oce ss_data(data); } … } void get_data(void *data) { /* UDP 프로토콜을 통한 데이터 수신 */ … } void set_data(void* data, MSG* msg) { /* 각 데이터 타입에 따라 필요한 구조를 가지는 메시지를 생성 */ } void process_data( void *data ) { switch (data.type) case 1: { /* 메시지 타입 지정 */ msg.mtype = 1L; /* 메시지 본문 지정 */ set_data(data, /* 첫번째 메시지 큐에 저장 */ if ( (ret = msgsnd(mq1, n(msg.mtext), 0) ) < 0) { err_sys("MQ1: Send Message Error"); } break; case 2: { /* 메시지 타입 지정 */ msg.mtype = 2L; /* 메시지 본문 지정 */ set_data( /* 두번째 메시지 큐에 저장 */ if ( (ret = msgsnd(mq2, n(msg.mtext), 0) ) < 0) { err_sys("MQ2: Send Message Error"); } break; default: err_sys(“ Wrong type data!!!”); } } [리스트 1] 데이터 수신기 프로그램 리스트 데데이이터터 처처리리 프프로로그그램램 데이터 처리기는 큐에서 메시지를 읽고 적절한 가공을 한 후, 데이터베이스에 저장한다. 프로그램은 크게 데이터베이스 처리 로직과 큐 처리 로직으로 구성된다. 큐 처리 로직은 지 정된 큐에서 메시지를 읽어 호스트 변수로 선언된 변수에 값을 지정한다. 데이터베이스 처 리 로직은 데이터베이스 연결과 INSERT 처리를 한다. 데이터베이스 처리부를 쉽게 작성하 기 위하여 SES C/C++ 을 활용하여 작성되었다. 프로그램을 간단히 살펴보자. 먼저 DB 컬 럼값을 저장하기 위한 변수를 호스트 변수로 선언한다. 호스트 변수는 DB컬럼 값을 지정하 거나 저장하는 역할을 한다. 다음, DB서버에 접속하기 위하여 IP와 사용자 등의 정보를 지정하여 DB서버에 연결한 다. 이때, 리턴되는 sqlcode를 검사하여 에러가 발생한 상황이면, 에러의 종류에 따라 재시 도를 하거나 종료해야 한다. 다음, 약속된 메시시 키로 메시지 큐에 연결하고, 메시지 큐에서 메시지를 하나 읽어 온다. 메시지를 해석하여 적당한 호스트 변수에 값을 지정한다. 이것은 결국, 해당 테이블의 컬럼값을 지정하는 것이다. 다음, 저장된 호스트 변수값으로 해당 테이블에 삽입연산을 수행한다. 이때, 리턴되는 sqlcode를 검사하여 에러가 발생한 상황이면, 에러의 종류에 따라 에러 처리 루틴을 작성 해야 한다. #include #include #include #include #include /* 호스트 변수 선언 */ EXEC SQL BEGIN DECLARE SECTION; typedef struct STK_TYPE_1 { char stk_code[12]; char stk_name[20]; int cur_price; … } STK_TYPE_1; char USERNAME[50]; char PASSWD[50]; char connStr[1024]; STK_TYPE_1 data; EXEC SQL END DECLARE SECTION; int main(int argc , char **argv) { … /* Altibase Server에 접속 */ /* Altibase DBMS 로 연결하는 데 필요한 값을 설정한다 */ sprintf(connStr, "DSN=127.0.0.1;CONNTYPE=%d;NLS_USE=%s;PORT_NO=%d", 1, NLS, PORT_NO); /* Connection을 형성, 실제로 DBMS 로 연결을 하는 부분이다. */ EXEC SQL CONNECT :USERNAME IDENTIFIED BY :PASSWD USING :connStr; if (sqlca.sqlcode == SQL_ERROR) { printf("connection error\n"); exit(1); } /* 메시지 큐를 오픈한다. */ if ( (mq1 = msgget(MSG_KEY1, 0) ) < 0 ) { err_sys(“Processor1: Can’t get message queue 1”); } while (1) { /* 데이터를 큐에서 읽어 온다 */ get_data(); /* 데이터를 데이터베이스에 저장 */ pr oce ss_data(); } /* 메시지 큐 제거 */ if (msgctl(MSG_KEY1, IPC_RMID, (struct msqid_ds *) 0) < 0 ) { err_sys(“Processor1: Can’t get rid of queue 1”); } … } void set_data(MSG* msg) { /* 메시지로부터 데이터를 추출하여 값을 지정한다 */ strcpy(data.stk_code, msg->mtext[0]); memcpy(data.stk_price, msg->mtext[32], 4); … } void get_data(data ) { /* 첫번째 메시지 큐에서 읽어온다 */ if ( (ret = msgrcv(mq1, f(msg.mtext), 0, 0) ) < 0) { err_sys("Processor1: Get Message Error from MQ1"); } set_data( } int process_data() { /* 호스트 변수에 지정된 데이터를 데이터베이스에 insert */ EXEC SQL INSERT INTO SISE_TBL VALUES ( :data ); if (sqlca.sqlcode == SQL_ERROR) { err_sys(“Processor1:INSERT INTO SISE_TBL”); } … } [리스트 2] 데이터 처리기 프로그램 리스트 성성능능 분분석석 각 프로그램이 전체적인 시스템 성능에 미치는 영향을 검토해보자. 데이터 수신기에서 는 단순히 큐 로직만이 존재하므로 별로 큰 영향을 주지 않는다. 반면, 데이터 처리기에서 는 별도의 프로세스인 데이터베이스를 접근하는 데이터베이스 처리 로직이 큐 로직에 비해 상대적으로 큰 실행 시간을 갖는다. 즉, 데이터베이스의 트랜잭션 처리 성능이 전체 시스템 성능에 큰 영향을 미치게 된다. 프로그램을 컴파일하여 실행시킨 후, 큐의 크기를 모니터링해 봄으로써 시스템의 성능 을 측정할 수 있다. 이를 위해, 데이터를 생성하여 UDP로 전송하는 데이터 생성기를 만들 고, 초당 데이터 건수를 조정해 가며 측정해 보면, 데이터의 손실 없이 실시간으로 데이터 를 수집하여 데이터베이스에 저장함을 확인할 수 있다. 데데이이터터 베베이이스스 구구성성 백-엔드의 데이터베이스 구성을 살펴보자. 고도의 안정성이 요구되는 금융시스템의 특 성상 데이터베이스를 이중으로 구성하고 이중화 기능을 활용하여 상호 백업 시스템을 갖추 도록 구성한다. 또한 서버 운영 효율을 높이기 위하여 운영-운영 방식으로 이중화를 운영한 다. 이중화 기능을 통해, 각각의 DB에서 처리되는 트랜잭션은 상대 DB에 반영된다. 이중화를 정의하기 위해서, DB1과 DB2의 서버 환경이 다음과 같을 때, DB1의 altibase_property 파일과 DB2의 altibase_property 파일에 이중화 포트 프로퍼티를 다음과 같이 지정한다. # DB1 서버 환경 IP : 192.168.1.11 이중화 포트 : 21521 # DB2 서버 환경 IP : 192.168.1.21 이중화 포트 : 22521 #DB1 프로퍼티 파일 REPLICATION_PORT_NO = 21521 #DB2 프로퍼티 파일 REPLICATION_PORT_NO = 22521 또, 이중화 대상 테이블이 SISE_TBL 과 JONG_TBL일때, 먼저 다음과 같은 이중화 SQL문을 ISQL에서 실행해서 이중화 객체를 생성한다. #DB1 에서 이중화 객체 생성 create replication sise_repl with 192.168.1.21, 22521 from sys.sise_tbl to sys.sise_tbl, from sys.jong_tbl to sys.jong_tbl; #DB2 에서 이중화 객체 생성 create replication sise_repl with 192.168.1.11, 21521 from sys.sise_tbl to sys.sise_tbl, from sys.jong_tbl to sys.jong_tbl 이중화 기능을 시작하기 위해서는 다음과 같은 이중화 SQL문을 각각 실행한다. 이 명 령은 이후에 발생하는 모든 변경 트랜잭션을 복제하도록 활성화 시킨다. #DB1 에서 이중화 기능 시작 alter replication sise_repl start; #DB2 에서 이중화 객체 생성 alter replication sise_repl start; 데데이이터터 베베이이스스 로로드드--밸밸런런싱싱과과 페페일일--오오버버 이중화로 구성된 DB간의 로드-밸런싱과 페일-오버를 다양한 방법으로 할 수 있다. 먼 저, 하드웨어적으로 L4 스위치를 DB 앞 단에 설치하고 각 DB를 서비스 노드로 등록하여 로드-밸런싱과 페일-오버를 구현할 수 있다. L4 스위치는 들어오는 SQL문을 각 DB로 분산 시키는 역할을 하고, 특정 서버의 다운시 다른 서버에 업무를 분담시키는 방식으로 페일-오 버를 수행한다. 응용 프로그램은 L4 스위치에서 제공하는 대표 IP를 이용하여 DB서버의 구 성을 고려하지 않고 운영중인 DB에 접근할 수 있다. 하드웨어적으로 구현되므로, 병목이 발생할 가능성이 적은 장점이 있다. 소프트웨어적으로는 미들웨어를 도입하는 방법이 있다. 미들웨어는 L4 스위치와 비슷한 방식으로 로드-밸런싱과 페일-오버 기능을 구현한다. 응용 프로그램은 미들웨어에서 제공 하는 대표 IP를 이용하여 백엔드의 DB서버의 구성을 고려하지 않고 운영중인 DB에 접근할 수 있다. 또한, 통신장비와 같이 소프트웨어가 장비에 내장되는 응용에서는 비용문제로 인 하여 직접 로드-밸런싱과 페일-오버 기능을 구현하는 경우가 있다. 이 경우에는, 시스템의 요구사항을 분석하여 정의된 트랜잭션을 특성별로 분류하여 각 DB에 전담시키는 방식으로 로드-밸런싱을 구현할 수 있다. 또한 특정 응용에 한정한다면, DB의 다운 여부를 지속적으 로 검사하여, 다운이 발견되면, 즉시 다른 DB로 디스패치하는 구현도 비교적 쉽게 할 수 있다. 지금까지 메인메모리DB를 활용하여 이중화 기술의 특징과 이를 적용하는 데이터베이스 구성과 실시간 이중화 구현 방법을 설명하였다. 이중화를 도입하여 백-엔드의 DB서버의 안 정성을 제고할 수 있으며, 특히 페일-오버를 통한 중단 없는 서비스를 제공할 수 있음을 살 펴보았다. 주목할 점은 응용 프로그램 레벨에서는 별도의 고려 없이 이중화 기능을 활용할 수 있다는 점이다. 다음 호에서는 현재 보편화된 디스크 기반 DB의 대용량 데이터 운영 특 징과 메인메모리DB의 빠른 트랜잭션 처리 특징을 결합하여 상호 연동하는 방법을 연구해보 고 사용자 인증 시스템의 구현 예제를 통해 활용방법을 살펴볼 예정이다. 내용에 대한 질문 은 알티베이스의 홈페이지( www.altibase.com)와 필자의 E메일을 통해 문의 바란다.
매뉴얼
-
- [Altibase 5.3.3] Stored Procedure User's Manual_KOR ㅣ 2012-09-27
- CREATE PROCEDURE ····················································································································
-
미리보기
ALTIBASE Application Development Stored Procedure User’s Manual Release 5.3.3 -------------------------------------------- ALTIBASE Application Development Stored Procedure User’s Manual Release 5.3.3 Copyright ⓒ 201~209 ALTIBASE Corp. All Rights Reserved. 본 문서의 저작권은 ㈜알티베이스에 있습니다. 이 문서에 대하여 당사의 동의 없이 무단으로 복제 또는 전용할 수 없습니다. ㈜알티베이스 152-790 서울시 구로구 구로동 182-13 대륭포스트타워Ⅱ 10 층 전화: 02-2082-1114 팩스: 02-2082-1099 e-mail: support@altibase.com homepage: http://ww.altibase.com -------------------------------------------- 목차 I 목 차 서문 ················································································································· i 이 매뉴얼에 대하여 ·························································································································· ii 1. 저장 프로시저 ························································································· 7 저장 프로시저의 개요 ······················································································································ 8 저장 프로시저의 구조 ··················································································································· 11 2. 저장 프로시저 SQL문 ··········································································· 13 개요 ······················································································································································· 14 CREATE PROCEDURE ····················································································································· 16 ALTER PROCEDURE ························································································································ 23 DROP PROCEDURE ························································································································· 25 EXECUTE ·············································································································································· 26 CREATE FUNCTION ························································································································ 28 ALTER FUNCTION ··························································································································· 32 DROP FUNCTION ···························································································································· 34 3. 저장 프로시저 블록 ·············································································· 35 저장 프로시저 블록 ······················································································································· 36 지역 변수 선언 ································································································································ 39 SELECT INTO ····································································································································· 46 ASSIGNMENT ···································································································································· 51 LABEL ···················································································································································· 54 PRINT ··················································································································································· 57 RETURN ··············································································································································· 60 4. 흐름 제어 ······························································································· 62 개요 ······················································································································································· 63 IF ···························································································································································· 64 II ALTIBASE5 Stored Procedure User’s Manual CASE ······················································································································································ 69 LOOP ···················································································································································· 73 WHILE LOOP ······································································································································ 75 FOR LOOP ·········································································································································· 77 EXIT ························································································································································ 82 CONTINUE ·········································································································································· 85 GOTO ···················································································································································· 87 NULL ····················································································································································· 90 5. 커서 ········································································································· 93 커서의 개요 ······································································································································· 94 DECLARE CURSOR··························································································································· 96 OPEN CURSOR ································································································································· 99 FETCH ················································································································································ 102 CURSOR FOR LOOP ···················································································································· 104 CLOSE CURSOR ····························································································································· 106 CURSOR 속성 ································································································································ 107 6. 사용자 정의 타입 ················································································· 112 개요 ···················································································································································· 113 DECLARE TYPE ······························································································································· 115 ASSOCIATIVE ARRAY TYPE의 함수 ······················································································· 118 RECORD 및 ASSOCIATIVE ARRAY의 사용 ········································································ 122 REF CURSOR ··································································································································· 125 7. 타입 세트 ······························································································ 131 타입 세트의 개요 ························································································································· 132 CREATE TYPESET ··························································································································· 134 DROP TYPESET ······························································································································· 136 8. 동적 SQL ······························································································· 137 동적 SQL의 개요 ·························································································································· 138 EXECUTE IMMEDIATE ·················································································································· 140 OPEN FOR ······································································································································· 143 9. 예외 처리 ······························································································ 145 목차 III 예외처리 개요 ································································································································ 146 DECLARE EXCEPTION ·················································································································· 149 RAISE ·················································································································································· 150 RAISE_APPLICATION_ERROR ···································································································· 152 사용자정의 EXCEPTION ·············································································································· 153 SQLCODE와 SQLERRM ··············································································································· 156 Exception Handler ························································································································· 158 10. 파일 제어 ······························································································ 163 디렉토리 관리 ································································································································ 164 파일 제어·········································································································································· 166 FCLOSE ·············································································································································· 169 FCLOSE_ALL ····································································································································· 170 FCOPY ················································································································································ 171 FFLUSH ·············································································································································· 174 FOPEN ················································································································································ 176 FREMOVE ·········································································································································· 178 FRENAME ·········································································································································· 180 GET_LINE ··········································································································································· 182 IS_OPEN ············································································································································ 184 NEW_LINE ········································································································································· 186 PUT ······················································································································································ 188 PUT_LINE ··········································································································································· 190 파일 제어 예외 처리 ··················································································································· 192 A. 부록 ········································································································ 195 저장 프로시저 예제 ····················································································································· 196 파일 제어 예제 ······························································································································ 204 스키마 ················································································································································ 206 찾아보기 ······································································································ 217 서문 i 서문 ii ALTIBASE5 Stored Procedure User’s Manual 이 매뉴얼에 대하여 이 매뉴얼은 저장 프로시저의 개념 및 사용 방법에 대해 설명한다. 대상 사용자 이 매뉴얼은 다음과 같은 알티베이스 사용자를 대상으로 작성되었다. y 데이터베이스 관리자 y 성능 관리자 y 데이터베이스 사용자 y 응용 프로그램 개발자 y 기술지원부 다음과 같은 배경 지식을 가지고 이 매뉴얼을 읽는 것이 좋다. y 컴퓨터, 운영 체제 및 운영 체제 유틸리티 운용에 필요한 기본 지식 y 관계형 데이터베이스 사용 경험 또는 데이터베이스 개념에 대한 이해 y 컴퓨터 프로그래밍 경험 y 데이터베이스 서버 관리, 운영 체제 관리 또는 네트워크 관리 경험 소프트웨어 환경 이 매뉴얼은 데이터베이스 서버로 알티베이스 버전 5.3.3 을 사용한다는 가정 하에 작성되었다. 이 매뉴얼의 구성 이 매뉴얼은 다음과 같이 구성되어 있다. y 제 1장 저장 프로시저 이 장은 저장 프로시저의 개념 및 구조, 사용 시 주의 사항에 대해 설명한다. y 제 2장 저장 프로시저 SQL문 이 장은 저장 프로시저 SQL문에 대한 사용 방법에 대해 설명한다. 서문 iii y 제 3장 저장 프로시저 블록 이 장은 저장 프로시저 블록의 개념, 저장 프로시저 바디 내에서 선언하는 지역 변수 및 사용가능한 문장에 대해 설명한다. y 제 4장 흐름 제어 이 장은 저장 프로시저 바디 내에서 절차적 프로그램 작성이 가능하도록 프로그램 흐름을 제어할 수 있는 흐름 제어문에 대해 설명한다. y 제 5장 커서 이 장은 저장 프로시저 내에서 조회 레코드 건수가 여러 개인 SELECT문을 처리할 수 있도록 커서를 정의하고 레코드를 제어할 수 있는 커서 관련문들에 대해 설명한다. y 제 6장 사용자 정의 타입 이 장은 저장 프로시저 내에서 사용자 정의 타입인 record 및 associative array의 정의 및 사용 방법에 대해 설명한다. y 제 7장 타입 세트 이 장은 사용자 정의 타입의 집합인 타입 세트의 정의 및 사용 방법에 대해 설명한다. y 제 8장 동적 SQL 이 장은 실행 시간에 사용자가 원하는 질의를 만들어서 실행하기 위한 동적 SQL에 대해 설명한다. y 제 9장 예외 처리 이 장은 저장 프로시저 실행 중 오류 발생 시 저장 프로시저 내에서 오류에 대한 예외 처리가 가능하도록 하는 예외 처리 관련문에 대해 설명한다. y 제 10장 파일 제어 이 장은 저장 프로시저의 운영체제 텍스트 파일에 대한 읽기 및 쓰기 기능에 대하여 설명한다. y A. 부록 이 장은 이 매뉴얼의 예제에서 사용한 스키마에 대한 설명과 저장 프로시저를 이용한 예제 프로그램을 설명한다. 문서화 규칙 이 절에서는 이 매뉴얼에서 사용하는 규칙에 대해 설명한다. 이 규칙을 이해하면 이 매뉴얼과 설명서 세트의 다른 매뉴얼에서 정보를 쉽게 찾을 수 있다. 여기서 설명하는 규칙은 다음과 같다. y 구문 다이어그램 y 샘플 코드 규칙 iv ALTIBASE5 Stored Procedure User’s Manual 구문 다이어그램 이 매뉴얼에서는 다음 구성 요소로 구축된 다이어그램을 사용하여, 명령문의 구문을 설명한다. 구성 요소 의미 예약어 명령문이 시작한다. 완전한 명령문이 아닌 구문 요소는 화살표로 시작한다. 명령문이 다음 라인에 계속된다. 완전한 명령문이 아닌 구문 요소는 이 기호로 종료한다. 명령문이 이전 라인으로부터 계속된다. 완전한 명령문이 아닌 구문 요소는 이 기호로 시작한다. ; 명령문이 종료한다. SELECT 필수 항목 NOT 선택적 항목 ADD DROP 선택사항이 있는 필수 항목. 한 항목만 제공해야 한다. ASC DESC 선택사항이 있는 선택적 항목. , ASC DESC 선택적 항목. 여러 항목이 허용된다. 각 반복 앞부분에 콤마가 와야 한다. 샘플 코드 규칙 코드 예제는 SQL, Stored Procedure, iSQL, 또는 다른 명령 라인 구문들을 예를 들어 설명한다. 아래 테이블은 코드 예제에서 사용된 인쇄 규칙에 대해 설명한다. 규칙 의미 예제 [ ] 선택 항목을 표시 VARCHAR [(size)] [[FIXED |] VARIABLE] { } 필수 항목 표시. 반드시 하나 이상을 { ENABLE | DISABLE | 서문 v 선택해야 되는 표시 COMPILE } | 선택 또는 필수 항목 표시의 인자 구분 표시 { ENABLE | DISABLE | COMPILE } [ ENABLE | DISABLE | COMPILE ] . . . 그 이전 인자의 반복 표시 예제 코드들의 생략되는 것을 표시 SQL> SELECT ename FROM employee; ENAME ------------------ ------ SWNO HJNO HSCHOI . . . 20 rows selected. 그 밖에 기호 위에서 보여진 기호 이 외에 기호들 EXEC :p1 := 1; acc NUMBER(11,2); 기울임 꼴 구문 요소에서 사용자가 지정해야 하는 변수, 특수한 값을 제공해야만 하는 위치 지정자 SELECT * FROM table_name; CONNECT userID/password; 소문자 사용자가 제공하는 프로그램의 요소들, 예를 들어 테이블 이름, 칼럼 이름, 파일 이름 등 SELECT ename FROM employee; 대문자 시스템에서 제공하는 요소들 또는 구문에 나타나는 키워드 DESC SYSTEM_.SYS_INDICES_; 관련 자료 자세한 정보를 위하여 다음 문서 목록을 참조하기 바란다. y Altibase Administration Installation User’s Manual y Altibase Administration Starting User’s Manual y Altibase Application Development SQL User’s Manual y Altibase Tools iSQL User’s Manual y Altibase Message Error Message Reference vi ALTIBASE5 Stored Procedure User’s Manual 온라인 매뉴얼 알티베이스 테크니컬 센터(http://atc.altibase.com/)에서 국문 및 영문 매뉴얼(PDF, HTML)을 받을 수 있다. 알티베이스는 여러분의 의견을 환영합니다. 이 매뉴얼에 대한 여러분의 의견을 보내주시기 바랍니다. 사용자의 의견은 다음 버전의 매뉴얼을 작성하는데 많은 도움이 됩니다. 보내실 때에는 아래 내용과 함께 기술지원센터(support@altibase.com)로 보내주시기 바랍니다. y 사용 중인 매뉴얼의 이름과 버전 y 매뉴얼에 대한 의견 y 사용자의 성함, 주소, 전화번호 이 외에도 알티베이스 기술지원 설명서의 오류와 누락된 부분 및 기타 기술적인 문제들에 대해서 이 주소로 보내주시면 정성껏 처리하겠습니다. 기술적인 부분과 관련하여 즉각적인 도움이 필요한 경우에는 기술지원센터로 연락하시기 바랍니다. 여러분의 의견에 항상 감사드립니다. 저장 프로시저 SQL문 7 1. 저장 프로시저 8 ALTIBASE5 Stored Procedure User’s Manual 저장 프로시저의 개요 저장 프로시저(Stored Prodedure)란 SQL 문들과 흐름 제어문, 할당문, 오류 처리 루틴 등을 이용해서 업무 절차를 하나의 서버 모듈로 만든 후 데이터베이스에 영구적으로 저장해 두고, 모듈 이름만을 호출하여 업무프로세스를 서버에서 수행할 수 있게 해주는 데이터베이스 객체(Object)이다. 저장 프로시저의 종류 저장 프로시저 입력 인자, 출력 인자, 입출력 인자를 가지고 프로시저 내에 정의된 절차에 따라서 SQL 문을 수행하는 데이터베이스 처리 기능이다. 반환값을 가지지 않으며 출력 인자와 입출력 인자들을 통해 클라이언트에게 값을 전달한다. 하나의 반환 값을 갖지 않기 때문에 SQL 문의 표현식 내에서 피연산자로 사용될 수 없다. 저장 함수 저장 프로시저와 동일하나 하나의 반환 값을 가지는 함수를 의미한다. 저장 프로시저와 달리 하나의 반환 값을 가지므로 SQL 문의 연산식(expression)내에서 피연산자로 사용할 수 있다. 타입 세트 저장 프로시저의 사용자 정의 타입들을 정의한 집합이다. 주로 저장 프로시저끼리 파라미터 또는 리턴값으로 사용자 정의 타입을 주고받을 때 사용한다. 자세한 내용은 타입 세트 부분에서 다룬다. 저장 프로시저의 특징 SQL 구문을 이용한 절차적 프로그램 SQL 문과 흐름 제어문을 동시에 사용할 수 있으므로 SQL 문으로 절차적 프로그래밍이 가능하다. 빠른 성능 여러 SQL 문을 수행하는 클라이언트 프로그램의 경우에 SQL 문 수행시 마다 데이터베이스 서버와 통신을 해야 하는 반면 저장 프로시저로 작성된 프로그램은 한번의 통신만으로 여러 SQL 문을 수행할 수 있다. 저장 프로시저를 사용하는 경우에 통신 부하의 감소와 함께 데이터베이스 서버와 클라이언트 프로그램의 프로그래밍 저장 프로시저 SQL문 9 언어의 차이로 발생하는 묵시적인 데이터 변환의 부하도 크게 줄어 들어 보다 빠른 데이터 처리가 가능하다. 모듈화 업무 절차를 하나의 저장 프로시저로 묶어 모듈화하여 관리할 수 있다. 소스 관리의 용이성 저장 프로시저는 데이터베이스 서버에 저장되는 모듈이므로 프로그램을 여러 곳으로 옮길 필요가 없어 프로그램 관리가 용이하다. 소스 공유와 생산성 한 사용자가 생성한 저장 프로시저는 데이터베이스에 저장되므로 접근 권한이 부여된 또 다른 사용자도 해당 저장 프로시저를 실행할 수 있어 서로 공유할 수 있을 뿐만 아니라, 한 저장 프로시저 내에서 다른 저장 프로시저의 호출이 가능하므로 같은 업무 절차의 재 프로그래밍이 필요 없으므로 생산성을 높일 수 있다. SQL 과 통합성 저장 프로시저 내의 흐름 제어문의 조건절은 SELECT 문의 조건절을 그대로 사용할 수 있으므로 C/C++등의 주 언어의 흐름 제어문의 조건절에서는 제공되지 않는 SQL 문 스타일의 기능을 제공하며, 연산식에 부질의(subquery)를 사용하거나 SQL 문이 지원하는 시스템 제공 함수들을 그대로 사용할 수 있다는 점 등 SQL 문과 밀착된 프로그래밍이 가능하다. 에러 및 예외처리 저장 프로시저 내에서 Exception Handler 를 제공하므로 SQL 문 수행 도중 오류가 발생했을 때 적절한 대응 조치를 서버 내에서 바로 처리할 수 있다. 저장성 저장 프로시저 또한 데이터베이스 객체이기 때문에 사용자가 삭제하기 전까지 데이터베이스 내에 저장된다. 따라서 업무 절차 또한 데이터베이스에 저장하여 보존시킬 수 있다. 트랜잭션 관리 저장 프로시저 내에서 사용 가능한 트랜잭션 제어문은 COMMIT, ROLLBACK 문이다. 저장 프로시저에서 사용한 트랜잭션 제어문은 저장 프로시저 밖의 작업에도 영향을 미칠 수 있다. 예를 들어서 NON-AUTOCOMMIT 모드에서 다음과 같은 작업을 수행했다고 가정하자. 10 ALTIBASE5 Stored Procedure User’s Manual iSQL> INSERT INTO t1 values (1); iSQL> INSERT INTO t1 values (2); iSQL> EXECUTE proc1; proc1 이 INSERT INTO t1 values (3)을 수행하고 ROLLBACK 을 수행한다면 3 뿐만 아니라 1 과 2 도 ROLLBACK 된다. 즉, 위의 두 INSERT 문과 EXECUTE 문은 하나의 트랜잭션으로 처리된다. 관련 메타 테이블 저장 프로시저 관련 메타 테이블에 대한 자세한 내용은 Administrator’s Manual의 데이터 딕셔너리 부분을 참조한다. 주의 사항 저장 프로시저 내에 COMMIT, ROLLBACK 을 사용할 경우 현재 커서가 OPEN 된 상태에서는 사용할 수 없다. 커서 관련문의 경우 반드시 한 트랜잭션 내에서 OPEN, FETCH, CLOSE 가 이루어져야 한다. SELECT 문 내에서 호출한 저장 함수의 경우 저장 함수 내에 INSERT, UPDATE, DELETE 문은 사용할 수 없으며, 트랜잭션 관련문도 수행할 수 없다. INSERT, UPDATE, DELETE 문 내에서 호출한 저장 함수의 경우에는 트랜잭션 관련문을 수행할 수 없다. DECLARE 로 LOB 타입을 사용할 수 없다. %TYPE, %ROWTYPE 으로 LOB 타입을 사용할 수 없다. LOB 타입을 커서에서 사용할 수 없다. 저장 프로시저 SQL문 11 저장 프로시저의 구조 저장 프로시저는 블록 구조 언어로 하나의 저장 프로시저는 여러 개의 논리적인 블록들로 구성되어 있다. 저장 프로시저는 크게 헤더와 바디로 나뉘어 지고 바디는 하나의 큰 블록으로 선언부, 바디, 예외 처리부의 세 부분으로 나뉘어 진다. 바디는 다시 여러 개의 하위 블록들을 가질 수 있다. 저장 프로시저 구조를 예를 들어 설명하면 다음과 같다. CREATE FUNCTION myCheck (id IN INTEGER, ...... ) RETURN INTEGER DECLARE v_name CHAR(20); v_salary INTEGER; comm_missing EXCEPTION; CURSOR ...... BEGIN ...... IF id > 10000 THEN ...... DECLARE ...... BEGIN ...... END; ...... END IF; RAISE com_missing; ...... RETURN v_salary; ...... EXCEPTION ...... WHEN comm_missing THEN ...... ...... END; 블록 2 는 블록 1 의 하위 블록으로 블록 1 과 같은 구조로 구성될 수 있다. 흐름 제어문도 명시적인 시작과 끝을 알 수 있는 하나의 블록이다. 저장 프로시저 헤더 저장 프로시저 바디 (블록1) 블록1 선언부 블록1 바디 블록1 예외 처리부 블록2 저장 프로시저 SQL문 13 2. 저장 프로시저 S QL 문 14 ALTIBASE5 Stored Procedure User’s Manual 개요 저장 프로시저 SQL 문 종류 관련문장 설명 생성 CREATE [OR REPLACE] PROCEDURE 문 새로운 저장 프로시저를 생성하거나 이미 생성된 저장 프로시저의 정의를 변경하는 문장이다. CREATE [OR REPLACE] FUNCTION 문 새로운 저장 함수를 생성하거나 이미 생성된 저장 함수의 정의를 변경하는 문장이다. CREATE [OR REPLACE] TYPESET 문 타입 세트를 생성 또는 변경하는 문장이다. 변경 ALTER PROCEDURE 문 이미 생성되어 있는 저장 프로시저를 매뉴얼하게 다시 컴파일해서 생성한다. ALTER FUNCTION 문 저장 함수 생성 이후 관련 객체들의 정의가 변경되어 현재 저장 함수의 실행계획이 최적화된 상태가 아닐 경우 이를 재 컴파일 해서 최적화된 실행 계획을 생성하는 문장이다. 삭제 DROP PROCEDURE 문 생성된 저장 프로시저를 삭제 하는 문장이다. DROP FUNCTION 문 생성된 저장 함수를 삭제 하는 문장이다. DROP TYPESET 문 생성된 타입 세트를 삭제 하는 문장이다. 실행 EXECUTE 문 저장 프로시저 또는 저장 함수를 실행하는 문장이다. FUNCTION SQL문 내에서 built-in function과 같은 형태로 사용할 수 있다. 데이터 타입 저장 프로시저 SQL문 15 저장 프로시저에서는 다음과 같은 데이터 타입을 지원한다. y Primitive 타입 - 일반 데이터 타입 : SQL 구문에서 사용가능한 데이터 타입으로 자세한 사항은SQL User’s Manual 에 있는 데이터 타입 부분을 참조한다. - BOOLEAN 타입 : 저장 프로시저 내에서만 사용 가능하며, TRUE, FALSE의 값을 가진다. y FILE_TYPE 저장 프로시저 내에서만 사용 가능하며, 파일 제어를 위한 타입이다. 자세한 내용은 9장 파일 제어를 참조한다. y 사용자 정의 타입 저장 프로시저 내에서만 사용 가능하며, RECORD 및 ASSOCIATIVE ARRAY를 지원한다. 자세한 내용은 사용자 정의 타입 부분을 참조한다. 16 ALTIBASE5 Stored Procedure User’s Manual CREATE PROCEDURE 구문 CREATEprocedure_name create_procedure::= PROCEDURE parameter_declaration() , AS IS BEGIN declaration_section statement EXCEPTIONexception_handler END procedure_name parameter_declaration ::= parameter_name IN OUT INOUT data_type DEFAULT := expression ORREPLACEuser_name. 저장 프로시저 SQL문 17 기능 저장 프로시저를 새로 생성하거나 이미 생성되어 있는 저장 프로시저를 새로운 저장 프로시저로 변경하는 기능을 수행한다. parameter_declaration 파라미터는 생략될 수 있으며, 파라미터를 명시할 경우엔 파라미터의 명칭, 테이터타입 및 입출력 구분 및 을 명시해야 한다. 사용 가능한 입출력 구분값은 다음 세가지 중의 하나이고 생략시에 IN 이 기본값이다. y IN: 프로시저 호출시 입력값으로 주어지는 입력 파라미터 y OUT: 프로시저 실행 후 반환 값을 출력하는 파라미터 y INOUT: 입출력 공용 파라미터 입력 파라미터인 IN 파라미터는 저장 프로시저 내에서 상수와 같이 동작하므로 할당문을 사용해 값을 할당할 수 없으며 SELECT 문의 INTO 절에는 사용할 수 없다. IN 파라미터가 숫자형 파라미터인 경우에 프로시저 호출시에 그 값은 묵시적으로 정수값(integer)으로 인식된다. 따라서 정수값이 아닌 숫자형 데이터를 그대로 전달 하고자 할 때에는 반드시 타입 변환구(type casting)를 넣어서 값을 전달하여야 한다. 예를 들어서 임의의 프로시저 PROC1 을 실행할 때 EXEC PROC1 (9223372036854775807);을 실행한다면 전달될 값이 정수값로 인식되므로 ERR-21010: Value overflow error 가 발생하는 것을 볼 수 있다. 따라서, bigint 형의 값을 전달하기 위하여 다음과 같이 전달될 값 앞에 데이터 타입을 기술해서 실행해야만 한다. EXEC PROC1 (bigint'9223372036854775807'); 파라미터는 기본 값을 가질 수 있으며 저장 프로시저가 호출될 때 파라미터에 값을 넘겨 주지 않을 경우 기본 값으로 수행하게 된다. declaration_section ‘지역 변수 선언’ 참고 data_type ‘지역 변수 선언’ 참고 Exception Handler ‘Exception Handler’ 참고 실행 SQL 문 또는 저장 프로시저 제어문 끝에 세미콜론(;)을 입력한다. iSQL 에서 CREATE PROCEDURE 문을 실행할 때는 마지막 END 18 ALTIBASE5 Stored Procedure User’s Manual 세미콜론(;) 이후에 반드시 슬래시(/)를 사용해야 프로시저 생성문이 수행된다. CREATE PROCEDURE 문을 실행시 컴파일 오류가 발생하지 않고 블록이 성공적으로 컴파일 되면 ‘Create Success’ 메시지가 출력된다. 저장 프로시저 바디 부분에 대해서는 다음 장에서부터 각각 블록, 흐름 제어문, 커서, Exception Handler 부분으로 구분해서 설명한다. 예제 예제 1 --########################### -- Using the IN mode --########################### CREATE TABLE t1 (i1 INTEGER UNIQUE, i2 INTEGER, i3 INTEGER); INSERT INTO t1 VALUES (1,1,1); INSERT INTO t1 VALUES (2,2,2); INSERT INTO t1 VALUES (3,3,3); INSERT INTO t1 VALUES (4,4,4); INSERT INTO t1 VALUES (5,5,5); SELECT * FROM t1; CREATE OR REPLACE PROCEDURE proc1 (p1 IN INTEGER, p2 IN INTEGER, p3 IN INTEGER) AS v1 INTEGER; v2 t1.i2%type; v3 INTEGER; BEGIN SELECT * INTO v1, v2, v3 FROM t1 WHERE i1 = p1 AND i2 = p2 AND i3 = p3; IF v1 = 1 AND v2 = 1 AND v3 = 1 THEN UPDATE t1 SET i2 = 7 WHERE i1 = v1; ELSIF v1 = 2 AND v2 = 2 AND v3 = 2 THEN UPDATE t1 SET i2 = 7 WHERE i1 = v1; ELSIF v1 = 3 AND v2 = 3 AND v3 = 3 THEN UPDATE t1 SET i2 = 7 WHERE i1 = v1; ELSIF v1 = 4 AND v2 = 4 AND v3 = 4 THEN UPDATE t1 SET i2 = 7 WHERE i1 = v1; ELSE DELETE FROM t1; END IF; INSERT INTO t1 VALUES (p1+10, p2+10, p3+10); END; / iSQL> EXEC proc1 (2,2,2); Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 저장 프로시저 SQL문 19 ------------------------------- 1 1 1 3 3 3 4 4 4 5 5 5 2 7 2 12 12 12 6 rows selected. 예제 2 (PARAMETER DEFAULT) CREATE TABLE t1 (i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE OR REPLACE PROCEDURE proc1 (p1 IN INTEGER DEFAULT 1, p2 IN INTEGER DEFAULT 1, p3 IN INTEGER DEFAULT 1) AS BEGIN INSERT INTO t1 VALUES (p1, p2, p3); END; / EXEC proc1; SELECT * FROM t1; EXEC proc1(2); SELECT * FROM t1; EXEC proc1(3,3); SELECT * FROM t1; EXEC proc1(4,4,4); iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ------------------------------- 1 1 1 2 1 1 3 3 1 4 4 4 4 rows selected. 예제 3 CREATE OR REPLACE PROCEDURE proc1 (emp_id INTEGER, amount NUMBER(10,2) AS BEGIN UPDATE employee SET salary = salary + amount WHERE eno = emp_id; END; / iSQL> EXEC proc1(15, '250000'); Execute success. iSQL> SELECT * FROM employe WHERE eno=15; EMPLOYEE.ENO EMPLOYEE.ENAME EMPLOYE.EMP_JOB ----------------------------------- EMPLOYE.EMP_TEL EMPLOYE.DNO EMPLOYE.SALARY EMPLOYEE.SEX ----------------------------------- EMPLOYE.BIRTH EMPLOYE.JOIN_DATE EMPLOYEE.STATUS ----------------------------------- 20 ALTIBASE5 Stored Procedure User’s Manual 15 JHSEOUNG WEBMASTER 01195568840 C01 1250000 M 0514 H 1 row selected. 예제 4 --########################### - Using the OUT mode --########################### CREATE TABLE t4(i1 INTEGER, i2 INTEGER); INSERT INTO t4 VALUES(1,1); INSERT INTO t4 VALUES(1,1); INSERT INTO t4 VALUES(1,1); INSERT INTO t4 VALUES(1,1); INSERT INTO t4 VALUES(1,1); CREATE OR REPLACE PROCEDURE proc1(a1 OUT INTEGER, a2 IN OUT INTEGER) AS BEGIN SELECT COUNT(*) INTO a1 FROM t4 WHERE i2 = a2; END; / iSQL> VAR t3 INTEGER; iSQL> VAR t4 INTEGER; iSQL> EXEC :t4 := 1; Execute success. iSQL> EXEC proc1(:t3, :t4); Execute success. iSQL> PRINT t3; NAME TYPE VALUE ----------------------------------- T3 INTEGER 5 예제 5 CREATE OR REPLACE PROCEDURE proc1(p1 INTEGER, p2 IN OUT INTEGER, p3 OUT INTEGER) AS BEGIN p2 := p1; p3 := p1 + 10; END; / iSQL> VAR v1 INTEGER; iSQL> VAR v2 INTEGER; iSQL> VAR v3 INTEGER; iSQL> EXEC :v1 := 3; Execute success. iSQL> EXEC proc1(:v1, :v2, :v3); Execute success. iSQL> PRINT VAR; [ HOST VARIABLE ] ----------------------------------- NAME TYPE VALUE ----------------------------------- T4 INTEGER 1 저장 프로시저 SQL문 21 V1 INTEGER 3 V2 INTEGER 3 V3 INTEGER 103 T3 INTEGER 5 예제 6 --########################### -- Using the IN OUT mode --########################### CREATE TABLE t3(i1 INTEGER); INSERT INTO t3 VALUES(1); INSERT INTO t3 VALUES(1); INSERT INTO t3 VALUES(1); CREATE OR REPLACE PROCEDURE proc1(a1 IN OUT INTEGER) AS BEGIN SELECT COUNT(*) INTO a1 FROM t3 WHERE i1 = a1; END; / iSQL> VAR p1 INTEGER; iSQL> EXEC :p1 := 1; Execute success. iSQL> EXEC proc1(:p1); Execute success. iSQL> PRINT p1; NAME TYPE VALUE ----------------------------------- P1 INTEGER 3 예제 7 CREATE OR REPLACE PROCEDURE proc1(p1 INTEGER, p2 IN OUT INTEGER, p3 OUT INTEGER) AS BEGIN p2 := p1 + p2; p3 := p1 + 10; END; / iSQL> VAR v1 INTEGER; iSQL> VAR v3 INTEGER; iSQL> EXEC :v1 := 3; Execute success. iSQL> EXEC :v2 := 5; Execute success. iSQL> EXEC proc1(:v1, :v2, :v3); Execute success. iSQL> PRINT VAR; [ HOST VARIABLE ] ----------------------------------- NAME TYPE VALUE ----------------------------------- T4 INTEGER 1 P1 INTEGER 3 V1 INTEGER 3 V2 INTEGER 8 22 ALTIBASE5 Stored Procedure User’s Manual V3 INTEGER 103 T3 INTEGER 5 주의사항 CREATE PROCEDURE 구문에서는 파라미터로 LOB 타입을 사용할 수 없다. 저장 프로시저 SQL문 23 ALTER PROCEDURE 구문 ALTERPROCEDUREprocedure_name alter_procedure_statement := ;COMPILE user_name. 기능 저장 프로시저 생성 이후에 이와 관련된 테이블, 시퀀스등의 데이터베이스 오브젝트 혹은 이 저장 프로시저가 호출하는 다른 저장 프로시저, 저장 함수등이 변경되어서 현재 이 저장 프로시저의 실행 계획으로 실행할 수 없는 경우에 이 저장 프로시저는 무효한(invalid) 상태라고 한다. 예를 들면 처음 저장 프로시저 생성 시 존재하던 인덱스가 삭제된 경우 이전 실행 계획 트리는 인덱스를 통해 테이블에 접근하도록 계획되어 있으므로 이전의 실행 계획을 이용해서 테이블에 접근할 수 없게 된다. ALTER PROCEDURE 문은 사용자가 명시적으로 저장 프로시저를 재 컴파일해서 무효한 저장 프로시저를 유효한(valid) 상태의 실행 계획을 재 생성하거나 할 때 사용한다.. 예제 예제 1 CREATE TABLE t1 (i1 NUMBER, i2 VARCHAR(10), i3 DATE); CREATE OR REPLACE PROCEDURE proc1 (p1 INUMBER, p2 IN VARCHAR(10), p3 IN DATE) AS BEGIN IF p1 > 0 then INSERT INTO t1 VALUES (p1, p2, p3); END IF; END; / 24 ALTIBASE5 Stored Procedure User’s Manual iSQL> EXECUTE proc1 (1, 'seoul', '20-JUN-2002'); Execute success. iSQL> EXECUTE proc1 (-3, 'daegu', '21-APR-2002'); Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ----------------------------------- 1 seoul 202/06/20 00:00:00 1 row selected. 예제 2 CREATE TABLE t1 (i1 NUMBER, i2 VARCHAR(10), i3 DATE DEFAULT SYSDATE); ALTER PROCEDURE proc1 COMPILE; iSQL> EXECUTE proc1 (2, 'incheon', SYSDATe); Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ----------------------------------- 2 incheon 000/00/01 00:00:78 1 row selected. 저장 프로시저 SQL문 25 DROP PROCEDURE 구문 DROPPROCEDUREproced ure_nam e drop_procedure_statem ent := ; user_n am e. 기능 데이터베이스로부터 저장 프로시저를 삭제하는 구문이다. 삭제되고 없는 저장 프로시저나 함수가 호출될 때 알티베이스는 오류 코드를 반환한다. 예제 DROP PROCEDURE proc1; 26 ALTIBASE5 Stored Procedure User’s Manual EXECUTE 구문 EXEC(UTE)procedure_name; execute_procedure_statement := EXEC(UTE)function_name; execute_function_statement := variable:= user_name. user_name.() expression , () expression , 기능 저장 프로시저 또는 저장 함수를 실행한다. 예제 CREATE OR REPLACE PROCEDURE proc1(eid INTEGER, amount NUMBER(10,2)) AS current_salary NUMBER(10,2); BEGIN SELECT salary INTO current_salary FROM employee WHERE eno = eid; UPDATE employee SET salary = salary + amount WHERE eno = eid; END; / iSQL> SELECT * FROM employe WHERE eno = 15; EMPLOYEE.ENO EMPLOYEE.ENAME EMPLOYE.EMP_JOB 저장 프로시저 SQL문 27 ----------------------------------- EMPLOYE.EMP_TEL EMPLOYE.DNO EMPLOYE.SALARY EMPLOYEE.SEX ----------------------------------- EMPLOYE.BIRTH EMPLOYE.JOIN_DATE EMPLOYEE.STATUS ----------------------------------- 15 JHSEOUNG WEBMASTER 01195568840 C001 1250000 M 0514 H 1 row selected. iSQL> EXEC proc1(15, 333333); Execute success. iSQL> SELECT * FROM employe WHERE eno = 15; EMPLOYE.ENO EMPLOYEE.ENAME EMPLOYE.EMP_JOB ----------------------------------- EMPLOYE.EMP_TEL EMPLOYE.DNO EMPLOYE.SALARY EMPLOYEE.SEX ----------------------------------- EMPLOYE.BIRTH EMPLOYE.JOIN_DATE EMPLOYEE.STATUS ----------------------------------- 15 JHSEOUNG WEBMASTER 01195568840 C001 1583333 M 0514 H 1 row selected. 28 ALTIBASE5 Stored Procedure User’s Manual CREATE FUNCTION 구문 CREATEfunction_name functi on_decl ar ati on ::= FUNCTION parameter_declaration() , AS IS BEGIN declaration_section exception_handler END function_name parameter_declaration ::= parameter_name IN OUT INOUT data_type DEFAULT := expression RETURNdata_type ; statement user_name.ORREPLACE EXCEPTION 저장 프로시저 SQL문 29 기능 저장 함수를 새로 생성하거나 이미 생성되어 있는 저장 함수를 대체한다. parameter_declaration 파라미터는 다음 세가지로 정의 될 수 있고 생략시에는 IN 이 기본 값이다. y IN: 함수 호출시 입력되는 입력 파라미터 y OUT :함수 실행 후 반환 값을 출력하는 파라미터 y INOUT : 입출력 공용 파라미터 IN 파라미터의 경우 저장 함수 내에서 상수처럼 동작하므로 할당문을 사용해서 값을 할당할 수 없으며 SELECT 문의 INTO 절에 사용할 수 없다. 파라미터는 기본값을 가질 수 있고, 저장 함수가 호출될 때 파라미터에 값을 넘겨 주지 않을 경우 기본값으로 수행하게 된다. iSQL 에서 CREATE FUNCTION 문을 실행할 때는 마지막 END 세미콜론(;) 이후에 반드시 슬래시(/)를 사용해야 CREATE FUNCTION 문이 수행 된다. 저장 함수 바디에 대해서는 다음 장에서부터 각각 블록, 흐름 제어문, 커서 관련문, Exception Handler 로 구분지어 기술된다. RETURN 저장 함수는 저장 프로시저와 달리 하나의 실행 후 하나의 값을 반환해 주는 것으로 반드시 반환 데이터 타입을 명시해야 한다. Data Type 지역 변수 선언 섹션 참고 Declaration Section 지역 변수 선언 섹션 참고 Exception Handler Exception Handler 섹션 참고 예제 CREATE TABLE t1( seq_no INTEGER, user_id VARCHAR(9), rate NUMBER, start_date DATE, 30 ALTIBASE5 Stored Procedure User’s Manual end_date DATE); INSERT INTO t1 VALUES(0, '000000500', 200.50, '23-May-2002', '23-Apr- 2002'); INSERT INTO t1 VALUES(0, '000000501', 190, '23-Nov-2002', '23-Dec- 2002'); INSERT INTO t1 VALUES(0, '000000523', 100, '12-Dec-2001', '12-Jan- 2001'); INSERT INTO t1 VALUES(0, '000000532', 100, '11-Dec-2001', '11-Jan- 2002'); INSERT INTO t1(seq_no, user_id, start_date, end_date) VALUES(0, '000000524', '30-Oct-2001', '30-Nov-2001'); INSERT INTO t1 VALUES(0, '000000524', 200.50, '30-Apr-2002', '30-May- 2002'); INSERT INTO t1 VALUES(0, '000000524', 200.50, '30-Apr-2002', '30-May- 2002'); INSERT INTO t1 VALUES(1, '000000524', 100, '30-Apr-2002', '30-May- 2002'); INSERT INTO t1 VALUES(1, '000000524', 115.0, '19-Jan-2002', '19-Mar- 2002'); INSERT INTO t1 VALUES(0, '000000502', 120.0, '27-Jan-2002', '27-Feb- 2002'); INSERT INTO t1 VALUES(1, '000000504', 150.0, '26-Nov-2001', '26-Dec- 2001'); iSQL> SELECT * FROM t1; T1.SEQ_NO T1.USER_ID T1.RATE T1.START_DATE --------------------------------------------- T1.END_DATE ----------------- 0 00000500 200.5 202/05/23 00:00:0 2002/04/23 0:00:00 0 00000501 190 2002/11/23 0:00:00 2002/12/23 0:00:00 0 00000523 100 2001/12/12 0:00:00 2001/01/12 0:00:00 0 00000532 100 2001/12/11 0:00:00 2002/01/11 0:00:00 0 00000524 2001/10/30 00:00:00 2001/11/30 0:00:00 0 00000524 200.5 202/04/30 00:00:0 2002/05/30 0:00:00 0 00000524 200.5 202/04/30 00:00:0 2002/05/30 0:00:00 1 00000524 100 2002/04/30 0:00:00 2002/05/30 0:00:00 1 00000524 115 2002/01/19 0:00:00 2002/03/19 0:00:00 0 00000502 120 2002/01/27 0:00:00 2002/02/27 0:00:00 1 00000504 150 2001/11/26 0:00:00 2001/12/26 0:00:00 11 rows selected. CREATE OR REPLACE FUNCTION get_rate (p1 IN CHAR(30), p2 IN CHAR(30), p3 IN VARCHAR(9)) RETURN NUMBER AS v_rate NUMBER; BEGIN 저장 프로시저 SQL문 31 SELECT NVL(SUM(rate), 0) INTO v_rate FROM (SELECT rate FROM t1 WHERE start_date = TO_DATE(p1) AND end_date = TO_DATE(p2) AND user_id = '0' | p3 AND seq_no = 0); RETURN v_rate; END; / iSQL> VAR res NUMBER; iSQL> EXECUTE :res := get_rate('30-Apr-2002', '30-May-2002', '524'); Execute success. iSQL> PRINT res; NAME TYPE VALUE ----------------------------------- RES NUMBER 401 주의사항 CREATE FUNCTION 구문에서는 파라미터로 LOB 타입을 사용할 수 없다. 반환 값으로 LOB 타입을 사용할 수 없다. 32 ALTIBASE5 Stored Procedure User’s Manual ALTER FUNCTION 구문 ALTERFUNCTIONfunction_name alter_function_statement := ;COMPILE user_name. 기능 저장 함수와 관련된 테이블, 시퀀스, 이 저장 함수가 호출하는 다른 저장 프로시저 또는 저장 함수가 생성시 정의된 상태와 달라 현재 이 저장 함수의 실행 계획 트리로 실행할 수 없을 경우 현재 이 저장 함수는 무효한 상태라고 한다. 예를 들면 처음 저장 함수 생성시 존재하던 인덱스가 삭제된 경우 이전 실행 계획 트리는 인덱스를 통해 테이블에 접근하도록 계획되어 있으므로 이전 실행 계획을 사용해 테이블에 접근할 수 없게 된다. ALTER FUNCTION 문은 무효한 저장 함수를 재 컴파일하여 유효한 상태의 실행 계획을 재생성하는 기능을 수행한다. 예제 CREATE OR REPLACE FUNCTION get_dept_name(p_emp_id IN INTEGER) RETURN CHAR(20) AS v_dept_id BYTE(2); v_dept_name CHAR(20); BEGIN SELECT dno INTO v_dept_id FROM employee WHERE eno = p_emp_id; SELECT dname INTO v_dept_name FROM department WHERE dno = v_dept_id; RETURN v_dept_name; 저장 프로시저 SQL문 33 END; / iSQL> VAR dept_name CHAR(20); iSQL> EXEC :dept_name := get_dept_name(15); Execute success. iSQL> PRINT dept_name; NAME TYPE VALUE ----------------------------------- DEPT_NAME CHAR(20) 마케팅팀 iSQL> SELECT status FROM system_.sys_procedures_ WHERE proc_name = 'GET_DEPT_NAME'; STATUS --------- 0 1 row selected. iSQL> DROP INDEX emp_idx1; Drop success. iSQL> SELECT status FROM system_.sys_procedures_ WHERE proc_name = 'GET_DEPT_NAME'; STATUS --------- 1 1 row selected. iSQL> ALTER FUNCTION get_dept_name COMPILE; Alter success. iSQL> SELECT status FROM system_.sys_procedures_ WHERE proc_name = 'GET_DEPT_NAME'; STATUS --------- 0 1 row selected. 34 ALTIBASE5 Stored Procedure User’s Manual DROP FUNCTION 구문 DROPFUNCTIONfunction_name drop_function_statement ::= ; user_name. 기능 저장 함수를 삭제하는 구문이다. 이미 삭제된 저장 함수를 참조하고 있던 임의의 저장 프로시저 또는 저장 함수가 실행될 때 알티베이스는 오류를 출력한다. 예제 DROP FUNCTION get_dept_name; 저장 프로시저 블록 35 3. 저장 프로시저 블록 저장 프로시저와 저장 함수는 블록으로 구성된다. 이 장에서는 블록을 사용하는 방법을 설명한다. 36 ALTIBASE5 Stored Procedure User’s Manual 저장 프로시저 블록 구문 저장 프로시저 블록 37 블록은 크게 선언부(Declare Section), 블록 바디(Block Body), 예외처리(Exception Handler)의 세 부분으로 나뉘어진다. DECLARE, BEGIN, EXCEPTION 등의 키워드 뒤에는 세미콜론을 사용하지 않지만 END 및 기타 모든 저장 프로시저 문에는 세미콜론이 있어야 명령문을 종료할 수 있다. 저장 프로시저의 코드에 주석 처리를 할 수 있다. 단일 행 주석에는 ‘‘를 문장 앞에 붙이고, 여러 행을 주석 처리 할 결우는 ‘/*’와 ‘*/’ 사이에 주석을 작성한다. 이 장에서는 선언부와 블록 바디에 사용할 수 있는 구문 중 SELECT INTO 문, 변수 할당문, PRINT 문, RETURN 문에 대해서 설명한다. 저장 프로시저 내에서 사용 가능한 흐름 제어문, 커서 관련문, 예외 처리에 관련된 내용은 다음 장에서 설명하고 있으며 그외 SQL 문들에 대한 자세한 내용은 SQL User’s Manual을 참조한다. 선언부 선언부는 DECLARE 에서 BEGIN 사이의 부분으로 지역 변수, 커서, 사용자 정의 예외 등을 선언하는 부분이다. 38 ALTIBASE5 Stored Procedure User’s Manual 블록 바디 BEGIN 과 END 사이의 부분으로 흐름 제어문과 SQL 문 등을 기술하는 부분이다. 블록 바디 내에 기술 가능한 구문은 다음과 같다. y 일반적인 DML문: enqueue 관련 DML 중에서 dequeue는 사용할 수 없다 y 트랜잭션 처리문: COMMIT, ROLLBACK y 흐름 제어문: IF, CASE, FOR, LOOP, WHILE, EXIT, CONTINUE, NULL y 할당문, PRINT문, RETURN문 y 커서 관련문: OPEN, FETCH, CLOSE, cursor FOR LOOP y 동적 SQL문: EXECUTE IMMEDIATE y 예외처리 구문- RAISE문 저장 프로시저의 장점 중 하나는 SQL 문과 달리 명령문을 중첩할 수 있다는 점이다. 실행문이 사용 가능한 경우 언제라도 블록을 중첩할 수 있으므로 중첩 블록을 명령문으로 만들 수 있다. 예외처리부 EXCEPTION 과 END 사이의 부분으로 오류 발생 시 처리할 루틴을 기술하는 부분이다. 저장 프로시저 블록 39 지역 변수 선언 구문 declaration_section := variable_declaration constant_declaration cursor_declaration exception_declaration variable_declaration ::= variable_namedata_type expression DEFAULT := constant_declaration := constant_nam eCONSTANTdata_typeexpression DEFAULT := data_type := sql_data_type type_attribute row type_attribute 40 ALTIBASE5 Stored Procedure User’s Manual type_attreibute ::= table_name .column_name record_name variable_name rowtype_attreibute := %TYPE cursor_name table_name %ROWTYPE 기능 Variable Name 변수의 이름은 하나의 블록 범위 내에서는 유일한 이름이어야 한다. SQL 문장 내에서 칼럼과 같은 이름의 변수가 존재할 경우 칼럼 명으로 인식된다. 다음의 예에서 eno 는 칼럼 명으로 인식되어 모든 레코드가 삭제된다. DECLARE eno INTEGER := 100; BEGIN DELETE FROM employee WHERE eno = eno; … 다음과 같은 방법으로 모호성을 없앨 수 있다. <> DECLARE eno INTEGER := 100; BEGIN DELETE FROM employee WHERE eno = del_block.eno; 저장 프로시저 블록 41 Data types 변수의 데이터 타입을 명시한다. 저장 프로시저는 SQL 문에서 사용하는 데이터 타입 이외에 BOOLEAN 타입과 이미 데이터 타입이 지정된 칼럼이나 변수와 같은 타입으로 변수를 정의할 수 있게 하는 %TYPE 속성, 여러 개의 칼럼이 모인 레코드 타입을 정의할 수 있는 %ROWTYPE 속성 및 사용자 정의 타입을 지원한다. Constant 특정 변수를 값을 할당할 수 없는 상수로 사용하고자 하는 경우에 사용하는 옵션이다. 이렇게 정의된 변수는 읽기 전용변수로만 사용된다. 다음과 같이 max_val 을 선언하면 max_val 에는 임의의 값을 할당할 수 없고 100 의 값을 가지는 상수와 같이 취급된다. max_val CONSTANT integer := 10; Default 다음과 같이 Default 구를 사용하여 변수 선언 시 초기값을 설정할 수 있다. curr_val INTEGER DEFAULT 100; count_val INTEGER := 0; Cursor Declaration DECLARE CURSOR 섹션 참고 Type Declaration DECLARE TYPE 섹션 참고 Exception Declaration Exception Handler 섹션 참고 참고-중첩 블록 및 변수의 범위 선언부에 명시한 변수들의 영향력은 자신이 선언된 BLOCK 문의 BEGIN 에서 시작되고 END 에서 종료된다. 만약 block2 가 block1 내에 정의되어 있고 각각 같은 이름을 가지는 v_result 를 가지는 변수를 선언하였다면 block2 의 밖에서 사용되는 v_result 는 block1 에 정의된 변수를 의미하게 되고 block2 내에서 사용된 v_result 는 block2 에서 선언한 변수를 의미하게 된다. 아래에 중첩 블록에 있는 변수 y 는 변수 x 를 참조할 수 있지만 변수 x 는 변수 y 를 참조할 수 없다. 중첩 블록에 있는 변수 y 에 외부 블록에 있는 변수 x 와 동일한 이름이 주어지면 그 값은 중첩 블록에서만 유효하다. 42 ALTIBASE5 Stored Procedure User’s Manual Others 다음과 같은 기능은 변수 선언부에서 지원하지 않는 기능들이다. y 변수에 NULL/NOT NULL 속성을 지정할 수 없다. /* start of block1 */ DECLARE v_result 1 integer; x integer; BEGIN …… v_result 1 := 1; …… /* start of sub-block (block2) */ DECLARE v_result 2 integer; y number; BEGIN … v_result 2 := 2; … END; /* end of block2 */ … v_result 1 := 3; … END; /* end of block1 */ v_result 2 값을 참조할 수 있는 영역 v_result 1 값을 참조할 수 있는 영역 v_result 1 값을 참조할 수 있는 영역 x의 범위 y의 범위 저장 프로시저 블록 43 y 여러 개의 변수들을 i,j,k INTEGER 형식으로 한꺼번에 선언할 수 없다. 예제 %TYPE DECLARE my_title books.title%TYPE; my_title 은 books 라는 테이블의 title 이라는 칼럼과 같은 데이터 타입을 가지는 변수로 정의된다. %ROWTYPE DECLARE dept_rec department%ROWTYPE dept_rec 은 레코드 타입의 변수로서 department 테이블 또는 department 라는 이름의 커서와 동일한 레코드 타입을 가지게 된다. 상수 선언, default값 설정 예제 1 CREATE TABLE t1(i1 INTEGER, i2 INTEGER); CREATE OR REPLACE PROCEDURE proc1 AS v1 constant INTEGER := 1; v2 constant t1.i1%TYPE := 1; BEGIN INSERT INTO t1 VALUES (v1,v2); END; / EXEC proc1; iSQL> SELECT * FROM t1; T1.I1 T1.I2 --------------------- 1 1 1 row selected. --DROP TABLE t1; CREATE TABLE t1 (i1 INTEGER, i2 INTEGER, i3 INTEGER); INSERT INTO t1 VALUES(1,1,1); CREATE OR REPLACE PROCEDURE proc1 AS r1 t1%ROWTYPE; BEGIN INSERT INTO t1 VALUES(3,3,3); < > DECLARE r1 t1%ROWTYPE; BEGIN 44 ALTIBASE5 Stored Procedure User’s Manual SELECT i1, i2, i3 INTO s.r1.i1, s.r1.i2, s.r1.i3 FROM t1 WHERE i1 = 1; INSERT INTO t1 VALUES (s.r1.i1, s.r1.i2, s.r1.i3); END; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ------------------------------- 1 1 1 3 3 3 1 1 1 3 rows selected. CREATE TABLE emp( eno INTEGER, ename CHAR(10), emp_job CHAR(15), join_date DATE, salary NUMBER(10,2), dno smallint ); CREATE TABLE emp401( eno INTEGER, ename CHAR(10), emp_job CHAR(15), join_date DATE, leave_date DATE, salary NUMBER(10,2), dno BYTE(2), fund NUMBER(10,2) DEFAULT 0); INSERT INTO emp VALUES (10, 'DKLE', 'ENGINEER', '01-Jul-2000', 30000000, BYTE'D001'); INSERT INTO emp VALUES (20, 'SWMYUNG', 'MANAGER', '01-Nov-1999', 50000000, BYTE'C02'); 예제 2 CREATE OR REPLACE PROCEDURE proc1(p1 INTEGER) AS BEGIN DECLARE emp_rec emp%ROWTYPE; BEGIN SELECT * INTO emp_rec FROM emp WHERE eno = p1; INSERT INTO emp401(eno, ename, emp_job, join_date, leave_date, salary, dno) VALUES(emp_rec.eno, emp_rec.ename, emp_rec.emp_job, emp_rec.join_date, sysdate, emp_rec.salary, emp_rec.dno); END; END; / iSQL> EXEC proc1(10); 저장 프로시저 블록 45 Execute success. iSQL> SELECT * FROM emp401; EMP401.ENO EMP401.ENAME EMP401.EMP_JOB EMP401.JOIN_DATE ----------------------------------- EMP401.LEAVE_DATE EMP401.SALARY EMP401.DNO EMP401.FUND ----------------------------------- 10 DKLE ENGINER 20/07/01 0:0:0 2005/01/27 16:26:26 3000 D001 0 1 row selected. 46 ALTIBASE5 Stored Procedure User’s Manual SELECT INTO 구문 s e le c t_ in to _ s ta te m e n t ::= select_listINTO record _n am e variable_nam e FROMrest_ of_ select_statem en t; ; select_list 와 rest_of_select_statement 는 SELECT 문장의 문법과 동일하므로 SQL User’s Manual 을 참고한다. 기능 저장 프로시저가 SELECT 문을 포함할 경우 INTO 절이 필요하다. INTO 절을 가지는 SELECT 문은 저장 프로시저 내에서 하나의 레코드만 검색하는 경우에 사용한다. INTO 절이 사용된 SELECT 구문이 여러 행을 반환하거나 한 행도 반환하지 않으면 오류가 발생한다. SELECT 절의 select_list와 INTO 절의 상응하는 variable_name은 개수가 동일해야 하며 호환 가능한 데이터 타입이어야 한다. ROWTYPE 변수를 INTO 절에 사용하는 경우에도 동일하다. 저장 프로시저는 표준 예외 사항이 발생하는 경우 오류를 처리하며 NO_DATA_FOUND 및 TOO_MANY_ROWS 등의 예외 사항을 사용하여 블록의 예외 부분에서 오류를 처리할 수 있다. 예외 처리에 대한 상세한 설명은 Exception Handler 를 참조한다. 저장 프로시저 블록 47 예제 예제 1 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); INSERT INTO t1 VALUES(1,1,1); CREATE OR REPLACE PROCEDURE proc1 AS v1 INTEGER; r1 t1%ROWTYPE; BEGIN INSERT INTO t1 VALUES (3,3,3); <> DECLARE v1 proc1.r1.i1%TYPE; r1 t1%ROWTYPE; BEGIN SELECT i1,i2,i3 INTO s.r1.i1, s.r1.i2, s.r1.i3 FROM t1 WHERE i1 = 1; INSERT INTO t1 VALUES(s.r1.i1, s.r1.i2, s.r1.i3); END; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ------------------------------- 1 1 1 3 3 3 1 1 1 3 rows selected. 예제 2 CREATE TABLE t1 (i1 INTEGER, i2 INTEGER, i3 INTEGER); INSERT INTO t1 VALUES(100, 100, 100); CREATE SEQUENCE seq1; CREATE SEQUENCE seq2; CREATE SEQUENCE seq3; CREATE OR REPLACE PROCEDURE proc1 AS BEGIN <> DECLARE nextval INTEGER; BEGIN nextval := 10; INSERT INTO t1 VALUES (seq1.NEXTVAL,0,0); END; END; 48 ALTIBASE5 Stored Procedure User’s Manual / CREATE OR REPLACE PROCEDURE proc2 AS BEGIN INSERT INTO t1 VALUES (seq1.NEXTVAL, seq2.NEXTVAL, seq3.NEXTVAL); INSERT INTO t1 VALUES (seq1.NEXTVAL, seq2.NEXTVAL, seq3.NEXTVAL); INSERT INTO t1 VALUES (seq1.NEXTVAL, seq2.NEXTVAL, seq3.NEXTVAL); END; / CREATE OR REPLACE PROCEDURE proc3 AS v1 INTEGER; v2 INTEGER; v3 INTEGER; BEGIN SELECT seq1.currval, seq2.NEXTVAL, seq3.NEXTVAL INTO v1, v2, v3 FROM t1 WHERE i1 = 10; INSERT INTO t1 VALUES (v1, v2, v3); SELECT seq1.currval, seq1.NEXTVAL, seq1.currval INTO v1, v2, v3 FROM t1 WHERE i1 = 10; INSERT INTO t1 VALUES (v1, v2, v3); SELECT seq1.currval, seq2.NEXTVAL, seq3.NEXTVAL INTO v1, v2, v3 FROM t1 WHERE i1 = 10; INSERT INTO t1 VALUES (v1, v2, v3); END; / EXEC proc1; SELECT * FROM t1; EXEC proc2; SELECT * FROM t1; EXEC proc3; SELECT * FROM t1; EXEC proc2; SELECT * FROM t1; EXEC proc3; iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ------------------------------- 10 10 10 10 0 0 1 1 1 2 2 2 3 3 3 3 4 4 4 4 4 4 5 5 5 6 6 6 7 7 7 8 8 7 9 9 8 8 8 저장 프로시저 블록 49 8 10 10 14 rows selected. 예제 3 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE TABLE t2(i1 INTEGER, i2 INTEGER, i3 INTEGER); INSERT INTO t1 VALUES (1,1,1); INSERT INTO t1 VALUES (2,2,2); CREATE OR REPLACE PROCEDURE proc1 AS v1 INTEGER; r1 t1%ROWTYPE; BEGIN SELECT i1 INTO v1 FROM t1 WHERE i1 = 1; SELECT * INTO r1 FROM t1 WHERE i1 = 1; INSERT INTO t2 VALUES (v1, r1.i2, r1.i3); < > DECLARE r1 t1%ROWTYPE; BEGIN SELECT i1, i2, i3 INTO s.r1.i1, s.r1.i2, s.r1.i3 FROM t1 WHERE i1 = 2; INSERT INTO t2 VALUES (s.r1.i1, s.r1.i2, s.r1.i3); END; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t2; T2.I1 T2.I2 T2.I3 ------------------------------- 1 1 1 2 2 2 2 rows selected. 예제 4 CREATE TABLE t3(i1 INTEGER); CREATE OR REPLACE PROCEDURE proc1 AS max_qty orders.qty%TYPE; BEGIN SELECT MAX(qty) INTO max_qty FROM orders; INSERT INTO t3 VALUES(max_qty); END; / iSQL> exec proc1; Execute success iSQL> SELECT * FROM t3; T3.I1 --------- 10 50 ALTIBASE5 Stored Procedure User’s Manual 1 row selected. 예제 5 CREATE TABLE delayed_processing( cno CHAR(14), order_date DATE); CREATE OR REPLACE PROCEDURE proc1 AS de_cno CHAR(14); de_order_date DATE; BEGIN INSERT INTO delayed_processing SELECT cno, order_date INTO de_cno, de_order_date FROM orders WHERE processing = 'D'; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM delayed_processing; DELAYED_PROCESSING.CNO DELAYED_PROCESSING.ORDER_DATE ----------------------------------- 761001-101 2000/11/29 00:00:00 700101-10101 2000/11/29 00:00:00 2 rows selected. 저장 프로시저 블록 51 ASSIGNMENT 구문 assignment_statement := variable_name parameter_name record_name column_name. SET variable_name parameter_name record_name column_name. := = expression; 기능 지역변수, OUT/IN OUT 형의 파라미터에 값을 할당하고자 할 때 사용하는 할당문이다. 할당문에는 다음과 같은 두 가지 형태가 있다. y 방법1: variable_name := value; y 방법2: SET variable_name = value; ROWTYPE 을 사용한 변수의 경우에도 위의 두 가지 방법을 다 사용할 수 있으며 다음과 같이 할당문을 기술한다. record_variable_name.filed_name := value; SET record_variable_name.filed_name = value; 예제 52 ALTIBASE5 Stored Procedure User’s Manual 예제 1 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE OR REPLACE PROCEDURE proc1 AS i INTEGER; BEGIN i := 5; WHILE i <= 10 LOOP INSERT INTO t1 VALUES (i, i+1, i+2); i := i + 1; END LOOP; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ------------------------------- 5 6 7 6 7 8 7 8 9 8 9 10 9 10 1 10 1 12 6 rows selected. 예제 2 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE OR REPLACE FUNCTION plus20(p1 IN INTEGER) RETURN INTEGER AS v1 INTEGER; BEGIN v1 := p1 + 20; RETURN v1; END; / CREATE OR REPLACE PROCEDURE proc1 AS v1 INTEGER; in_arg INTEGER; BEGIN in_arg := 80; v1 := plus20(in_arg); INSERT INTO t1 VALUES (v1, v1); END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ------------------------------- 저장 프로시저 블록 53 10 10 10 1 row selected. 54 ALTIBASE5 Stored Procedure User’s Manual LABEL LABLE 은 저장 프로시저 내부의 특정 위치에 명칭을 지정하는 기능이다. LABEL 은 블록 내에 다음과 같이 지정할 수 있다. << User_defined_label_name >> 기능 사용자가 지정한 블록의 LABEL 명은 다음 3 가지 경우에 사용된다. y 변수의 범위를 분류하여 사용하고 싶은 경우 또는 변수 이름과 컬럼 이름의 모호성을 없애기 위한 경우 y 중첩된 LOOP에서 빠져나오고 싶은 경우 y GOTO 문장을 사용하는 경우 제약조건 1. LABEL 은 동일 블록 내에 같은 이름이 존재하면 안 된다. 아래 예제의 경우 LABEL1 이 동일 블록 내에 두번 지정되어 컴파일 시 에러가 출력된다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 INTEGER; BEGIN V1 := 1; <> V1 := V1 + 1; … 2. 변수의 영역를 분류하기 위하여 사용하는 경우 반드시 DECLARE 문 위에 LABEL 을 선언해야 한다. 단, LABEL 을 여러 개 선언하는 것은 허용된다. 아래 예제 2 의 경우 변수 선언부 위에서 LABEL 을 지정하지 않아서 에러가 나게 된다. <예제 1> CREATE OR REPLACE PROCEDURE PROC1 AS V1 INTEGER; BEGIN < > - LABLE 지정 DECLARE V1 INTEGER; .......(1) BEGIN 저장 프로시저 블록 55 DECLARE V1 INTEGER; ......(2) BEGIN LABEL1.V1 := 1; - (1)의 V1, LABLE 참조 LABEL2.V1 := 2; - (1)의 V1 LABEL3.V1 := 3; - (2)의 V1 END; END; END; / <예제 2> CREATE OR REPLACE PROCEDURE PROC1 AS V1 INTEGER; BEGIN < > V1 := 1; DECLARE V1 INTEGER; BEGIN LABEL1.V1 := 1; --- ERROR. 3. 중첩된 LOOP 에서 빠져나올 때 사용하는 경우 반드시 LOOP 시작 직전에 LABEL 을 선언해야 한다. 예제 2 의 경우와 같이 지정된 LABEL 을 이용해서 LOOP 를 빠져나올 수 없으므로 에러가 발생하게 된다. 단, LABEL 을 여러 개 선언하는 것은 허용된다. <예제 1> CREATE OR REPLACE PROCEDURE PROC1 AS V1 INTEGER; BEGIN V1 := 0; FOR I IN 1 . 10 LOP V1 := V1 + 1; FOR I IN 1 . 10 LOP V1 := V1 + 1; EXIT LABEL1 WHEN V1 = 30; END LOP; END LOP; END; / <예제 2> CREATE OR REPLACE PROCEDURE PROC1 AS V1 INTEGER; BEGIN V1 := 0; FOR I IN 1 . 10 LOP 56 ALTIBASE5 Stored Procedure User’s Manual V1 := V1 + 1; FOR I IN 1 . 10 LOP V1 := V1 + 1; EXIT LABEL1 WHEN V1 = 30; - EROR END LOP; END LOP; END; / 저장 프로시저 블록 57 PRINT 구문 print_statem ent := PRINT PRINTLN ()strin g; 기능 PRINT 구문은 저장 프로시저 실행 시에 사용자가 지정한 메시지 텍스트를 해당 프로시저를 호출한 클라이언트에게 출력한다. PRINT 구문은 주로 디버깅 및 테스트를 목적으로 사용하고, 알티베이스가 제공하는 시스템 프로시저이다. PRINTLN 은 PRINT 와 동일하나 출력 메시지의 마지막에 개행문자(“\n”) 추가해서 출력하는 것을 의미한다. PRINT, PRINTLN 의 소유자는 SYSTEM_이므로 사용 시 이를 명시할 수 있다. 그러나 시스템에 이들에 대한 시노님이 기본적으로 생성되어 있어 SYSTEM_를 명시하지 않을 경우 PUBLIC 시노님을 통해 이들 객체를 수행할 수 있다. String 클라이언트로 출력할 문자열을 기술한다. 사용자 메시지 출력시 문자열과 함께 변수값 등을 출력하고 싶을 경우에 예제 2 와 같이 문자열 연결 연산자인 “||” 를 사용해서 하나의 문자열로 만들어서 출력할 수 있다. 예제 예제 1 CREATE OR REPLACE PROCEDURE proc1 AS 58 ALTIBASE5 Stored Procedure User’s Manual v1 BIGINT; BEGIN v1 := BIGINT'9223372036854775807'; system_.println ('1'); system_.println (v1); system_.println ('2'); END; / iSQL> EXEC proc1; 1 9223372036854775807 2 Execute success. 예제 2 CREATE OR REPLACE PROCEDURE proc1 AS eno_count INTEGER; BEGIN SELECT COUNT(eno) INTO eno_count FROM employee; println('The NUMBER of Employees: ' || eno_count); END; / iSQL> EXEC proc1; The NUMBER of Employees: 20 Execute success. 예제 3 다음 예제는 지금까지 생성된 procedures/functions 의 이름을 보여 준다. CREATE OR REPLACE PROCEDURE showProcedures AS CURSOR c1 IS SELECT SYSTEM_.sys_procedures_.proc_name, decode(SYSTEM_.sys_procedures_.object_TYPE, 0, 'Procedure',1,'Function') FROM system_.sys_procedures_ ; v1 CHAR(40); v2 CHAR(20); BEGIN OPEN c1; SYSTEM_.PRINTLN('-----------------------'); SYSTEM_.PRINT('Proc_Name'); SYSTEM_.PRINTLN(' Procedure/Function'); SYSTEM_.PRINTLN('-----------------------'); LOOP FETCH C1 INTO v1, v2; EXIT WHEN C1%NOTFOUND; PRINT(' '); PRINT(v1); PRINTLN(v2); END LOOP; 저장 프로시저 블록 59 PRINTLN('------------------'); CLOSE c1; END; / iSQL> EXEC showProcedures; ----------------------------------- Proc_Name Procedure/Function ----------------------------------- PRINT Procedure PRINTLN Procedure TIMES_HALF Function FUNC_PLUS_10 Function FUNC2 Function FUNC1 Function BONUS Procedure GET_RATE Function PROC2 Procedure PROC3 Procedure MAX_AL_VAL Function PLUS20 Function PROC1 Procedure SHOWPROCEDURES Procedure ----------------------------------- Execute success. 60 ALTIBASE5 Stored Procedure User’s Manual RETURN 구문 re tu rn _ s ta te m e n t ::= () ;expressionRETURN 기능 저장 프로시저의 수행을 도중에 중단 하거나, 함수에서 값을 반환하고 수행을 중단하려 하는 경우에 사용하는 제어문이다. 저장 프로시저는 RETURN 문에 반환할 값을 지정하게 되면 에러가 발생한다. 반면 저장 함수는 항상 값을 반환해야 하기 때문에 RETURN 문에 반환할 값을 명시하여야 한다. Expression 저장 함수의 경우에 반환할 값을 기술한다. 반환값은 연산식의 형태로도 기술 가능하다. 예제 예제 1 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); INSERT INTO t1 VALUES(1,1,1); CREATE OR REPLACE FUNCTION times_half(p1 IN INTEGER) RETURN INTEGER AS BEGIN RETURN p1 / 2; END; / iSQL> SELECT times_half(times_half(8)) FROM t1; 저장 프로시저 블록 61 TIMES_HALF(TIMES_HALF(8)) ---------------------- 2 1 row selected. 예제 2 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); INSERT INTO t1 VALUES(1,1,1); INSERT INTO t1 VALUES(10,10,10); INSERT INTO t1 VALUES(100,100,100); CREATE OR REPLACE FUNCTION max_all_val RETURN INTEGER AS v1 INTEGER; BEGIN SELECT MAX(i1) INTO v1 FROM t1; RETURN v1; END; / iSQL> SELECT max_all_val FROM t1; MAX_ALL_VAL --------- 10 10 10 3 rows selected. 예제 3 CREATE TABLE t4(i1 INTEGER, i2 INTEGER); INSERT INTO t4 VALUES(3, 0); INSERT INTO t4 VALUES(2, 0); INSERT INTO t4 VALUES(1, 0); INSERT INTO t4 VALUES(0, 0); CREATE OR REPLACE FUNCTION func_plus_10(p1 INTEGER) RETURN INTEGER AS BEGIN RETURN p1+10; END; / iSQL> SELECT func_plus_10(i1) FROM t4; FUNC_PLUS_10(I1) ---------------- 13 12 1 10 4 rows selected. 62 ALTIBASE5 Stored Procedure User’s Manual 4. 흐름 제어 흐름제어 63 개요 구문 c o n tro l_ flo w _ s ta te m e n t ::= if_ sta tem en t ca se_ sta tem en t sim ple_loop_statem ent w h ile _ lo o p _ s ta te m e n t fo r _ lo o p _ s ta te m e n t e x it_ s ta te m e n t continue_statem ent n u ll_ sta tem en t 저장 프로시저가 제공하는 흐름 제어문은 다음과 같다. y 조건 분기문인 IF문, CASE문 y 조건을 만족할 때 반복 수행하는 LOOP문, WHILE문, FOR문 y 반복 수행문의 흐름을 제어하는 EXIT문, CONTINUE문 y 아무것도 수행하지 않음을 명시적으로 나타낼 수 있는 NULL문 y 특정 위치로 이동할 수 있는 GOTO문 64 ALTIBASE5 Stored Procedure User’s Manual IF 구문 if_statem ent ::= IFconditionTHENstatem ent ELS(E)IFconditionTHENstatem ent END IF ELSEstatem ent ; 기능 조건을 만족하는 경우와 그렇지 않은 경우에 따라 처리 흐름을 분기하는 조건 분기문이다. Condition 조건절에는 SQL 문의 WHERE 절에서 사용 가능한 모든 술어(predicate)들을 사용할 수 있다. 지원하는 술어들에 대한 상세 내용은 SQL User’s Manual의 SELECT 구문을 참조한다. ELS(E)IF ELS(E)IF 절의 경우 IF 문의 조건과는 다른 조건을 명시할 수 있다. ELSIF 는 한 단어이며 하나의 IF 문 내에 여러 개의 ELS(E)IF 절을 사용할 수 있다. ELSE 앞서 열거된 모든 조건을 다 만족하지 않는 경우 ELSE 절의 문장이 수행된다. ELSE 절은 명시하지 않을 수도 있으며, 하나의 IF 문 내에 흐름제어 65 한번 기술 가능하다. 중첩 IF 문 특정 작업을 수행하기 전에 첫 번째 IF 문의 결과로 수행되는 일련의 작업 중 하나에 추가 IF 문을 포함시킬 수 있다. THEN 과 ELSE 절은 IF 문을 포함할 수 있으며 중첩 IF 문은 각각 해당 END IF 로 종료해야 한다. 예제 예제 1 CREATE OR REPLACE PROCEDURE proc1 AS CURSOR c1 ISELECT eno, emp_job, salary FROM employee; emp_id employee.eno%TYPE; e_job employee.emp_job%TYPE; e_salary employee.salary%TYPE; BEGIN OPEN c1; LOP FETCH c1 INTO emp_id, e_job, e_salary; EXIT WHEN c1%NOTFOUND; IF e_salary IS NULL THEN IF e_job = 'CEO' THEN e_salary := 5000000; ELSIF e_job = 'MANAGER' THEN e_salary := 4500000; ELSIF e_job = 'ENGINER' THEN e_salary := 4300000; ELSIF e_job = 'PROGRAMMER' THEN e_salary := 4100000; ELSE e_salary := 4000000; END IF; UPDATE employee SET salary = e_salary WHERE eno = emp_id; END IF; END LOOP; CLOSE c1; END; / iSQL> SELECT eno, emp_job FROM employee WHERE salary IS NULL; ENO EMP_JOB -------------------------- 1 CEO 8 MANAGER 20 SALESMAN 3 rows selected. iSQL> EXEC proc1; Execute success. iSQL> SELECT eno, emp_job, salary FROM employee 66 ALTIBASE5 Stored Procedure User’s Manual WHERE eno=1 OR eno=8 OR eno=20; ENO EMP_JOB SALARY ------------------------------------ 1 CEO 50 8 MANAGER 450 20 SALESMAN 40 3 rows selected. 예제 2 CREATE TABLE t1 (i1 VARCHAR(20), i2 NUMBER, i3 DATE); CREATE TABLE t2 (i1 VARCHAR(20), i2 NUMBER, i3 DATE); INSERT INTO t1 VALUES ('21-JUL-2001', 2, '01-JUL-2000'); iSQL> INSERT INTO t2 VALUES (NULL,NULL,'01-FEB-190'); INSERT INTO t2 VALUES (NULL,NULL,'02-FEB-1990'); CREATE OR REPLACE FUNCTION func2 (p1 IN DATE, p2 IN CHAR(30)) RETURN NUMBER AS BEGIN RETURN (TO_NUMBER(TO_CHAR(p1, 'dd')) + TO_NUMBER(p2)); END; / CREATE OR REPLACE FUNCTION func1 (p1 IN DATE, p2 IN DATE) RETURN DATE AS BEGIN IF p1 >= p2 THEN RETURN add_months(p1, 3); ELSE RETURN add_months(p1, 4); END IF; END; / CREATE OR REPLACE PROCEDURE proc1 AS v1 VARCHAR(20); v2 NUMBER; v3 DATE; BEGIN SELECT i1, func2(TO_DATE(i1), TO_CHAR(i3, 'yyyy')), i3 INTO v1,v2,v3 FROM t1 WHERE i2 = 2; INSERT INTO t2 VALUES (v1,v2,v3); IF v2 not in (2001, 2002, 2003) AND v1 = '21-JUL-201' THEN UPDATE t2 SET i1 = func1(v1, '17-JUL-2001'), i2 = nvl(i2, 10) WHERE i3 = '01-FEB-1990'; UPDATE t2 SET i1 = func1(v1, '27-JUL-2001'), i2 = nvl(i2, 10*2) WHERE i3 = '02-FEB-1990'; END IF; 흐름제어 67 END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t2; T2.I1 T2.I2 T2.I3 ----------------------------------- 21-JUL-201 2021 2000/07/01 00:00:00 21-OCT-01 10 1990/02/01 0:00:00 21-NOV-01 20 1990/02/02 00:0:0 3 rows selected. 예제 3 CREATE TABLE payroll( eno INTEGER, bonus NUMBER(10, 2)); CREATE OR REPLACE PROCEDURE proc1 AS BEGIN DECLARe CURSOR c1 IS SELECT DISTINCT(eno), SUM(qty) FROM orders GROUP BY eno; emp_id orders.eno%TYPE; sum_qty orders.qty%TYPE; bonus NUMBER(10, 2); BEGIN OPEN c1; IF c1%ISOPEN THEN LOP FETCH c1 INTO emp_id, sum_qty; EXIT WHEN c1%NOTFOUND; IF sum_qty > 250 THEN bonus := 10; ELSIF sum_qty > 150 THEN bonus := 50; ELSE bonus := 20; END IF; INSERT INTO payrol VALUES(emp_id, bonus); END LOP; END IF; END; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT DISTINCT(eno), SUM(qty) sum FROM orders GROUP BY eno; ENO SUM --------------------------- 12 17870 19 25350 20 13210 3 rows selected. iSQL> SELECT * FROM payroll; 68 ALTIBASE5 Stored Procedure User’s Manual PAYROLL.ENO PAYROLL.BONUS ----------------------- 12 50 19 10 20 20 3 rows selected. 흐름제어 69 CASE 구문 case_statement_1 ::= CASEWHENconditionTHENstatement ELSEstatement ENDCASE; case_statement_2::= CASEcase_variableWHENwhen_variableTHENstatement ELSEstatement ENDCASE; 기능 특정 변수의 값에 따라서 처리 경로를 바꾸는 조건 분기문이다. IF 문과 동일한 기능이지만 CASE 문을 사용하면 프로그램의 가독성을 높일 수 있다. CASE 문은 위의 다이어그램에서 보여주 듯이 다음 두 가지가 있다. case_statement1의 경우: 특정한 조건식이 참일 때에 특정한 문장을 수행하는 방식 70 ALTIBASE5 Stored Procedure User’s Manual case_statemen2의 경우: 하나의 변수가 특정한 값이 되었을 때에 특정한 문장을 수행하는 방식 단, 하나의 CASE 문에서 이 두가지 방식을 혼용할 수 없으며, 이 둘중에서 하나의 방식만을 사용하여야 한다. CASE 절을 모두 만족하지 못하면 ELSE 절의 문장을 수행하게 되며, ELSE 절이 없는 경우에는 어떠한 문장도 수행하지 않는다. Condition CASE 문의 조건식을 SELECT 구문의 WHERE 절과 같이 술어 문으로 명시한다. Case Variable 저장 프로지저의 처리를 분기시키는데 기준값이 되는 변수명을 기술한다. When Value Case Variable 변수와 비교할 실제 상수 값을 기술한다. ELSE CASE 조건식이 모두 거짓이 되었을 때 수행해야 할 처리 구문을 ELSE 절에 기술한다. ELSE 절은 없어도 상관 없으며 하나의 CASE 문에 한번 사용 가능하다. 조건식이 모두 거짓이 되었음에도 ELSE 절이 없을 경우 CASE 문은 어떠한 문장도 수행하지 않고 지나간다. 예제 예제 1 CREATE OR REPLACE PROCEDURE proc1 AS CURSOR c1 ISELECT eno, emp_job, salary FROM employee; emp_id employee.eno%TYPE; e_job employee.emp_job%TYPE; e_salary employee.salary%TYPE; BEGIN OPEN c1; LOP FETCH c1 INTO emp_id, e_job, e_salary; EXIT WHEN c1%NOTFOUND; IF e_salary IS NULL THEN CASE WHEN e_job = 'CEO' THEN e_salary := 5000000; WHEN e_job = 'MANAGER' THEN e_salary := 450; WHEN e_job = 'ENGINER' THEN e_salary := 4300000; 흐름제어 71 WHEN e_job = 'PROGRAMMER' THEN e_salary := 4100000; ELSE e_salary := 40; END CASE; UPDATE employee SET salary = e_salary WHERE eno = emp_id; END IF; END LOOP; CLOSE c1; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT eno, emp_job, salary FROM employee WHERE eno=1 OR eno=8 OR eno=20; ENO EMP_JOB SALARY ------------------------------------ 1 CEO 50 8 MANAGER 450 20 SALESMAN 40 3 rows selected. 예제 2 @schema.sql CREATE OR REPLACE PROCEDURE proc1 AS CURSOR c1 ISELECT eno, emp_job, salary FROM employee; emp_id employee.eno%TYPE; e_job employee.emp_job%TYPE; e_salary employee.salary%TYPE; BEGIN OPEN c1; LOP FETCH c1 INTO emp_id, e_job, e_salary; EXIT WHEN c1%NOTFOUND; IF e_salary IS NULL THEN CASE e_job WHEN 'CEO' THEN e_salary := 5000000; WHEN 'MANAGER' THEN e_salary := 4500000; WHEN 'ENGINER' THEN e_salary := 4300000; WHEN 'PROGRAMER' THEN e_salary := 410; ELSE e_salary := 40; END CASE; UPDATE employee SET salary = e_salary WHERE eno = emp_id; END IF; END LOOP; CLOSE c1; END; / iSQL> SELECT eno, emp_job FROM employee WHERE salary IS NULL; ENO EMP_JOB -------------------------- 72 ALTIBASE5 Stored Procedure User’s Manual 1 CEO 8 MANAGER 20 SALESMAN 3 rows selected. iSQL> EXEC proc1; Execute success. iSQL> SELECT eno, emp_job, salary FROM employee WHERE eno=1 OR eno=8 OR eno=20; ENO EMP_JOB SALARY ------------------------------------ 1 CEO 50 8 MANAGER 450 20 SALESMAN 40 3 rows selected. 흐름제어 73 LOOP 구문 label_name<<>> LOOPstatement END LOOP label_name ; l oop_st at ement : = 기능 LOOP 을 수행할 수 있는 조건을 따로 지정하지 않는 경우에 사용하는 반복문이다. 그러나 LOOP 구문에서 EXIT 문을 사용하지 않게 되면 무한 LOOP 에 빠져서 시스템에 문제를 일으킬 수 있으므로 주의한다. 예제 CREATE TABLE item(id INTEGER, counter NUMBER(2)); iSQL> CREATE OR REPLACE PROCEDURE proc1 AS BEGIN DECLARE v_id item.id%TYPE := 501; v_counter NUMBER(2) := 1; BEGIN LOP INSERT INTO item VALUES(v_id, v_counter); v_counter := v_counter + 1; EXIT WHEN v_counter > 10; END LOP; END; END; 74 ALTIBASE5 Stored Procedure User’s Manual / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM item; ITEM.ID ITEM.COUNTER ---------------------- 501 1 501 2 … 501 9 501 10 10 rows selected. 흐름제어 75 WHILE LOOP 구문 while_loop_statement ::= label_name<<>> LOOPcondition label_name ; WHILE END LOOPstatement 기능 특정한 조건이 참인 경우만 LOOP 을 수행하고자 할 때 사용하는 반복문이다. 만약 처음부터 이 조건이 참이 아니면, WHILE 문은 전혀 수행되지 않고 다음 문장을 수행하게 된다. Condition LOOP 을 수행할 지의 여부를 결정하는 조건절을 명시한다. 조건절에는 SQL 문의 WHERE 절에서 사용 가능한 모든 술어(predicate)들을 사용할 수 있다. 예제 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE OR REPLACE PROCEDURE proc1 AS v1 INTEGER; BEGIN v1 := 1; WHILE v1 < 3 LOOP v1 := v1 + 1; INSERT INTO t1 VALUES (v1, v1, v1); IF v1 = 2 THEN 76 ALTIBASE5 Stored Procedure User’s Manual CONTINUE; END IF; END LOOP; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ------------------------------- 2 2 2 3 3 3 2 rows selected. 흐름제어 77 FOR LOOP 구문 for_lop_statement ::= label_name<< INcounter_nameFOR >> REVERSE lower_bound..upper_bound STEPstep_size LOOP statementEND LOOP label_name ; 기능 정수 값으로 일정 횟수만큼 LOOP 를 수행한다. Counter Name 단조 증가, 혹은 단조 감소하는 정수형 변수를 하나 기술한다. 이 변수는 블록의 선언부에 선언할 필요가 없다. 또한 이 변수의 범위는 LOOP 과 END LOOP 으로 둘러싸여 있는 문장들 에서만 사용 가능하며, FOR LOOP 내에서 이 변수에 새로운 값을 할당할 수 없다. REVERSE REVERSE 모드를 지정하면 Counter Name 변수의 값을 Upper Bound 에서 Low Bound까지 감소시키면서 FOR 문을 수행한다. Lower Bound Counter_Name 변수가 가질 수 있는 값 중 가장 작은 값이다. 78 ALTIBASE5 Stored Procedure User’s Manual 정수형과 호환성이 있는 표현식을 지정하여야 한다. 여기에는 지역변수를 사용할 수도 있으나, 이 값은 FOR 문이 맨 처음으로 실행될 때에 딱 한번 그 값을 계산하여 저장해 두고 사용하므로, 추후 해당 지역변수를 변경하여도 FOR 문의 동작에는 영향을 미칠 수 없다. 결과값이 실수형인 경우 반올림된 정수값으로 변환된다. Upper Bound Counter_Name 변수가 가질 수 있는 값 중 가장 큰 값이다. 표현식의 규칙은 Lower Bound 와 같으며, 만약 FOR 문이 맨 처음으로 수행될 때 Upper Bound 가 Lower Bound 보다 작으면 어떠한 에러도 내지 않고 단지 FOR 문을 무시하고 다음 문장을 수행한다. 결과값이 실수형인 경우 반올림된 정수 값으로 변환된다. Step Size 기본적으로 FOR 문은 Counter_Name 변수를 1 씩 증가, 혹은 감소 시키는데, 이 값을 재 지정할 수 있다. 표현식의 규칙은 Lower Bound 와 동일하다. 단, step size는 1 보다 작을 수 없다. 결과값이 실수형인 경우에는 반올림된 정수값으로 변환된다. 예제 --###################### -- FOR LOOP --###################### 예제 1 CREATE TABLE t6(i1 INTEGER, sum INTEGER); CREATE OR REPLACE PROCEDURE proc1 AS v1 INTEGER; sum INTEGER := 0; BEGIN FOR i IN 1 .. 50 LOOP v1 := 2 * i - 1; sum := sum + v1; INSERT INTO t6 VALUES(v1, sum); END LOOP; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t6; T6.I1 T6.SUM --------------------- 1 1 3 4 5 9 … 97 2401 흐름제어 79 9 250 50 rows selected. 예제 2 CREATE OR REPLACE PROCEDURE proc1 AS eno_count INTEGER; BEGIN SELECT COUNT(eno) INTO eno_count FROM employee; FOR i IN 1 .. eno_count LOOP UPDATE employee SET salary = salary * 1.2 WHERE eno = i; END LOOP; END; / iSQL> SELECT eno, salary FROM employee WHERE eno in (1,12,13); ENO SALARY --------------------- 1 2750 12 1890 13 980 3 rows selected. iSQL> EXEC proc1; Execute success. iSQL> SELECT eno, salary FROM employee WHERE eno IN (1,12,13); iSQL> ENO SALARY --------------------- 1 30 12 2680 13 1760 3 rows selected. 예제 3 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE OR REPLACE PROCEDURE proc1 AS BEGIN <> INSERT INTO t1 VALUES (1,1,1); IF 1 = 1 THEN NUL; END IF; <> FOR v1 IN 1 .. 3 LOOP FOR v1 IN 1 .. 3 LOOP INSERT INTO t1 VALUES (a.v1, b.v1, c.v1); END LOP; END LOOP; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 80 ALTIBASE5 Stored Procedure User’s Manual ------------------------------- 1 1 1 1 1 1 1 1 2 1 1 3 2 2 1 2 2 2 2 2 3 3 3 1 3 3 2 3 3 3 10 rows selected. --##################### -- reverse --##################### CREATE TABLE t6(i1 INTEGER, sum INTEGER); CREATE OR REPLACE PROCEDURE proc1 AS sum INTEGER := 0; BEGIN FOR i IN reverse 1 .. 100 LOOP sum := sum + i; INSERT INTO t6 VALUES(i, sum); END LOOP; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t6; T6.I1 T6.SUM --------------------- 10 10 9 19 98 297 … 3 5047 2 5049 1 5050 100 rows selected. --##################### -- step --##################### CREATE TABLE t6(i1 INTEGER, sum INTEGER); CREATE OR REPLACE PROCEDURE proc1 AS sum INTEGER := 0; BEGIN FOR i IN 1 .. 100 STEP 2 LOOP sum := sum + i; INSERT INTO t6 VALUES(i, sum); END LOOP; END; 흐름제어 81 / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t6; T6.I1 T6.SUM --------------------- 1 1 3 4 5 9 … 97 2401 9 250 50 rows selected. 82 ALTIBASE5 Stored Procedure User’s Manual EXIT 구문 EXIT exit_statement ::= label_nameWHENcondition ; 기능 EXIT 문을 감싸고 있는 1 차 LOOP 문을 빠져나간다. 그러나 label_name이 명시적으로 주어진 경우에는 label_name이 정의된 LOOP 을 빠져나간다. LOOP 문 내부가 아닌 다른 블록에서 EXIT 문을 사용하면 오류가 발생한다. < > LOOP ... LOP ... EXIT outer WHEN ... -- EXIT both LOOPs END LOOP; ... END LOOP outer; EXIT WHEN count > 10; IF count > 10 THEN EXIT; END IF; label_name 현재 EXIT 문을 감싸고 있는 1 차 LOOP 가 아닌 그보다 더 바깥의 LOOP 으로 빠져나가야 하는 경우, 해당 LOOP 의 바로 앞에 LABEL 을 정의하고 그 이름을 여기에 명시한다. WHEN 특정 조건이 참인 경우 EXIT 할 수 있도록 WHEN 절에 조건식을 지정할 수 있다. 흐름제어 83 EXIT 문을 만나면 WHEN 절에 명시된 조건이 참이면 저장 프로시저는 1 차 LOOP 나 지정된 LABEL 블록문을 빠려 나가서 그 다음 문장을 수행한다. 참고로 다음의 두 문장들은 동일한 기능을 한다. EXIT WHEN count > 10; IF count > 10 THEN EXIT; END IF; Condition 특정한 조건을 만족할 경우에만 LOOP 을 빠져나가고자 하는 경우 조건을 명시한다. 조건절에는 SELECT 문의 WHERE 절에서 사용 가능한 모든 술어들을 사용할 수 있다. 예제 --##################### -- EXIT --##################### CREATE TABLE stock( gno BYTE(5) primary key, stock INTEGER, price numeric(10,2)); CREATE OR REPLACE PROCEDURE proc1 AS CURSOR c1 IS SELECT gno, stock, price FROM goods; rec1 c1%ROWTYPE; BEGIN OPEN c1; LOP FETCH c1 INTO rec1; IF c1%found THEN IF rec1.stock > 0 AND rec1.stock < 1000 THEN INSERT INTO stock VALUES(rec1.gno, rec1.stock, rec1.price); END IF; ELSIF c1%NOTFOUND THEN EXIT; END IF; END LOOP; CLOSE c1; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM stock; STOCK.GNO STOCK.STOCK STOCK.PRICE ------------------------------- A111100002 100 98000 B101 780 3580 84 ALTIBASE5 Stored Procedure User’s Manual D111100003 650 45100 E111100001 900 2290.54 E111100006 900 2338.62 5 rows selected. --##################### -- EXIT WHEN --##################### CREATE OR REPLACE PROCEDURE proc1 AS CURSOR c1 IS SELECT gno, stock, price FROM goods; rec1 c1%ROWTYPE; BEGIN OPEN c1; IF c1%ISOPEN THEN LOOP FETCH c1 INTO rec1; EXIT WHEN c1%NOTFOUND; IF rec1.stock > 0 AND rec1.stock < 1000 THEN INSERT INTO stock VALUES(rec1.gno, rec1.stock, rec1.price); END IF; END LOP; END IF; CLOSE c1; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM stock; STOCK.GNO STOCK.STOCK STOCK.PRICE ------------------------------- A111100002 100 98000 B1111001 780 35800 D111100003 650 45100 E111100001 900 2290.54 E111100006 900 2338.62 5 rows selected. 흐름제어 85 CONTINUE 구문 c o n tin u e_s ta te m e n t ::= CONTINUE; 기능 현재 CONTINUE 문을 감싸고 있는 LOOP 에서 CONTINUE 문 이후의 문장들을 전부 무시하고 LOOP 의 맨 처음으로 되돌아간다. CONTINUE 문을 사용할 수 있는 LOOP 문은 다음과 같다. y LOOP y WHILE LOOP y FOR LOOP y CURSOR FOR LOOP LOOP 문 내부가 아닌 다른 곳에서 CONTINUE 문을 사용하면 오류가 발생한다. 예제 CREATE TABLE t8(i1 INTEGER, mathpower INTEGER default 0); INSERT INTO t8(i1) VALUES(7); INSERT INTO t8(i1) VALUES(3); INSERT INTO t8(i1) VALUES(20); INSERT INTO t8(i1) VALUES(15); INSERT INTO t8(i1) VALUES(6); INSERT INTO t8(i1) VALUES(1); INSERT INTO t8(i1) VALUES(9); CREATE OR REPLACE PROCEDURE proc1 AS BEGIN DECLARE CURSOR c1 IS SELECT i1 FROM t8; rec c1%ROWTYPE; 86 ALTIBASE5 Stored Procedure User’s Manual BEGIN OPEN c1; LOOP FETCH c1 INTO rec; EXIT WHEN c1%NOTFOUND; IF power(rec.i1, rec.i1) > 500 THEN continue; ELSE UPDATE t8 SET mathpower = power(rec.i1, rec.i1) WHERE i1 = rec.i1; END IF; END LOP; CLOSE c1; END; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t8; T8.I1 T8.MATHPOWER ---------------------- 7 0 20 0 15 0 9 0 3 27 6 4656 1 1 7 rows selected. 흐름제어 87 GOTO 구문 기능 지정된 LABEL 로 제어를 이동하는 분기문이다. label_name 제어를 이동할 LABEL 의 이름이다. 제약 사항 GOTO 는 다음과 같은 제약사항을 가진다. IF 문이나 CASE 문에서 다른 ELSE, THEN, WHEN 문으로 이동할 수 없다. 따라서 아래 예제의 경우 에러를 출력한다. <예제> CREATE OR REPLACE PROCEDURE PROC1 AS V1 INTEGER; BEGIN V1 := 1; IF V1 = 1 THEN GOTO LABEL1; ELSE PRINTLN(V1); END IF; END; / [ERR-3120F : Illegal GOTO statement. In PROC1 0007 : GOTO LABEL1; ^ ^ ] 88 ALTIBASE5 Stored Procedure User’s Manual 외부 블록에서 내부 블록으로 이동할 수 없다. 모든 BEGIN-END 블록 및 LOOP 문장이 이 제약 조건을 가진다. <예제> CREATE OR REPLACE PROCEDURE PROC1 AS V1 INTEGER; BEGIN V1 := 1; DECLARE V2 INTEGER; BEGIN V2 := 1; END; GOTO LABEL1; END; / [ERR-3120F : Illegal GOTO statement. In PROC1 0012 : GOTO LABEL1; ^ ^ ] 예외 처리부에서 분기하는 경우 예외 처리부가 속한 블록으로는 분기할 수 없으며, 그 상위 블록으로 분기가 가능하다. 따라서 다음의 예제 1 은 에러를 반환한다. 예제 2 의 경우 v1 이 5 가 될 때까지 EXCEPTION 을 4 번 발생시키고 정상으로 종료한다. <예제 1> CREATE OR REPLACE PROCEDURE PROC1 AS E1 EXCEPTION; BEGIN RAISE E1; PRINTLN('END'); EXCEPTION WHEN E1 THEN GOTO LABEL1; END; / [ERR-3120F : Illegal GOTO statement. In PROC1 010 : GOTO LABEL1; ^ ^ ] <예제 2> CREATE OR REPLACE PROCEDURE PROC1 AS E1 EXCEPTION; V1 INTEGER; BEGIN V1 := 1; 흐름제어 89 V1 := V1 + 1; PRINTLN('BLOCK1'); BEGIN PRINTLN('BLOCK2'); PRINTLN(V1); IF V1 = 5 THEN PRINTLN('goto label2 '|v1); GOTO LABEL2; ELSE RAISE E1; END IF; EXCEPTION WHEN E1 THEN PRINTLN('goto label1 '| v1); GOTO LABEL1; END; PRINTLN('BLOCK1 AFTER BLOCK2'); END; / $iSQL> EXEC PROC1; BLOCK1 BLOCK2 2 goto label1 2 BLOCK1 BLOCK2 3 goto label1 3 BLOCK1 BLOCK2 4 goto label1 4 BLOCK1 BLOCK2 5 goto label2 5 BLOCK1 AFTER BLOCK2 Execute success. 90 ALTIBASE5 Stored Procedure User’s Manual NULL 구문 null_statem ent := NULL; 기능 NULL 문은 흐름에 영향을 미치지 않고 아무것도 수행하지 않고 다음으로 넘어감을 명시적으로 나타내기 위해서 사용한다. 프로그램의 가독성을 높이기 위해서 사용한다. 예제 CREATE OR REPLACE PROCEDURE bonus (amount NUMBER(10,2)) AS CURSOR c1 ISELECT eno, sum(qty) FROM orders group by eno; order_eno orders.eno%TYPE; order_qty orders.qty%TYPE; BEGIN OPEN c1; LOP FETCH c1 INTO order_eno, order_qty; EXIT WHEN c1%NOTFOUND; IF order_qty > 200 THEN UPDATE employee SET salary = salary + amount WHERE eno = order_eno; ELSE NULL; END IF; END LOOP; CLOSE c1; END; / iSQL> SELECT e.eno, salary, sum(qty) FROM employee e, orders o WHERE e.eno = o.eno group by e.eno, salary; ENO SALARY SUM(QTY) 흐름제어 91 ----------------------------------- 12 1890 17870 19 180 25350 20 13210 3 rows selected. iSQL> EXEC bonus(55550); Execute success. iSQL> SELECT eno, salary FROM employee WHERE eno = 19; ENO SALARY --------------------- 19 1850 1 row selected. 커서 93 5. 커서 94 ALTIBASE5 Stored Procedure User’s Manual 커서의 개요 저장 프로시저 내에서 테이블의 레코드를 읽어 오는 방법에는 SELECT INTO 문을 사용하는 방법과 커서를 사용하는 방법이 있다. SELECT INTO 문의 경우 결과 레코드의 수는 반드시 한 개여야 하고 그 이외의 경우 오류가 발생한다. 따라서 일반적으로 여러 개의 레코드를 검색해야 할 경우 커서를 사용해야 한다. 커서는 선언부에 커서명과 함께 수행할 SELECT 문을 정의하고, 바디에서 OPEN, FETCH, CLOSE 또는 CURSOR FOR LOOP 을 사용해서 여러 개의 레코드를 읽어올 수 있게 하는 객체이다. 사용자는 커서와 관련해서 수행 도중 커서의 상태를 파악하기 위해서 알티베이스가 관리하고 있는 속성값들을 참조할 수 있다. DECLARE 커서의 이름과 커서가 수행할 SELECT 문을 정의하는 커서 선언부이다. OPEN 커서의 사용을 위해서 커서와 관련된 모든 리소스들을 초기화 하는 단계이다. 커서 정의시에 사용자 파라미터를 지정한 경우 OPEN 구문에서 파라미터 값을 전달한다. FETCH 커서의 SELECT 문을 만족하는 행을 하나씩 가져와서 사용자 변수에 저장한다. CLOSE 사용이 끝난 커서의 리소스를 해제한다. OPEN 문을 사용해서 열어둔 커서는 해당 프로시러가 종료되기 전에 반드시 CLOSE 문을 사용해서 닫아야 한다. 커서 95 CURSOR FOR LOOP 커서의 OPEN, FETCH, CLOSE 단계를 한번에 수행하는 LOOP 문이다. 결과 레코드가 존재하지 않을 때까지 LOOP 를 반복 수행한다. 커서에 대해서 명시적으로 OPEN 문이나 CLOSE 문을 사용할 필요가 없는 경우에 편리한 구문이다. 96 ALTIBASE5 Stored Procedure User’s Manual DECLARE CURSOR 구문 cursor_declaration := CURSORcursor_name cursor_parameter_declaration() , ISselect_statement; cursor_parameter_declaration ::= parameter_name IN data_type DEFAULT := cursor_name data_type ::= sql_data_type type_attribute 기능 커서를 정의한다. DECLARE 구문에서는 커서명과 가져올 데이터를 선택하는 SELECT 문을 정의해야 한다. Cursor Name 커서 97 OPEN, FETCH, CLOSE 및 CURSOR FOR LOOP 에서 사용하게 될 커서의 이름을 지정한다. Parameter 커서의 SELECT 문에 인자가 필요한 경우, 프로시저의 파라미터를 지정하는 문법과 같이 파라미터들을 지정한다. 이 인자들은 SELECT 문 안에서만 사용할 수 있으며, 다음과 같은 제약이 있다. y ROWTYPE 불가능 y OUT/INOUT 형식의 불가능 이 파라미터들은 커서 OPEN 이나 커서 FOR 문에서 값을 받아서 SELECT 문을 수행하게 된다. DECLARE CURSOR c1 IS SELECT empno, ename, job, sal FROM emp WHERE sal > 2000; CURSOR c2 (low INTEGER DEFAULT 0, high INTEGER DEFAULT 99) IS SELECT ......; Date Type 지역 변수 선언부를 참고한다. 예제 CREATE TABLE highsal (eno INTEGER, ename CHAR(10), salary NUMBER(10,2)); Create success. iSQL> CREATE OR REPLACE PROCEDURE proc1 AS BEGIN DECLARE CURSOR c1 IS SELECT eno, ename, salary FROM employee WHERE salary IS not NUL ORDER BY salary desc; emp_name CHAR(10); emp_no INTEGER; emp_sal NUMBER(10,2); BEGIN OPEN c1; FOR i IN 1 .. 5 LOOP FETCH c1 INTO emp_no, emp_name, emp_sal; EXIT WHEN c1%NOTFOUND; INSERT INTO highsal VALUES(emp_no, emp_name, emp_sal); END LOP; CLOSE c1; END; END; / 98 ALTIBASE5 Stored Procedure User’s Manual iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM highsal; HIGHSAL.ENO HIGHSAL.ENAME HIGHSAL.SALARY ---------------------------------- 10 YHBAE 40 1 MSKIM 2750 5 SJKIM 250 16 JHCHOI 230 14 KCJUNG 2003000 5 rows selected. 커서 99 OPEN CURSOR 구문 open_cursor_statement := OPENcursor_name ()cursor_parameter_name ; , 기능 커서를 만족하는 데이터를 FETCH 하기 위해 커서를 초기화 한다. 시스템은 커서를 사용하기 위한 모든 리소스들을 할당받게 된다. 이미 OPEN 된 커서를 다시 OPEN 하려고 시도하면 CURSOR_ALREADY_OPEN 에러가 발생한다. Cursor Name OPEN 하고자 하는 커서명을 지정한다. 이 커서명은 블록의 선언부에 선언되어 있어야 한다. Cursor Parameter Name 커서에 사용자 파라미터가 지정된 경우 값을 지정한다. 아래 DECLARE CURSOR c1 IS SELECT empno, ename, job, sal FROM emp WHERE emp_name > :emp_NM; BEGIN OPEN c1; ...... END; 인자가 존재하는 경우 다음과 같이 커서 OPEN 시 값을 넘겨준다. OPEN c1(emp_name, 100); OPEN c1('mylee', 100); OPEN c1(emp_name, dept_no); 100 ALTIBASE5 Stored Procedure User’s Manual 예제 예제 1 CREATE TABLE mgr (mgr_eno INTEGER, mgr_name CHAR(20), mgr_dno SMALLINT); CREATE OR REPLACE PROCEDURE proc1 AS BEGIN DECLARE CURSOR emp_cur IS SELECT eno, ename, dno FROM employee WHERE emp_job = 'MANAGER'; emp_no employee.eno%TYPE; emp_name employee.ename%TYPE; emp_dno employee.dno%TYPE; BEGIN OPEN emp_cur; LOOP FETCH emp_cur INTO emp_no, emp_name, emp_dno; EXIT WHEN emp_cur%NOTFOUND; INSERT INTO mgr VALUES(emp_no, emp_name, emp_dno); END LOP; CLOSE emp_cur; END; END; / iSQL> EXEC proc1; Execute success. iSQL> select * from mgr; MGR.MGR_ENO MGR.MGR_NAME MGR.MGR_DNO ----------------------------------- 7 HJMIN 402 8 JDLE 401 16 JHCHOI 101 3 rows selected. 예제 2 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE TABLE t2(i1 INTEGER, i2 INTEGER, i3 INTEGER); INSERT INTO t1 VALUES(1,1,1); INSERT INTO t1 VALUES(2,2,2); INSERT INTO t1 VALUES(30,30,30); INSERT INTO t1 VALUES(50,50,50); CREATE OR REPLACE PROCEDURE proc1 AS CURSOR c1(k1 INTEGER, k2 INTEGER, k3 INTEGER) IS SELECT * FROM t1 WHERE i1 <= k1 AND i2 <= k2 AND i3 <= k3; BEGIN FOR rec1 IN c1(2,2,2) LOOP 커서 101 INSERT INTO t2 VALUES (rec1.i1, rec1.i2, rec1.i3); END LOOP; END; / iSQL> SELECT * FROM t2; T2.I1 T2.I2 T2.I3 ------------------------------- No rows selected. iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t2; T2.I1 T2.I2 T2.I3 ------------------------------- 1 1 1 2 2 2 2 rows selected. 102 ALTIBASE5 Stored Procedure User’s Manual FETCH 구문 fetch_statement ::= FETCHcursor_nameINTO record_name variable_name ; , 기능 이미 OPEN 된 커서로부터 하나의 행을 가져와서 INTO 절에 명시된 변수에 그 값을 저장한다. 이 때, 커서의 SELECT 문에 명시한 컬럼들에 상응하는 사용자변수를 각각 나열하거나 레코드명만 명시해서 하나의 레코드 변수에 저장되도록 할 수도 있다. FETCH 한 결과행을 레코드 변수에 저장시킬 경우에 레코드 변수는 오직 한 개만 와야 하며, SELECT 문이 지정 하는 모든 컬럼을 다 수용할 수 있어야 하고, 일반 변수와 섞어서 사용할 수 없다. 만약 OPEN 되지 않은 커서로부터 FETCH 를 하려고 시도하면 INVALID_CURSOR 오류가 발생한다. Cursor Name FETCH 하고자 하는 커서명을 지정한다. 이 커서명은 블록의 선언부에 선언되어 있어야 한다. Record Name 커서의 SELECT 문이 반환하는 레코드를 받을 레코드형 변수명을 지정한다. 이때 사용되는 레코드형 변수는 SEELCT 구문의 select- list 와 컬럼의 개수와 TYPE 이 순서대로 정확히 일치해야 하기 때문에 커서의 SELECT 문이 하나의 테이블의 모든 컬럼을 가져오는 경우, 해당 테이블의 ROWTYPE 변수를 많이 사용하게 된다. 커서 103 Variable Name 커서의 SELECT 문이 반환하는 레코드를 받을 사용자 변수를 순서대로 나열한다. LOOP FETCH c1 INTO my_name, my_empno, my_deptno; EXIT WHEN c1%NOTFOUND; END LOOP; 예제 CREATE TABLE emp_temp(eno INTEGER, ename CHAR(20)); CREATE OR REPLACE PROCEDURE proc1 AS BEGIN DECLARE CURSOR c1 ISELECT eno, ename FROM employee; emp_rec c1%ROWTYPE; BEGIN OPEN c1; LOP FETCH c1 INTO emp_rec; EXIT WHEN c1%NOTFOUND; INSERT INTO emp_temp VALUES(emp_rec.eno, emp_rec.ename); END LOP; CLOSE c1; END; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM emp_temp; EMP_TEMP.ENO EMP_TEMP.ENAME --------------------------- 1 SWNO 2 HJNO 3 HSCHOI … 19 KMKIM 20 DIKIM 20 rows selected. 104 ALTIBASE5 Stored Procedure User’s Manual CURSOR FOR LOOP 구문 cursor_for_loop_statement ::= label_name<< INcounter_nameFOR >> cursor_name ()cursor_parameter_name , LOOP label_name ;END LOOPstatement 기능 OPEN, FETCH, CLOSE 를 자동으로 처리하는 기능을 한다. CURSOR FOR LOOP 은 블록 선언부에 선언한 커서를 이용하여, 커서의 조건을 만족하는 행을 FETCH 할때마다 한 번의 LOOP 을 수행한다. 이 때 하나의 행은 ROWTYPE 의 변수에 저장되므로 LOOP 안에서 이를 자유롭게 사용할 수 있다. label name EXIT 문이나 CONTINUE 문에서 LOOP 을 지칭하기 위해서 사용하도록 LABEL 을 지정할 수 있다. counter name 커서를 이용하여 FETCH 된 하나의 행이 저장될 ROWTYPE 의 변수명을 지정한다. 이 변수는 블록의 선언부에 선언되어 있지 않아도 FETCH 된 행이 지니는 컬럼의 개수와 TYPE 에 맞도록 자동으로 생성된다. 커서 105 이렇게 생성된 변수는 counter_name.column_name 형식으로 참조할 수 있다. 이때 column_name 은 SELECT 구문의 select-list 에 존재하는 칼럼명이다. 따라서 select-list 에 표현식을 사용한 경우는 해당 select-list 에 별칭(alias)를 지정하여 참조 가능하도록 만들어야 한다. Cursor Name CURSOR FOR LOOP 에서 사용하게될 커서를 지정한다. 이 커서는 블록의 선언부에 선언되어 있어야 한다. Cursor Parameter Name 커서에 사용자 지정 파라미터가 필요한 경우 파라미터에 값을 지정한다. DECLARE CURSOR c1 IS SELECT id, name, sal FROM emp; total INTEGER := 0; BEGIN FOR emp_one IN c1 LOOP total := total + emp_one.sal; END LOOP; ...... END; 예제 CREATE TABLE emp_temp(eno INTEGER, ename CHAR(20)); CREATE OR REPLACE PROCEDURE proc1 AS BEGIN DECLARE CURSOR c1 IS SELECT eno, ename FROM employee; BEGIN FOR emp_rec IN c1 LOP INSERT INTO emp_temp VALUES(emp_rec.eno, emp_rec.ename); END LOP; END; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM emp_temp; EMP_TEMP.ENO EMP_TEMP.ENAME --------------------------- 1 SWNO 2 HJNO … 19 KMKIM 20 DIKIM 20 rows selected. 106 ALTIBASE5 Stored Procedure User’s Manual CLOSE CURSOR 구문 close_cursor_statem ent := CLOSEcu rso r_ n a m e; 기능 열려있는 커서를 닫고 해당 커서에 할당된 리소스를 해제한다. 커서가 열려있지 않았거나, 이미 닫힌 커서를 닫으려고 하면 INVALID_CURSOR 에러가 발생한다. 사용자가 커서를 명시적으로 CLOSE 하지 않아도 커서가 선언된 블록을 빠져나가게 되면 자동으로 CLOSE 되지만, 커서를 CLOSE 한다는 것은 커서와 관련된 모든 리소스를 시스템에 반납한다는 것을 의미하므로, 사용이 끝난 커서는 즉시 CLOSE 해주는 것이 바람직하다. Cursor Name CLOSE 하고자 하는 커서의 이름을 명시한다. 예제 CLOSE c1; 커서 107 CURSOR 속성 구문 c u rs o r_ a ttre ib u te ::= % FOUND NOTFOUND ISOPEN ROWCOUNT ;cursor_nam e 기능 특정 커서의 속성을 참조할 수 있다. 커서 속성은 커서의 상태를 나타내는 BOOLEAN 형의 표현식이다. 현재 커서의 상태에 따라 각각의 속성 값이 TRUE 와 FALSE 을 갖는다. 사용자는 DECLARE 구문에 직접 정의한 커서의 속성 값뿐만 아니라 시스템 내부에서 정의되는 암시적 커서의 속성 값도 참조할 수 있다. 암시적 커서는 DELETE, UPDATE, INSERT, 한건의 레코드가 반환되는 SELECT INTO 구문에 선언되며, 가장 최근에 수행된 SQL 구문의 커서 속성 값을 갖는다. %FOUND 커서를 정의한 SELECT 문의 조건을 만족하는 행이 존재하는지 여부를 나타낸다. 단, 다음의 경우에는 조건을 만족하는 행의 여부에 상관없이 %FOUND 의 값이 무조건 FALSE 이다. y OPEN 하기 전의 커서 y FETCH 를 한번도 하지 않은 커서 y CLOSE 한 후의 커서 암시적 커서는 DELETE, UPDATE, INSERT 의 수행 결과로 1 건 이상이 영향을 받거나, SELECT INTO 의 반환되는 레코드가 1 건 이상일때 %FOUND 의 값은 TRUE 가 된다. 108 ALTIBASE5 Stored Procedure User’s Manual 그러나 SELECT INTO 의 반환되는 레코드가 2 건 이상일 때에는 %FOUND 를 검사하기 전에 Too many rows exception 이 발생하므로, 이 경우 %FOUND 커서 속성이 아닌 exception 으로 처리해야 한다. %FOUND 의 값은 다음과 같이 참조한다. DELETE FROM emp; IF SQL%FOUND THEN -- delete succeeded INSERT INTO emp VALUES ( ...... ); ...... END IF; %NOTFOUND 커서를 정의한 SELECT 문의 조건을 만족하는 행이 존재하는지 여부를 나타낸다. 항상 %FOUND 와 반대의 값을 지닌다. 암시적 커서는 DELETE, UPDATE, INSERT 구문이 성공적으로 수행되었으나, 영향을 받은 레코드가 하나도 없거나, 또는 SELECT INTO 의 반환되는 레코드가 하나도 없을 때 %NOTFOUND 의 값이 TRUE 로 된다. 그러나 SELECT INTO 에 반환되는 레코드가 없을 때 %NOTFOUND 속성을 검사하기 전에 NO_DATA_FOUND exception 이 발생하므로, %NOTFOUND 커서 속성이 아닌 exception 으로 처리한다. %NOTFOUND 의 값은 다음과 같이 참조한다. DELETE FROM emp; IF SQL%NOTFOUND THEN ...... END IF; %ISOPEN 커서가 OPEN 되었는지의 여부를 나타낸다. 커서가 CLOSE 되면 이 값은 FALSE 가 된다. %ISOPEN 의 값은 다음과 같이 참조한다. OPEN c1; IF c1%ISOPEN THEN ...... END IF; %ROWCOUNT 현재 커서를 사용하여 몇 개의 행을 FETCH 하였는지를 나타낸다. 주의할 점은 %ROWCOUNT 는 커서의 SELECT 문을 만족하는 행의 수가 아니라, FETCH 를 성공할 때마다 1 씩 증가한다는 것이다. FETCH 를 한번도 하지 않은 커서의 경우 %ROWCOUNT 는 0 이다. OPEN 하기 전의 커서와 CLOSE 이후의 커서 상태는 INVALID CURSOR 에러를 반환한다. DELETE FROM emp; 커서 109 IF SQL%ROWCOUNT > 10 THEN ...... END IF; 예제 예제 1 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE TABLE t3(i1 INTEGER); INSERT INTO t1 VALUES(2,2,2); CREATE OR REPLACE PROCEDURE proc1 AS v1 INTEGER; BEGIN SELECT i1 INTO v1 FROM t1 WHERE i1 = 2; IF SQL%found THEN INSERT INTO t1 SELECT * FROM t1; v1 := SQL%ROWCOUNT; INSERT INTO t3 VALUES(v1); END IF; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t3; T3.I1 --------- 1 1 row selected. 예제 2 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE TABLE t2(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE TABLE t3(i1 INTEGER); INSERT INTO t1 VALUES(1,1,1); INSERT INTO t1 VALUES(1,1,1); INSERT INTO t1 VALUES(1,1,1); CREATE OR REPLACE PROCEDURE proc1 AS CURSOR c1 ISELECT * FROM t1; v1 INTEGER; v2 INTEGER; v3 INTEGER; BEGIN OPEN c1; IF c1%ISOPEN THEN LOP FETCH c1 INTO v1, v2, v3; 110 ALTIBASE5 Stored Procedure User’s Manual IF c1%FOUND THEN INSERT INTO t2 VALUES (v1, v2, v3); ELSIF c1%NOTFOUND THEN EXIT; END IF; END LOP; END IF; v1 := c1%ROWCOUNT; INSERT INTO t3 VALUES (v1); CLOSE c1; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ------------------------------- 1 1 1 1 1 1 1 1 1 3 rows selected. iSQL> SELECT * FROM t2; T2.I1 T2.I2 T2.I3 ------------------------------- 1 1 1 1 1 1 1 1 1 3 rows selected. iSQL> SELECT * FROM t3; T3.I1 --------- 3 1 row selected. 예제 3 CREATE TABLE emp_temp(eno INTEGER, ename CHAR(20)); CREATE OR REPLACE PROCEDURE proc1 AS BEGIN DECLARE CURSOR c1 IS SELECT eno, ename FROM employee; emp_rec c1%ROWTYPE; BEGIN OPEN c1; LOOP FETCH c1 INTO emp_rec; EXIT WHEN c1%ROWCOUNT > 10 OR c1%NOTFOUND; INSERT INTO emp_temp VALUES(emp_rec.eno, emp_rec.ename); END LOP; CLOSE c1; END; END; / 커서 111 iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM emp_temp; EMP_TEMP.ENO EMP_TEMP.ENAME --------------------------- 1 SWNO 2 HJNO 3 HSCHOI 4 KSKIM 5 SJKIM 6 HYCHOI 7 HJMIN 8 JDLE 9 KMLE 10 YHBAE 10 rows selected. 112 ALTIBASE5 Stored Procedure User’s Manual 6. 사용자 정의 타입 이 장에서는 저장 프로시저와 저장 함수에서 사용할 수 있는 사용자 정의 타입에 대해서 설명하고 있다. 사용자 정의 타입 113 개요 저장 프로시저의 사용자 정의 타입인 RECORD 및 ASSOCIATIVE ARRAY 는 데이터를 논리적 단위로 구성하여 처리하는 기능을 제공한다. 또한 저장 프로시저 또는 함수의 파라미터 또는 리턴 타입으로 사용이 가능하다. 단, 클라이언트로는 해당 타입의 값을 전송할 수 없다. RECORD RECORD 는 컬럼의 집합으로 이루어진 사용자 정의 데이터 타입이다. 이를 이용하여 각각의 서로 다른 종류의 데이터들을 논리적 단위로 구성하여 처리할 수 있다.예를 들어 이름 , 급여, 부서 등의 서로 다른 데이터들을 ‘사원’ 이라는 하나의 데이터로 처리하는 경우 하나의 RECORD 타입으로 묶어 처리할 수 있다. RECORD 의 선언은 DECLARE TYPE 섹션을 참고한다. 그 외 사용방법은 %ROWTYPE 과 동일하다. ASOCIATIVE ARRAY ASSOCIATIVE ARRAY 는 INTEGER 또는 VARCHAR 를 배열 요소의 접근자로 사용하는 배열로 프로그래밍 언어의 해시 테이블과 유사하다. 이를 이용하여 데이터의 개수와 상관없이 동일한 형식의 데이터들을 하나의 단위로 묶어 처리를 할 수 있다. 예를 들어 사원들중 사원번호 1~100 까지의 사원 데이터를 처리하는 경우 100 개의 데이터를 하나의 ASSOCIATIVE ARRAY 타입의 변수로 처리할 수 있다. ASSOCIATIVE ARRAY 의 선언은 DECLARE TYPE 섹션을 참고한다. ASSOCIATIVE ARRAY 변수의 배열 요소 접근을 위해서는 다음과 같이 대괄호( [ ] )를 사용한다. 예) V1[ 1 ] := 1; REF CURSOR (커서 변수) 커서 변수는 다중 레코드를 검색하는 동적 SQL 문에 대한 변수이다. 이러한 커서 변수는 특정 질의문에 종속되지 않기 때문에 커서보다 훨씬 유연하다. 프로시저간 파라미터로 전달이 가능하고, 114 ALTIBASE5 Stored Procedure User’s Manual 클라이언트로도 전달할 수 있다. 커서와 다른 점은 커서는 항상 동일한 질의문을 참조하지만 커서 변수는 커서 OPEN 시에 다른 질의문을 참조할 수 있다는 점이다. 사용자 정의 타입 115 DECLARE TYPE 구문 type_name 116 ALTIBASE5 Stored Procedure User’s Manual 사용자 정의 타입의 이름을 명시한다. associative_array_spec data_type 으로 이루어진 ASSOCIATIVE ARRAY 타입을 생성한다. 단, data_type 에 ASSOCIATIVE ARRAY 는 올 수 없다. 즉 ASSOCIATIVE ARRAY 타입의 ASSOCIATIVE ARRAY 는 생성할 수 없다. record_type_spec Sql_data_type 으로 이루어진 레코드 형식의 타입을 생성한다. 단, ASSOCIATIVE ARRAY 타입, RECORD 타입은 RECORD 타입의 구성 요소로 사용될 수 없다. ref_cursor_type_spec REF CURSOR 타입을 생성한다. 예제 예제 1 이름(VARCHAR(20)), 부서(INTEGER), 봉급(NUMBER(8))을 갖는 사원 RECORD 타입 생성 DECLARE TYPE employee IS RECORD( name VARCHAR(20), dept INTEGER, salary NUMBER(8); … BEGIN … 예제 2 VARCHAR(20)을 구성 요소로 하고 INTEGER 를 인덱스로 하는 이름 리스트 ASSOCIATIVE ARRAY 타입 생성 DECLARE TYPE namelist IS TABLE OF VARCHAR(20) INDEX BY INTEGER; … BEGIN … 예제 3 사원 타입을 구성 요소로 가지고 VARCHAR(10)을 인덱스로 하는 사원리스트 ASSOCIATIVE ARRAY 타입 생성 DECLARE TYPE employee IS RECORD( name VARCHAR(20), dept INTEGER, salary NUMBER(8); TYPE employeelist IS TABLE OF employee 사용자 정의 타입 117 INDEX BY VARCHAR(10)); … BEGIN … 118 ALTIBASE5 Stored Procedure User’s Manual ASSOCIATIVE ARRAY TYPE 의 함수 구문 기능 ASSOCIATIVE ARRAY 타입 변수의 구성 요소 관리를 위한 여러가지 기능을 제공한다. 위 함수들은 SQL 함수와 달리 ()를 생략할 수 없다. COUNT Associative array 의 구성 요소의 개수를 반환. DELETE DELETE()는 모든 구성 요소를 제거하고 제거된 구성 요소의 개수를 반환. DELETE(n)은 n 에 해당하는 요소를 제거하고 제거된 구성 요소의 개수를 반환. 사용자 정의 타입 119 DELETE(m, n)은 범위 n~m 에 속하는 모든 구성 요소를 제거하고 제거된 구성 요소의 개수를 반환. EXISTS 해당 인덱스에 구성 요소가 존재하는지 검사 FIRST 첫번째 인덱스를 반환. 아무런 구성 요소가 없으면 NULL 을 반환. LAST 마지막 인덱스를 반환. 아무런 구성 요소가 없으면 NULL 을 반환. NEXT 해당 인덱스의 다음 인덱스를 반환. 다음 인덱스가 없으면 NULL 을 반환. PRIOR 해당 인덱스의 이전 인덱스를 반환. 이전 인덱스가 없으면 NULL 을 반환. 예제 예제 1 ASSOCIATIVE ARRAY 타입 변수 V1 의 구성 요소를 삭제 CREATE OR REPLACE PROCEDURE PROC1( P1 IN VARCHAR(10), P2 IN VARCHAR(10) ) AS TYPE MY_ARR IS TABLE OF INTEGER INDEX BY VARCHAR(10); V1 MY_AR; V2 INTEGER; BEGIN V1['FSDGADS'] := 1; V1['A'] := 2; V1['7G65'] := 3; V1['N887K'] := 4; V1['KU'] := 5; V1['34'] := 6; PRINTLN( 'V1 COUNT IS : '||V1.COUNT() ); V2 := V1.DELETE(P1, P2); PRINTLN( 'DELETED COUNT IS : '||V2); PRINTLN( 'V1 COUNT IS : '||V1.COUNT() ); END; / 120 ALTIBASE5 Stored Procedure User’s Manual 실행 결과 EXEC PROC1('005T34', 'BC35'); -- 이 범위에 포함되는 인덱스는 '34', '7G65', 'A'. 총 3 개가 지워져야 함. V1 COUNT IS : 6 DELETED COUNT IS : 3 V1 COUNT IS : 3 Execute success. 예제 2 ASSOCIATIVE ARRAY 타입 변수 V1 을 오름차순, 내림차순으로 출력 CREATE OR REPLACE PROCEDURE PROC1 AS TYPE MY_AR1 IS TABLE OF INTEGER INDEX BY INTEGER; V1 MY_AR1; V1_IDX INTEGER; BEGIN V1[435754] := 1; V1[95464] := 2; V1[38] := 3; V1[57334] := 4; V1[138] := 5; V1[85462] := 6; PRINTLN( 'ASCENDING ORDER V1'); V1_IDX := V1.FIRST(); LOP IF V1_IDX IS NUL THEN EXIT; ELSE PRINTLN( 'V1 IDX IS : '||V1_IDX||' VALUE IS : '||V1[V1_IDX] ); V1_IDX := V1.NEXT(V1_IDX); END IF; END LOOP; PRINTLN( 'DESCENDING ORDER V1'); V1_IDX := V1.LAST(); LOP IF V1_IDX IS NUL THEN EXIT; ELSE PRINTLN( 'V1 IDX IS : '||V1_IDX||' VALUE IS : '||V1[V1_IDX] ); V1_IDX := V1.PRIOR(V1_IDX); END IF; END LOOP; END; / 사용자 정의 타입 121 실행 결과 EXEC PROC1; ASCENDING ORDER V1 V1 IDX IS : 38 VALUE IS : 3 V1 IDX IS : 138 VALUE IS : 5 V1 IDX IS : 57334 VALUE IS : 4 V1 IDX IS : 85462 VALUE IS : 6 V1 IDX IS : 95464 VALUE IS : 2 V1 IDX IS : 435754 VALUE IS : 1 DESCENDING ORDER V1 V1 IDX IS : 435754 VALUE IS : 1 V1 IDX IS : 95464 VALUE IS : 2 V1 IDX IS : 85462 VALUE IS : 6 V1 IDX IS : 57334 VALUE IS : 4 V1 IDX IS : 138 VALUE IS : 5 V1 IDX IS : 38 VALUE IS : 3 Execute success. 122 ALTIBASE5 Stored Procedure User’s Manual RECORD 및 ASSOCIATIVE ARRAY 의 사용 여기서는 사용자 정의 타입을 저장 프로시저 내에서 사용할 때의 규칙 및 예제를 다룬다. 사용자 정의 타입을 파라미터 및 리턴값으로 사용하는 부분은 7 장 타입 세트를 참고한다. 타입 호환성 L_VALUE := R_VALUE; 위와 같은 할당문의 유형에서 사용자 정의 타입의 타입 호환성을 살펴보면 다음과 같다. L_VALUE R_VALUE 호환성 RECORD 타입 RECORD 타입 동일한 이름의 타입간에만 호환 가능 RECORD 타입 %ROWTYPE 내부에 정의된 칼럼의 개수만 동일하면 호환 가능 %ROWTYPE RECORD 타입 내부에 정의된 칼럼의 개수만 동일하면 호환 가능 ASSOCIATIVE ARRAY 타입 ASSOCIATIVE ARRAY 타입 동일한 이름의 타입간에만 호환 가능 다음 예제와 같이 사용자 정의 타입은 내부 구조가 동일하더라도 할당문은 실패하게 된다. 예제 1 RECORD 타입 변수의 할당 CREATE OR REPLACE PROCEDURE PROC1 AS TYPE emp_rec_type1 IS RECORD ( name VARCHAR(20), job_id VARCHAR(10), salary NUMBER(8) ); TYPE emp_rec_type2 IS RECORD ( name VARCHAR(20), job_id VARCHAR(10), salary NUMBER(8) ); v_emp1 emp_rec_type1; v_emp2 emp_rec_type2; BEGIN v_emp1.name := 'smith'; v_emp1.job_id := 'RND1069'; v_emp1.salary := '10000000'; 사용자 정의 타입 123 v_emp2 := v_emp1; - 실패. 타입 구조는 같지만 참조하는 타입의 이름이 달라서 실패한다. 그러나 v_emp2.name := v_emp1.name; 로 할 경우에는 변환이 가능해 성공한다. RECORD 타입 예제 예제 1 사원의 이름, 급여, 부서를 저장하는 RECORD 변수 생성 CREATE OR REPLACE PROCEDURE PROC1 AS TYPE emp_rec_type IS RECORD ( name VARCHAR(20), job_id VARCHAR(10), salary NUMBER(8) ); v_emp emp_rec_type; BEGIN v_emp.name := 'smith'; v_emp.job_id := 'RND1069'; v_emp.salary := '10000000'; PRINTLN('NAME : '||v_emp.name||' '|| 'JOB ID : '||v_emp.job_id||' '|| 'SALARY : '|v_emp.salary ); END; / ASOCIATIVE ARRAY 타입 예제 예제 1 사원번호가 1~100 사이에 속한 사원의 이름을 출력한다. CREATE OR REPLACE PROCEDURE PROC1 AS TYPE emp_aray_type IS TABLE OF VARCHAR(20) INDEX BY INTEGER; v_emp emp_array_type; BEGIN FOR I IN 1 .. 100 LOOP SELECT name INTO v_emp[I] FROM employees WHERE emp_id = I; END LOOP; FOR I IN v_emp.FIRST() .. v_emp.LAST() LOOP PRINTLN( v_emp[I] ); END LOOP; END; / 124 ALTIBASE5 Stored Procedure User’s Manual 예제 2 사원번호가 1~100 사이에 속한 사원의 이름, 급여, 부서를 출력한다. CREATE OR REPLACE PROCEDURE PROC1 AS TYPE emp_rec_type IS RECORD ( name VARCHAR(20), job_id VARCHAR(10), salary NUMBER(8) ); TYPE emp_aray_type IS TABLE OF emp_rec_type INDEX BY INTEGER; v_emp emp_array_type; BEGIN FOR I IN 1 .. 100 LOOP SELECT name, job_id, salary INTO v_emp[I] FROM employees WHERE emp_id = I; END LOOP; FOR I IN v_emp.FIRST() .. v_emp.LAST() LOOP PRINTLN( v_emp[I].name|| ’ ’|| v_emp[I].job_id|| ’ ’|| v_emp[I].salary ); END LOOP; END; / 사용자 정의 타입 125 REF CURSOR 저장 프로시저는 동적 SQL 의 실행 결과를 커서 변수(REF CURSOR)를 통해 클라이언트에게 전달할 수 있다. OPEN FOR 문으로 커서 변수를 열고, 이를 이용하여 다중 레코드를 결과로 반환하는 질의를 실행할 수 있다. 커서 변수를 열기 위한 OPEN FOR 구문을 제외하고, 기존의 커서 관련 구문을 그대로 이용할 수 있다. 커서 변수는 저장 프로시저의 OUT, IN OUT 파라미터로만 사용할 수 있으며 RETURN 문으로 반환할 수 없다. 저장 프로시저에서 OPEN 된 상태로 커서 변수가 전달되어야 결과집합을 FETCH 할 수 있다. 즉, 커서 상태가 CLOSE 상태로 전달될 경우 결과집합을 사용할 수 없다. 저장 프로시저에서 UPDATE, INSERT 문을 실행할 경우, 영향 받은 레코드 수(Affected Row Count)는 반환하지 않는다. 클라이언트에서 커서 변수로 결과집합을 받는 방법은 클라이언트의 형태에 따라 달라진다. 커서 변수를 이용하여 결과 집합을 클라이언트로 전달하는 것은 ODBC 와 JDBC 를 통해서만 가능하다. Embedded SQL (Precompiler, SESC)에서는 커서 변수를 통해 프로시저의 결과 집합을 전달할 수 없다. 예제 REF CURSOR를 이용한 저장 프로시저를 생성한다. 1. 테이블 emp, staff 생성 및 값 입력 create table emp (eno integer, ename char(20), dno integer); create table staff (name char(20), dept char(20), job char(20), salary integer); insert into emp values (10, 'dulgi papa', 100); insert into emp values (20, 'kunhan' , 200); insert into emp values (30, 'okasa' , 300); insert into staff values ('dulgi papa' , '100' , 'papa', 10); insert into staff values ('shincha' , '200' , 'engineer' , 200); insert into staff values ('ji hyung', '300', '', 0); 2. 타입세트 MY_TYPE: MY_CUR 정의 create typeset MY_TYPE as 126 ALTIBASE5 Stored Procedure User’s Manual type MY_CUR is REF CURSOR; end; / 3. MY_CUR 를 OUT 파라미터로 하는 두 개의 변수(P1, P2)와 INTEGER 형태의 IN 파라미터 SAL 을 가지는 프로시져 PROC1 생성 create or replace procedure PROC1 (P1 out MY_TYPE.MY_CUR, P2 out MY_TYPE.MY_CUR, SAL in INTEGER) as sql_stmt VARCHAR2(20); begin sql_stmt := 'SELECT name,dept,job FROM staff WHERE salary > ?'; OPEN P1 FOR 'SELECT eno, ename, dno FROM emp'; OPEN P2 FOR sql_stmt USING SAL; end; / 4. DB 연결(db_connect()) 후에, 프로시져 PROC1 실행 SQLRETURN execute_proc() { SQLCHAR errMsg[MSG_LEN]; char sql[1000]; SQLHSTMT stmt = SQL_NULL_HSTMT; int sal; int sal_len; int eno; int eno_len; int dno; int dno_len; SQLCHAR ename[ENAME_LEN+1]; SQLCHAR name[NAME_LEN+1]; SQLCHAR dept[DEPT_LEN+1]; SQLCHAR job[JOB_LEN+1]; int job_ind; SQLRETURN rc = SQL_SUCESS; if (SQL_ERROR == SQLAllocStmt(dbc, printf("SQLAlocStmt eror!n"); return SQL_EROR; } /* 실행할 SQL 문을 준비 */ sprintf(sql, "EXEC proc1(?)"); if ( SQLPrepare(stmt,(SQLCHAR *)sql,SQL_NTS) == SQL_EROR ) { printf("ERROR: prepare stmtn"); } else { printf("SUCCESS: prepare stmtn"); } 사용자 정의 타입 127 /* 변수 sal 에 100 을 할당 */ sal = 100; /* SQL 문장에 매개변수(sal)를 연결시킴 */ if ( SQLBindParameter( stmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, NUL) = SQL_EROR ) { printf("ERROR: Bind Parametern"); } else { printf("SUCCES: 1 Bind Parametern"); } /* SQL 문장 실행, 프로시져 PROC1 을 실행해서 'SELECT eno, ename, dno FROM emp'의 결과값은 P1 에 'SELECT name,dept,job FROM staff WHERE salary > ?'(USING SAL)의 결과값은 P2 에 가져온다 */ if (SQL_ERROR == SQLExecute(stmt)) { printf("ERROR: Execute Proceduren"); } /* 'SELECT eno, ename, dno FROM emp'의 결과값을 변수(eno, ename, dno)에 저장 */ if (SQL_EROR == SQLBindCol(stmt, 1, SQL_C_SLONG, ong *) { printf("ERROR: Bind 1 Columnn"); } if (SQL_EROR == SQLBindCol(stmt, 2, SQL_C_CHAR, ename, sizeof(ename), NULL)) { printf("ERROR: Bind 2 Columnn"); } if (SQL_EROR == SQLBindCol(stmt, 3, SQL_C_SLONG, ong *) { printf("ERROR: Bind 3 Columnn"); } /* P1 에 결과값이 있는 동안 결과값을 받아 화면에 출력 */ while (SQL_SUCESS = rc) { rc = SQLFetch(stmt); if (SQL_SUCCESS == rc) { printf("Result Set 1 : %d,%s,%dn" ,eno, ename, dno); } else { if (SQL_NO_DATA = rc) 128 ALTIBASE5 Stored Procedure User’s Manual { break; } else { printf("EROR: SQLFetch [%d]n", rc); execute_err(dbc, stmt, sql); break; } } } /* 다음 결과(P2)로 이동 */ rc = SQLMoreResults(stmt); if (SQL_EROR == rc) { printf("ERROR: SQLMoreResultsn"); } else { /* 'SELECT name,dept,job FROM staff WHERE salary > ?'(USING SAL)의 결과값을 변수(name, dept, job)에 저장 */ if (SQL_ERROR == SQLBindCol(stmt, 1, SQL_C_CHAR, name, sizeof(name), NULL)) { printf("ERROR: Bind 1 Columnn"); } if (SQL_ERROR == SQLBindCol(stmt, 2, SQL_C_CHAR, dept, sizeof(dept), NULL)) { printf("ERROR: Bind 2 Columnn"); } if (SQL_ERROR == SQLBindCol(stmt, 3, SQL_C_CHAR, job, sizeof(job), (long *) { printf("ERROR: Bind 3 Columnn"); } /* P2 에 결과값이 있는 동안 결과값을 받아 화면에 출력 */ while (SQL_SUCCESS == rc) { rc = SQLFetch(stmt); if (SQL_SUCESS == rc) { if( job_ind == -1 ) printf("Result Set 2 : %s,%s,NULLn" ,name, dept); else printf("Result Set 2 : %s,%s,%sn" ,name, dept, job); } else { if (SQL_NO_DATA == rc) { break; } else { 사용자 정의 타입 129 printf("ERROR: SQLFetch [%d]n", rc); execute_err(dbc, stmt, sql); break; } } } } if (SQL_EROR == SQLFreeStmt( stmt, SQL_DROP )) { printf("sql fre stmt erorn"); } } 타입 세트 131 7. 타입 세트 이 장에서는 타입 세트를 정의하고 사용하는 방법에 대해 설명한다. 132 ALTIBASE5 Stored Procedure User’s Manual 타입 세트의 개요 타입 세트(Type Set)는 저장 프로시저에서 사용하는 사용자 정의 타입들을 한 곳에 모아서 관리하도록 해주는 데이터베이스 객체(Object)이다. 타입 세트의 특징 사용자 정의 타입의 공유 사용자 정의 타입을 한 곳에서 관리하므로 각각의 저장 프로시저 내에서 동일한 구조의 사용자 정의 타입을 중복해서 선언하지 않아도 된다. 사용자 정의 타입을 파라미터 또는 리턴값으로 사용 동일한 타입세트의 타입은 파라미터 또는 리턴값으로 프로시저 간 전달이 가능하다. 단, 클라이언트로는 전송할 수 없다. 저장 프로시저를 데이터의 논리적 단위로 통합 관리 타입세트를 사용하는 프로시저들을 데이터의 논리적 단위로 통합 관리하기 용이하다. 결과 집합 반환 프로시저 내부에서 실행된 SQL 문의 결과 집합을 REF CURSOR 타입을 사용하여 클라이언트로 반환할 수 있다. 구조 타입 세트를 사용하면 다음과 같이 여러 프로시저에서 사용자 데이터 타입 공유 및 관리가 가능하며, 데이터 이동이 용이하다. TYPESET 1 : emp_rec_type, emp_arr_type 정의 타입 세트 133 CREATE TYPESET typeset_1 AS TYPE emp_rec_type IS RECORD ( name VARCHAR(20), job_id VARCHAR(10), salary NUMBER(8) ); TYPE emp_ar_type IS TABLE OF emp_rec_type INDEX BY INTEGER; END; / PROCEDURE 1 : emp_arr_type을 OUT 파라미터로 하는 procedure_2 호출 CREATE PROCEDURE procedure_1 AS V1 typeset_1.emp_arr_type; BEGIN procedure_2( V1 ); PRINTLN(V1[1].name); PRINTLN(V1[1].job_id); PRINTLN(V1[1].salary); END; / PROCEDURE 2 : OUT 파라미터에 function_3의 반환값을 할당 CREATE PROCEDURE procedure_2 ( P1 OUT typeset_1.emp_arr_type ) AS V1 typeset_1.emp_rec_type; BEGIN V1 := function_3(); P1[1] := V1; END; / FUNCTION 3 : typeset_1.emp_rec_type을 반환 CREATE FUNCTION function_3 RETURN typeset_1.emp_rec_type AS V1 typeset_1.emp_rec_type; BEGIN V1.name := 'Smith'; V1.job_id := 1010; V1.salary := 200; RETURN V1; END; / 134 ALTIBASE5 Stored Procedure User’s Manual CREATE TYPESET 구문 전제 조건 SYS 사용자 또는 CREATE PROCEDURE, CREATE ANY PROCEDURE 시스템 권한을 가진 사용자여야 한다. 기능 저장 프로시저에서 사용할 사용자 정의 타입의 타입 세트를 정의한다. 타입 세트에서 정의한 타입은 프로시저의 INPUT / OUTPUT 파라미터로 사용할 때 용이하다. user_name 생성될 타입 세트의 소유자 이름을 명시한다. 생략하면 알티베이스는 현재 세션에 연결된 사용자의 스키마에 타입 세트를 생성한다 typeset_name 타입 세트의 이름이다. type_declaration 6 장 사용자 정의 타입. DECLARE TYPE 을 참고한다. 타입 세트 135 예제 예제 1 my_typeset 이란 이름의 타입 세트를 생성한다. CREATE TYPESET my_typeset AS TYPE emp_rec_type IS RECORD( name VARCHAR(20), id INTEGER ); TYPE emp_ar_type IS TABLE OF emp_rec_type INDEX BY INTEGER; END; / 예제 2 my_typeset 을 이용하는 프로시저 my_proc1 생성 CREATE PROCEDURE my_proc1 AS V1 my_typeset.emp_rec_type; V2 my_typeset.emp_arr_type; BEGIN V1.name := 'jejeong'; V1.id := 10761; V2[1] := V1; V1.name := 'ehkim'; V1.id := 1385; V2[2] := V1; V1.name := 'mslee'; V1.id := 13693; V2[3] := V1; PRINTLN('NAME : '||V2[1].name|| ' ID : '|V2[1].id ); PRINTLN('NAME : '||V2[2].name|| ' ID : '|V2[2].id ); PRINTLN('NAME : '||V2[3].name|| ' ID : '|V2[3].id ); END; / 결과 iSQL> exec my_proc1; NAME : jejeong ID : 10761 NAME : ehkim ID : 1385 NAME : mslee ID : 13693 Execute success. 136 ALTIBASE5 Stored Procedure User’s Manual DROP TYPESET 구문 전제 조건 SYS 사용자이거나 객체의 생성자 또는 DROP ANY PROCEDURE 시스템 권한을 가진 사용자여야 한다. 기능 명시된 타입 세트를 제거한다. 타입 세트를 사용하던 저장 프로시저는 유효하지 않은(Invalid) 상태가 된다. user_name 제거될 타입 세트의 소유자 이름을 명시한다. 생략하면 알티베이스는 현재 세션에 연결된 사용자의 스키마에 속한 것으로 간주한다. typeset_name 타입 세트의 이름이다. 예제 my_typeset 이란 이름의 타입 세트를 삭제한다. DROP TYPESET my_typeset; 동적 SQL 137 8. 동적 SQL 이 장에서는 저장 프로시저와 저장 함수에서 동적 SQL 을 사용하는 방법을 설명한다. 138 ALTIBASE5 Stored Procedure User’s Manual 동적 SQL 의 개요 동적 SQL(Dynamic SQL)은 실행 시간에 사용자가 원하는 질의를 만들어서 실행하는 것이다. 일반적인 저장 프로시저의 SQL 실행 방법은 정적인(Static) 방법으로, 저장 프로시저가 컴파일될 때 이미 실행계획이 생성된다. 컴파일 시에 작성하지 않은 SQL 구문은 동적 SQL 을 사용하지 않는 한 실행할 방법이 없다. 동적 SQL의 실행과정 저장 프로시저 내에서 동적 SQL 을 실행하였을 때의 과정을 설명하면 아래 그램과 같다. [그림 8-1] 정적 SQL과 동적 SQL의 실행 과정 비교 동적 SQL 139 [그림 8- 1]의 왼쪽 저장 프로시저는 ‘DELETE FROM T1’ 문을 정적으로 처리한 것이고, 오른쪽 저장 프로시저는 동일한 DELETE 문을, EXECUTE IMMEDIATE 를 사용하여 동적으로 처리한 것이다. 전자는 프로시저 생성 시에 이미 DELETE 문에 대한 실행계획이 만들어지고, 실행시에 수행(EXECUTE)을 하지만, 후자는 생성 시에 실행계획을 만들지 않고 수행 시에 DELETE 문에 대해 실행계획 생성+수행을 한다. 수행 시에 실행계획 생성+수행을 하기 때문에 수행 시에 SQL 문장이 다른 문장으로 바뀌어도 실행이 가능하다. 동적 SQL의 특징 동적 SQL 의 특징은 실행시에 SQL 문을 사용자 마음대로 변경하여 실행시킬 수 있다는 것이다. 또한 SQL 문의 종류에 관계없이 DBMS 가 지원하는 SQL 은 무엇이든 실행시킬 수 있다. 동적 SQL 은 다음의 상황에서 유용하게 사용된다. y 테이블의 이름을 동적으로 실행시에 받아서 SQL을 실행하는 작업 y 상황에 따라 질의의 힌트나 조건절의 조건 연산자를 바꾸어 실행하는 작업 y DDL/DML이 빈번하게 일어나서 항상 최적화가 필요한 SQL문을 저장 프로시저에서 사용할 경우 y 최적화 비용보다 실행 비용이 큰 SQL을 자주 실행하는 작업 y 재활용성이 높은 범용 저장 프로시저 작성 단, 동적 SQL 문은 문장의 생성/삭제 및 바인딩 비용이 매우 크므로 정적 SQL 과 비교해서 낮은 성능을 보일 수 있다. 동적 SQL 문의 사용은 응용프로그램 구조를 유연하게 하는 반면 성능을 저하시킬 수 있다. 140 ALTIBASE5 Stored Procedure User’s Manual EXECUTE IMMEDIATE 동적 SQL 형태로 DDL, DCL, DML 및 단일 레코드를 결과로 반환하는 질의를 실행하기 위해 사용한다. 구문 동적 SQL 141 기능 dynamic_string 실행할 질의문을 문자형태로 나타낸다. INTO 절의 내용은 SELECT ... INTO 구문과 마찬가지로, 결과로 가져올 컬럼을 의미한다. USING 절은 실행시에 바인드할 파라미터이다. IN, OUT, IN-OUT 형식으로 지정할 수 있다. 예제 다음은 동적 SQL 에 DML 을 사용하는 예제이다. CREATE PROCEDURE fire_emp(v_emp_id INTEGER) AS BEGIN EXECUTE IMMEDIATE 'DELETE FROM employees WHERE emp_id = ?' USING v_emp_id; END; / CREATE PROCEDURE insert_table ( table_name VARCHAR(10), dept_no NUMBER, dept_name VARCHAR(10), location VARCHAR(10) AS stmt VARCHAR2(20); BEGIN stmt := 'INSERT INTO ' || table_name || ' values (?, ?, ?)'; EXECUTE IMEDIATE stmt USING dept_no, dept_name, location; END; / 해당 질의문을 Direct-Execute 방식으로 실행할 때는 EXECUTE IMMEDIATE dynamic_string 구문을 사용한다. USING 뒤에 나오는 변수는 바인드 파라미터가 된다. DDL 및 DCL 의 경우도 DML 과 마찬가지로 EXECUTE IMMEDIATE 를 사용하여 실행할 수 있다. 제약사항 저장 프로시저 내에서 동적 SQL 형태로 사용 가능한 질의문은 다음과 같다. y DML select, insert, update, delete, move, lock table, enqueue 142 ALTIBASE5 Stored Procedure User’s Manual y DDL create, alter, drop y DCL Alter System, Alter session, commit, rollback 동적 SQL 에서 지원하지 않는 구문은 다음과 같다. y 클라이언트에 종속된 구문 y select * from tab; y desc. y set timing. y set autocommit y connect y disconnect y dequeue 구문 동적 SQL 143 OPEN FOR OPEN FOR 문으로 결과집합을 반환하기 위한 커서변수(REF CURSOR)를 열 수 있다. USING 구문을 이용하여 파라미터를 바인딩 가능하다. 구문 cursor_variable_nameOPEN IN USINGvariable_name , FORdynamic_string 기능 cursor_variable_name REF CURSOR 형으로 정의된 커서 변수의 이름이다. dynamic_string dynamic_string 은 실행될 질의문이다. 여기에는 문자형으로 기술된 SELECT 구문 만이 올 수 있다. USING 절은 바인드할 파라미터이다. 예제 다음은 여러 행을 가져오는 동적 SQL 문의 실행 결과를 클라이언트로 전달할 수 있도록, 프로시저 내부에서 커서변수를 여는 예제이다. 144 ALTIBASE5 Stored Procedure User’s Manual 열린 커서변수를 통하여 결과집합을 FETCH 하는 방법은 Precompiler User's Manual, ODBC User's Manual, API User's Manual 매뉴얼을 참고한다. <예제> 여러 행을 가져오는 동적 SQL CREATE OR REPLACE PROCEDURE fetch_employee AS TYPE MY_CUR IS REF CURSOR; emp_cv MY_CUR; emp_rec employees%ROWTYPE; stmt VARCHAR2(200); v_job VARCHAR2(10) := 'WEBMASTER'; BEGIN stmt := 'SELECT * FROM employees WHERE job_id = ?'; OPEN emp_cv FOR stmt USING v_job; LOP FETCH emp_cv INTO emp_rec; EXIT WHEN emp_cv%NOTFOUND; PRINTLN('[Name]: ' || emp_rec.name || ' [Job Id]: ' | emp_rec.job_id); END LOOP; CLOSE emp_cv; END; / 예외 처리 145 9. 예외 처리 146 Stored Procedure User’s Manual 예외처리 개요 저장 프로시저 수행중에 예외 사항과 오류 발생시 처리 구문을 예외처리부에 지정하여 제어할 수 있다. 예외처리의 종류 저장 프로시저가 지원하는 예외에는 알티베이스가 미리 정의해 둔 시스템정의 EXCEPTION(System-defined Exception)과 사용자가 임의로 정의하여 사용할 수 있는 사용자정의 EXCEPTION(User- defined Exception)이 있다. 시스템 EXCEPTION 은 저장 프로시저 실행 도중 발생할 수 있는 일반적인 오류들로 그 이름이 미리 정해져 있어 DECLARE 부분에 이름을 선언할 필요가 없다. 시스템 정의 예외처리 Exception Name 발생 사유 CURSOR_ALREADY_OPEN 이미 OPEN되어 있는 커서를 CLOSE하지 않고 다시 OPEN하려 하는 경우 발생한다. CURSOR FOR LOOP의 경우 내부에서 묵시적으로 OPEN되므로 LOOP내에서 OPEN문을 사용하여 명시적으로 OPEN할 수 없다. DUP_VAL_ON_INDEX Unique Index가 정의된 칼럼에 중복된 값을 입력하려 하는 경우 발생한다. INVALID_CURSOR OPEN되지 않은 커서를 FETCH, CLOSE하려는 경우 등과 같이 현재 커서 상태에서 수행할 수 없는 작업을 수행하려 하는 경우에 발생한다. INVALID_NUMBER 숫자 형으로 변환할 수 없는 데이터를 숫자 형으로 변환하려 하는 경우 발생한다. NO_DATA_FOUND SELECT된 데이터가 한 건도 없을 때 발생한다. PROGRAM_ERROR stored procedure internal error STROAGE_ERROR out of memory TIMEOUT_ON_RESOURCE 메모리나 CPU등의 시스템자원할당을 기다리다가 예외 처리 147 타임아웃이 되는 경우 발생한다. TOO_MANY_ROWS SELECT INTO문은 하나의 레코드만 반환해야 하는데 둘 이상의 레코드가 반환된 경우 발생한다. VALUE_ERROR arithmetic, conversion, truncation, or size-constraint등 표현식 연산 시 오류가 발생한 경우이다. ZERO_DIVIDE 나누기 연산에서 0으로 나누는 경우 발생한다. 사용자 정의 예외처리 사용자가 명시적으로 정의하여 사용하는 EXCEPTION 으로 RAISE 문을 사용해 발생시키는 EXCEPTION 이다. DECLARE com_mising EXCEPTION; -- DECLARE user defined EXCEPTION BEGIN ...... RAISE comm_missing; -- raising EXCEPTION ...... EXCEPTION WHEN comm_missing THEN ...... 시스템정의 EXCEPTION 과 같은 이름을 사용자정의 EXCEPTION 으로 정의하여 사용할 경우 Exception Handler 내에서는 사용자정의 EXCEPTION 으로 간주된다. Declare Exception 시스템정의 EXCEPTION 의 경우 시스템 내부에 EXCEPTION 이름이 정의되어 있으므로 명시적으로 선언할 필요는 없다. 사용자정의 EXCEPTION 의 경우 선언부에 명시적으로 그 이름을 정의해 사용해야 한다. Raise Exception 시스템정의 EXCEPTION 의 경우 수행 도중 오류가 발생하면 시스템정의 EXCEPTION 인지 검사하고 해당 Exception Handler 가 존재할 경우 자동으로 분기해 Exception Handler 에 정의된 작업을 처리하며, 사용자정의 Exception 의 경우 RAISE 문을 사용해 EXCEPTION 을 발생시킬 수 있다. 148 Stored Procedure User’s Manual Exception Handler 사용자정의 EXCEPTION 및 시스템정의 EXCEPTION 이 발생할 경우 처리할 작업들을 정의한다. 예외 처리 149 DECLARE EXCEPTION 사용자정의 EXCEPTION 을 정의한다. 구문 exception_declaration := exception_nam eEXCEPTION; 기능 Exception Name 한 블록 내에서 유일한 이름이어야 하며 자신이 선언된 BLOCK 문의 BEGIN-END 범위내에서 유효하다. 예제 DECLARE error_1 EXCEPTION; error_2 EXCEPTION; error_3 EXCEPTION; 150 Stored Procedure User’s Manual RAISE EXCEPTION 을 발생시켜서 해당 EXCEPTION 을 처리하는 핸들러의 루틴이 수행되도록 한다. 만약 해당 Exception Handler 가 존재하지 않을 경우에는 현재 상태에서 저장 프로시저의 수행을 중단하고 오류를 돌려 준다. 구문 기능 exception name 발생시키고자 하는 EXCEPTION 명을 기술한다. 이 EXCEPTION 이름은 블록 선언부에 선언되어 있는 EXCEPTION 이름이거나 시스템정의 EXCEPTION 이름이어야 한다. 사용자정의 EXCEPTION 의 경우 외부 블록과 내부 블록에 같은 EXCEPTION 이름이 정의되어 있는 경우 모호함을 없애기 위해서 EXCEPTION 이름 앞에 LABEL 명을 붙여 사용할 수도 있다. EXCEPTION 명을 기술하지 않아도 되는 경우는 Exception Hander 안에서만 사용되는 경우로서 이때는 이미 발생한 EXCEPTION 을 다시 발생하게 한다. Exception Hander 외부에서 사용할 때에는 반드시 EXCEPTION 명을 기술해야 한다. 예제 예제 1 다음은 VALUE_ERROR EXCEPTION 을 EXCEPTION HANDLER 에서 처리하고 이를 다시 RAISE 하는 예제이다. 예외 처리 151 CREATE OR REPLACE PROCEDURE PROC1 AS BEGIN RAISE VALUE_ERROR; EXCEPTION WHEN VALUE_ERROR THEN PRINTLN('VALUE ERROR CATCHED. BUT RE-RAISE.'); RAISE; END; / EXEC PROC1; VALUE ERROR CATCHED. BUT RE-RAISE. [ERR-3116F : Value error 0004 : RAISE VALUE_ERROR; ^ ^ ] 예제 2 다음은 예제 1 의 PROC1 에서 발생한 EXCEPTION 을 핸들링하는 예제이다 CREATE OR REPLACE PROCEDURE PROC2 AS BEGIN PROC1; EXCEPTION WHEN OTHERS THEN PRINTLN('EXCEPTION FROM PROC1 CATCHED.'); PRINTLN('SQLCODE : '|SQLCODE); END; / EXEC PROC2; VALUE ERROR CATCHED. BUT RE-RAISE. EXCEPTION FROM PROC1 CATCHED. SQLCODE : 201071 Execute success. 152 Stored Procedure User’s Manual RAISE_APPLICATION_ERROR 사용자 지정 에러코드 및 에러 메시지를 출력할 수 있게 지원하는 시스템 프로시저이다. 에러코드의 범위는 990000 부터 991000 까지 1001 개의 사용자 에러 코드를 지원한다. 구문 RAISE_APPLICATION_ERROR ( errcode INTEGER, errmsg VARCHAR(2047) ); 파라미터 이름 입출력 데이터 타입설명 errcode IN INTEGER 사용자 지정 에러 코드 (990000 ~ 991000 범위까지 사용 가능함. ) errmsg IN VARCHAR 사용자 지정 에러 메시지 텍스트 기능 이 프로시저는 EXCEPTION 을 발생시키면서 사용자가 정의한 에러 코드, 에러메시지를 출력한다. 예제 다음은 사용자 정의 에러를 발생시키는 예제이다. 단, iSQL 에서 에러코드는 Hexadecimal 값으로 표시된다. CREATE OR REPLACE PROCEDURE PROC1 AS BEGIN RAISE_APPLICATION_ERROR( 990000, 'This is my error msg. ' ); END; / iSQL> exec proc1; [ER-F1B30 : This is my error msg. 0004 : RAISE_APPLICATION_ERROR( 90000, 0005 : 'This is my error msg.' ); 예외 처리 153 사용자정의 EXCEPTION 사용자가 직접 RAISE 문을 사용해서 EXCEPTION 을 발생시키는 경우는 다음 2 가지 경우이다. y 사용자가 정의한 EXCEPTION을 처리하는 경우 y 시스템이 정의한 EXCEPTION을 처리하는 경우 사용자정의 EXCEPTION 코드 사용자가 정의한 EXCEPTION 을 처리하는 경우 SQLERRM 을 보면 에러코드는 언제나 201232로 고정되어 있다. CREATE OR REPLACE PROCEDURE PROC1 AS E1 EXCEPTION; BEGIN RAISE E1; EXCEPTION WHEN E1 THEN PRINTLN(SQLCODE); PRINTLN(SQLERRM); END; / iSQL> EXEC PROC1; 201232 User-Defined Exception. Execute success. 만약 이 EXCEPTION 을 사용자 EXCEPTION 으로 처리하지 않는 경우 발생하는 에러는 다음과 같다. 즉 사용자 EXCEPTION 처리되지 않았다는 내용의 메시지다. CREATE OR REPLACE PROCEDURE PROC1 AS E1 EXCEPTION; BEGIN RAISE E1; END; / iSQL> EXEC PROC1; [ERR-31157 : Unhandled exception : E1] 154 Stored Procedure User’s Manual 사용자정의 EXCEPTION 은 다음 하나의 에러 코드로 출력된다. Exception Name Error Code(integer) Error Code(hexadecimal) Error Section 201232 31210 qpERR_ABORT_QSX_USER_ DEFINED_EXCEPTION 시스템 정의 EXCEPTION 코드 시스템정의 EXCEPTION 이 발생하는 경우에는 다음과 같이 시스템에 지정된 에러코드가 출력된다. CREATE OR REPLACE PROCEDURE PROC1 AS BEGIN RAISE NO_DATA_FOUND; EXCEPTION WHEN NO_DATA_FOUND THEN PRINTLN(SQLCODE); PRINTLN(SQLERRM); END; / iSQL> EXEC PROC1; 201066 No data found. 0004 : RAISE NO_DATA_FOUND; Execute success. 다음과 같이 시스템 EXCEPTION 의 경우 별도의 예외처리를 하지 않더라도 지정된 에러코드가 출력되는 것을 볼 수 있다. CREATE OR REPLACE PROCEDURE PROC1 AS BEGIN RAISE NO_DATA_FOUND; END; / iSQL> EXEC PROC1; [ER-3116A : No data found. 0004 : RAISE NO_DATA_FOUND; 예외 처리 155 참고로 많이 사용되는 시스템 EXCEPTION 코드는 다음과 같다. Exception Name Error Code (integer) Error Code (hexade cimal) Error Section "CURSOR_ALREAD Y_OPEN" 201062 31166 qpERR_ABORT_QSX_CURSOR_ALREAD Y_OPEN "DUP_VAL_ON_IND EX" 201063 31167 qpERR_ABORT_QSX_DUP_VAL_ON_IN DEX "INVALID_CURSOR" 201064 31168 qpERR_ABORT_QSX_INVALID_CURSOR "INVALID_NUMBER 201065 31169 qpERR_ABORT_QSX_INVALID_NUMBE R "NO_DATA_FOUND" 201066 3116A qpERR_ABORT_QSX_NO_DATA_FOUND "PROGRAM_ERROR 201067 3116B qpERR_ABORT_QSX_PROGRAM_ERROR "STORAGE_ERROR" 201068 3116C qpERR_ABORT_QSX_STORAGE_ERROR "TIMEOUT_ON_RES OURCE" 201069 3116D qpERR_ABORT_QSX_TIMEOUT_ON_RE SOURCE "TOO_MANY_ROWS 201070 3116E qpERR_ABORT_QSX_TOO_MANY_ROW S "VALUE_ERROR" 201071 3116F qpERR_ABORT_QSX_VALUE_ERROR "ZERO_DIVIDE" 201072 31170 qpERR_ABORT_QSX_ZERO_DIVIDE "INVALID_PATH" 201237 31215 qpERR_ABORT_QSX_FILE_INVALID_PA TH "INVALID_MODE" 201235 31213 qpERR_ABORT_QSX_INVALID_FILEOPE N_MODE "INVALID_FILEHAN DLE" 201238 31216 qpERR_ABORT_QSX_FILE_INVALID_FI LEHANDLE "INVALID_OPERATI ON" 201239 31217 qpERR_ABORT_QSX_FILE_INVALID_OP ERATION "READ_ERROR" 201242 3121A qpERR_ABORT_QSX_FILE_READ_ERRO R "WRITE_ERROR" 201243 3121B qpERR_ABORT_QSX_FILE_WRITE_ERR OR "ACCESS_DENIED" 201236 31214 qpERR_ABORT_QSX_DIRECTORY_ACCE SS_DENIED "DELETE_FAILED" 201240 31218 qpERR_ABORT_QSX_FILE_DELETE_FAI LED "RENAME_FAILED" 201241 31219 qpERR_ABORT_QSX_FILE_RENAME_FA ILED 156 Stored Procedure User’s Manual SQLCODE 와 SQLERRM SQLCODE, SQLERRM 은 SQL 문 수행 후에 Exception Handler 에서 해당 에러코드와 메시지를 얻어와서 이에 대한 적절한 대응을 하기 위해 사용된다. SQLCODE, SQLERRM 에 에러가 세팅되는 경우는 다음과 같다. y 저장 프로시저 실행 도중 에러가 발생한 경우 y 사용자정의 EXCEPTION이 발생한 경우 y 시스템정의 EXCEPTION이 발생한 경우 y 사용자가 RAISE_APPLICATION_ERROR로 자신이 정의한 에러를 발생시킨 경우 y Exception Handler 내에서 다시 RAISE하는 경우 위와 같은 경우 기존의 SQLCODE 와 SQLERRM 는 새로 발생한 에러코드와 에러메시지로 변경된다. 추가로 Exception Handler 가 정상적으로 동작한 경우에도 SQLCODE 와 SQLERRM 에 새로운 에러가 세팅되는데 이 경우는 EXCEPTION 발생 이전의 SQLCODE 와 SQLERRM 으로 원복하는 것이다. 따라서, 한번 발생한 에러코드 및 에러메시지는 해당 저장 프로시저의 최상위 블록까지 EXCEPTION 처리가 완벽하게 되지 않는 한 끝까지 남아있게 된다. 다음의 예제를 보면, CREATE OR REPLACE PROCEDURE PROC1 AS BEGIN BEGIN RAISE NO_DATA_FOUND; EXCEPTION WHEN OTHERS THEN BEGIN PRINTLN('1SQLCODE : '|SQLCODE); PRINTLN('1SQLERM : '|SQLERM); RAISE VALUE_EROR; EXCEPTION WHEN OTHERS THEN PRINTLN('2SQLCODE : '|SQLCODE); PRINTLN('2SQLERM : '|SQLERM); END; PRINTLN('3SQLCODE : '||SQLCODE); PRINTLN('3SQLERRM : '||SQLERRM); END; PRINTLN('4SQLCODE : '||SQLCODE); PRINTLN('4SQLERRM : '||SQLERRM); 예외 처리 157 END; / 위 예제의 경우 다음과 같은 결과를 출력한다. 1SQLCODE : 201066 1SQLERRM : No data found. 0005 : RAISE NO_DATA_FOUND; 2SQLCODE : 201071 2SQLERRM : Value error 0011 : RAISE VALUE_ERROR; 3SQLCODE : 201066 3SQLERRM : No data found. 0005 : RAISE NO_DATA_FOUND; 4SQLCODE : 0 4SQLERRM : Successfully completed Execute success. 이를 도식화하면 SQLCODE, SQLERRM 의 범위는 다음과 같음을 알 수 있다. 158 Stored Procedure User’s Manual Exception Handler Exception Handler 에는 해당 EXCEPTION 이 발생했을 때의 처리 루틴을 기술한다. 구문 exception_handler ::= WHEN exception_name ORexception_name OTHERS THENstatement 기능 exception name 처리 하고자 하는 시스템정의 EXCEPTION 이나 사용자정의 EXCEPTION 의 이름을 기술한다. EXCEPTION 발생시에 동일한 처리를 하고자 하는 EXCEPTION 들을 OR 로 묶어서 하나의 루틴으로 처리할 수 있다. others 이전에 기술된 모든 예외처리부가 현재 발생한 EXCEPTION 을 처리하지 못할 경우 최종적으로 others 구문의 루틴이 처리된다. Exception Handler 를 찾아내는 규칙은 다음과 같다. y 현재 블록부터 시작하여 현재 블록을 포함하고 있는 바깥 블록들로 EXCEPTION 이름이 같은 Exception Handler를 찾는다. 도중에 어느 한 블록에서라도 OTHERS 핸들러를 만나게 되면 예외처리가 OTHERS 핸들러에서 이루어진다. 예외 처리 159 y 맨 바깥블록까지 Exception Handler가 발견되지 않는다면, 사용자에게 “Unhandled Exception” 에러를 출력하고 프로시저의 수행은 즉시 중지된다. 예제 예제 1 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE TABLE t2(i1 INTEGER, i2 INTEGER, i3 INTEGER); INSERT INTO t1 VALUES(1,1,1); INSERT INTO t1 VALUES(2,2,2); CREATE OR REPLACE PROCEDURE proc1 AS BEGIN DECLARE CURSOR c1 IS SELECT * FROM t1; v1 INTEGER; v2 INTEGER; v3 INTEGER; BEGIN - OPEN c1; FETCH c1 INTO v1, v2, v3; INSERT INTO t2 VALUES (v1, v2, v3); CLOSE c1; EXCEPTION WHEN INVALID_CURSOR THEN INSERT INTO t2 VALUES (-999, -999, -999); END; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t2; T2.I1 T2.I2 T2.I3 ------------------------------- -9 -9 -9 1 row selected. 예제 2 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE OR REPLACE PROCEDURE proc1(p1 IN INTEGER) AS v1 INTEGER; er1 EXCEPTION; BEGIN IF p1 < 0 THEN 160 Stored Procedure User’s Manual RAISE er1; END IF; SELECT i1 INTO v1 FROM t1; EXCEPTION WHEN NO_DATA_FOUND OR TOO_MANY_ROWS THEN INSERT INTO t1 VALUES(1,1,1); WHEN OTHERS THEN INSERT INTO t1 VALUES(0,0,0); END; / iSQL> EXEC proc1(1); Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ------------------------------- 1 1 1 1 row selected. iSQL> EXEC proc1(-8); Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ------------------------------- 1 1 1 0 0 0 2 rows selected. 예제 3 CREATE TABLE t1(i1 INTEGER NOT NULL); CREATE OR REPLACE PROCEDURE proc1 AS code INTEGER; erm VARCHAR(200); BEGIN INSERT INTO t1 VALUES(NULL); EXCEPTION WHEN OTHERS THEN -- 변수 code 에 SQLCODE 에러코드 값이 저장 code := SQLCODE; -- 변수 errm 에 SQLERRM 에러 메시지가 저장 errm := SUBSTRING(SQLERM, 1, 20); system_.println('SQLCODE : ' | code); system_.println('SQLERRM : ' | errm); END; / iSQL> EXEC proc1; SQLCODE : 822558860 SQLERM : Unable to insert or update the NULL value to a NOT NULL column 0006 : INSERT INTO T1 VALUES(NULL); 예외 처리 161 Execute success. 파일 제어 163 10. 파일 제어 저장 프로시저의 파일 제어 기능은 운영 체제의 텍스트 파일에 대한 읽기 및 쓰기 기능을 제공한다. 이 기능을 이용하여 사용자는 저장 프로시저 실행에 대한 별도의 메시지 등을 파일에 남길 수도 있으며, 파일로 결과를 출력 하거나 파일로부터 데이터를 읽어와 테이블에 삽입하는 등 다양한 작업을 수행할 수 있다. 이 장은 이러한 저장 프로시저 파일 제어 기능에 대해서 설명한다. 164 Stored Procedure User’s Manual 디렉토리 관리 저장 프로시저에서 파일들을 생성하고 제어하기 위해서 이들 파일들이 저장될 디렉토리는 데이터베이스 객체로서 DML 문으로 생성, 관리된다. 디렉토리 생성 저장 프로시저 파일 제어 기능에서 사용하는 파일들을 저장할 디렉토리들은 CREATE DIRECTORY 문을 사용하여 데이터베이스 객체로 생성한다. CREATE DIRECTORY 문을 수행하면 SYS_DIRECTORIES_ 메타 테이블에 디렉토리 정보가 등록되며, 실제 운영 체제의 파일 시스템에 디렉토리가 생성되지는 않는다. 따라서 사용자는 실제 파일 시스템에 디렉토리를 생성하는 작업을 따로 해야 한다. 사용자는 CREATE DIRECTORY 문에 데이터베이스가 참조할 논리적인 디렉토리명과 실제 파일 시스템 상의 해당 절대 경로를 명시해야 한다. 예를 들어 다음과 같이 /home/알티베이스/알티베이스_home/psm_msg 디렉토리 밑에 alti_dir1 디렉토리를 생성하고 데이터베이스내에서는 alti_dir1 라는 이름으로 디렉토리내의 파일들을 제어할 수 있다. iSQL> create directory alti_dir1 as '/home/altibase/altibase_home/psm_msg'; Create success. 디렉토리 변경 CREATE OR REPLACE DIRECTORY 문을 사용해 이미 생성한 디렉토리의 절대 경로를 다음과 같이 변경할 수 있다. iSQL> create or replace directory alti_dir1 as '/home/altibase/altibase_home/psm_result'; Create success. 위의 예제에서 alti_dir1 디렉토리가 이미 데이터베이스에 존재할 경우에는 사용자가 명시한 절대 경로 정보를 변경하며, alti_dir1 디렉토리가 존재하지 않을 경우에는 새로운 객체로 데이터베이스에 생성한다. CREATE [OR REPLACE] DIRECTORY 문에 대한 자세한 설명은 SQL User’s Manual을 참조한다. 파일 제어 165 디렉토리 삭제 CREATE DIRECTORY 문으로 생성한 데이터베이스 객체인 디렉토리는 DROP DIRECTORY 문을 사용해서 데이터베이스에서 삭제할 수 있다. DROP DIRECTORY 문을 사용해 디렉토리를 삭제하는 경우 데이터베이스에서 관리하는 오브젝트만 삭제되며 실제 파일 시스템 상의 디렉토리가 제거되는 것은 아니다. 따라서 사용자는 파일 시스템 상에 존재하는 불필요한 디렉토리와 파일들은 운영 체제 명령어를 이용해 직접 제거해야 한다. 다음은 DROP DIRECTORY 문을 사용해 데이터베이스에서 디렉토리를 삭제하는 예제이다. iSQL> DROP DIRECTORY alti_dir1; Drop success. DROP DIRECTORY 문에 대한 자세한 설명은 SQL User’s Manual 을 참조한다. 166 Stored Procedure User’s Manual 파일 제어 파일 타입 저장 프로시저 내에서 파일 제어를 위해서 FILE_TYPE 이라는 데이터 타입을 지원한다. FILE_TYPE 은 내부적으로 파일 식별자 및 기타 정보를 가지고 있으며 사용자가 직접 내부 데이터에 접근해서 정보를 볼 수는 없다. 저장 프로시저 내에서 FILE_TYPE 을 데이터 타입으로 가지는 지역변수들은 파일 제어 관련 시스템 저장 프로시저 및 저장 함수들의 파라미터로 사용된다. 예제 FILE_TYPE 으로 변수를 선언하는 예제는 다음과 같다. CREATE OR REPLACE PROCEDURE WRITE_T1 AS V1 FILE_TYPE; ID INTEGER; N AME V ARCHA R ( 40) ; BEGIN …… END; / 시스템 프로시저와 함수 알티베이스는 저장 프로시저 및 저장 함수 내에서 파일 제어와 관련해서 다음과 같은 시스템 프로시저와 함수를 제공한다. 시스템 프로시저/함수명 설명 FCLOSE 파일을 닫는다. FCLOSE_ALL 현재 세션에 열려있는 모든 파일을 닫는다. FCOPY 파일을 복사한다. FFLUSH 파일에 데이터를 물리적으로 기록한다. FOPEN 읽기 또는 쓰기 목적으로 파일을 오픈한다. FREMOVE 파일을 삭제한다. FRENAME 파일명을 변경한다. GET_LINE 파일에서 한 라인을 읽는다. IS_OPEN 파일이 열려있는지 검사한다. 파일 제어 167 NEW_LINE 개행문자를 출력한다. PUT 문자열을 파일에 기록한다. PUT_LINE 한 라인을 기록 (PUT+NEW_LINE)한다. 위 표의 시스템 프로시저와 함수들은 CREATE DATABASE 시 시스템 내에서 자동 생성되는 저장 프로시저 및 저장 함수로 PUBLIC 시노님으로 정의되어 있어 임의의 사용자가 이들을 이용해 저장 프로시저 내에서 파일을 제어 할 수 있다. 이러한 시스템 프로시저 및 함수를 사용한 파일 제어 작업은 다음 그림과 같이 표현된다. 주의사항 다음은 저장 프로시저 실행 시 오류를 발생시킬 수 있는 사항들이므로 주의해야 한다. 디렉토리 이름 파일제어 함수 사용시 디렉토리 파라미터는 반드시 CREATE DIRECTORY 문으로 생성한 이름을 사용하돼 반드시 대문자로 표기한다. 168 Stored Procedure User’s Manual 예를 들어, CREATE DIRECTORY alti_dir AS ‘…’; 이와 같이 디렉토리 객체를 생성하였다면 저장 프로시저 내에서는 다음과 같이 사용해야 한다. file = FOPEN( ‘ALTI_DIR’, ‘a.txt’, ‘r’ ); 디렉토리 생성 시 소문자로 디렉토리 이름을 명시하여도 데이터베이스 내 객체 이름은 모두 대문자로 저장되기 때문에 시스템 프로시저 및 함수의 파라미터로 디렉토리 이름을 입력할 때는 대문자를 사용해야 한다. 파일 오픈 모드 파일 오픈 모드는 반드시 소문자( ‘r’, ‘w’, ‘a’ )여야 한다. 예를 들어 다음과 같이 사용해야 한다. file = FOPEN( ‘ALTI_DIR’, ‘a.txt’, ‘r’ ); 한 라인의 문자열 길이 파일 내 한 라인의 최대 문자열 길이는 32767 bytes 를 넘을 수 없다. 만약 최대 길이를 초과할 경우 오류가 발생한다. 파일 데이터 타입 FILE_TYPE 은 사용자가 임의로 변수값을 대입하거나 정보를 읽을 수 없으며, 시스템 프로시저 및 함수의 파라미터로만 사용할 수 있다. 파일 제어 관련 시스템 프로시저 및 함수 파일 제어 관련 시스템 프로시저 및 함수들은 기본적인 시스템 EXCEPTION 외에 다른 EXCEPTION 들을 발생시킬 수 있다. 예를 들면 디스크 및 열 수 있는 파일 핸들이 모자라는 경우나 운영체제 상에서 오류가 발생할 경우 INVALID OPERATION 등의 예기치 않은 오류를 발생시킨다. 파일 제어 관련 시스템 프로시저 및 함수들은 인자를 잘못 넘겨 받은 경우 VALUE_ERROR EXCEPTION 을 발생시킨다. 파일 제어 169 FCLOSE 열려 있는 파일 핸들을 닫고 초기화 하는 기능을 제공하는 저장 프로시저다. 구문 FCLOSE ( file IN OUT FILE_TYPE ); 파라미터 이름 입출력 데이터 타입 설명 file IN OUT FILE_TYPE 파일 핸들 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 처리 수행 시 오류 발생 없이 항상 성공한다. 즉, 이미 닫힌 파일 핸들에 대해 수행해도 오류없이 성공한다. 예제 FOPEN 을 수행 후에는 FCLOSE 를 호출하여 열린 파일 핸들을 다음과 같이 닫아 주어야 한다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 FILE_TYPE; V2 VARCHAR(1024); BEGIN V1 := FOPEN( 'ALTI_DIR', 'schema.sql', 'r' ); GET_LINE( V1, V2, 100 ); PRINTLN(V2); FCLOSE(V1); END; / 170 Stored Procedure User’s Manual FCLOSE_ALL 현재 세션에 열려있는 모든 파일 핸들을 닫는 기능을 제공하는 저장 프로시저다. 저장 프로시저 호출 후 파일이 닫혀 있지 않은 경우가 발생할 수 있으므로, 주로 예외 처리 시에 사용한다. 구문 FCLOSE_AL; 파라미터 파라미터가 없다. 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 처리 수행 시 오류를 발생시키지 않으며 항상 성공한다. 예제 다음은 예외 처리 시 열려 있는 모든 파일 핸들을 닫는 에제이다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 FILE_TYPE; V2 VARCHAR(1024); BEGIN V1 := FOPEN( 'ALTI_DIR', 'schema.sql', 'r' ); GET_LINE( V1, V2, 100 ); PRINTLN(V2); FCLOSE(V1); EXCEPTION WHEN READ_ERROR THEN PRINTLN('READ ERROR!!!'); FCLOSE_AL; END; / 파일 제어 171 FCOPY 파일을 라인 단위로 복사하는 기능을 제공하는 저장 프로시저이다. 결과 파일이 해당 디렉토리 내에 존재하지 않을 경우에는 새로운 파일을 생성하여 소스 파일 내용을 복사하고, 이미 결과 파일이 존재하는 경우에는 오류없이 그대로 내용을 복사한다. 구문 FCOPY ( location IN VARCHAR, filename IN VARCHAR, dest_dir IN VARCHAR, dest_file IN VARCHAR, start_line IN INTEGER DEFAULT 1, end_line IN INTEGER DEFAULT NULL); 파라미터 이름 입출력 데이터 타입 설명 location IN VARCHAR 소스 파일의 디렉토리 경로 filename IN VARCHAR 소스 파일의 이름 dest_dir IN VARCHAR 결과 파일의 디렉토리 경로 dest_file IN VARCHAR 결과 파일의 이름 start_line IN INTEGER 시작 라인 번호 기본값: 1 end_line IN INTEGER 마지막 라인 번호 NULL로 주게 되면 파일의 끝까지 복사 기본값 NULL 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 처리 INVALID_PATH 172 Stored Procedure User’s Manual ACCESS_DENIED INVALID_OPERATION READ_ERROR WRITE_ERROR 예외 처리에 대한 자세한 설명은 이 장의 ‘파일 제어의 예외 처리’ 절을 참조한다. 예제 다음은 a.txt 의 파일을 모두 b.txt 에 복사하는 에제이다. iSQL> EXEC FCOPY( 'ALTI_DIR', 'a.txt', 'ALTI_DIR', 'b.txt' ); Execute success. $ cat a.txt 1-ABCDEFG 2-ABCDEFG 3-ABCDEFG 4-ABCDEFG 5-ABCDEFG 6-ABCDEFG 7-ABCDEFG 8-ABCDEFG 9-ABCDEFG 10-ABCDEFG $ cat b.txt 1-ABCDEFG 2-ABCDEFG 3-ABCDEFG 4-ABCDEFG 5-ABCDEFG 6-ABCDEFG 7-ABCDEFG 8-ABCDEFG 9-ABCDEFG 10-ABCDEFG 다음은 특정 라인 위치만을 복사하는 예제이다. iSQL> EXEC FCOPY( 'ALTI_DIR', 'a.txt', 'ALTI_DIR2', 'b.txt', 4, 9 ); Execute success. $ cat a.txt 1-ABCDEFG 2-ABCDEFG 3-ABCDEFG 4-ABCDEFG 5-ABCDEFG 6-ABCDEFG 7-ABCDEFG 8-ABCDEFG 9-ABCDEFG 파일 제어 173 10-ABCDEFG $ cat b.txt 4-ABCDEFG 5-ABCDEFG 6-ABCDEFG 7-ABCDEFG 8-ABCDEFG 9-ABCDEFG 174 Stored Procedure User’s Manual FFLUSH 파일에 물리적으로 기록하는 기능을 제공하는 저장 프로시저다. 구문 FFLUSH ( file IN FILE_TYPE ); 파라미터 이름 입출력 데이터 타입 설명 file IN FILE_TYPE 파일 핸들 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 처리 INVALID_FILEHANDLE WRITE_ERROR 예외 처리에 대한 자세한 설명은 이 장의 ‘파일 제어의 예외 처리’ 절을 참조한다. 예제 다음은 T1 테이블 I1 칼럼의 데이터를 파일에 모두 기록하는 예제로 PUT_LINE 의 flush 옵션인 마지막 인자에 FALSE 를 넘겨 PUT_LINE 호출 때마다 flush 하지 않고 마지막에 한번 FFLUSH 저장 프로시저를 호출해 flush 하는 예제이다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 FILE_TYPE; R2 T1%ROWTYPE; CURSOR C1 IS SELECT I1 FROM T1; BEGIN V1 := FOPEN( 'ALTI_DIR', 'a.txt', 'w' ); FOR R2 IN C1 LOP PUT_LINE( V1, R2.I1, FALSE ); 파일 제어 175 END LOP; FLUSH(V1); FCLOSE(V1); EXCEPTION WHEN INVALID_PATH THEN PRINTLN('CANNOT OPEN FILE.'); WHEN NO_DATA_FOUND THEN PRINTLN('NO DATA FOUND.'); FCLOSE( V1 ); END; / 176 Stored Procedure User’s Manual FOPEN 파일을 열고 파일 핸들을 반환하는 기능을 제공하는 저장 함수이다. 구문 FILE_TYPE variable := FOPEN ( location IN VARCHAR, filename IN VARCHAR, open_mode IN VARCHAR ); 파라미터 이름 입출력 데이터 타입 설명 location IN VARCHAR 파일의 디렉토리 경로 filename IN VARCHAR 파일의 이름 open_mode IN VARCHAR 입력 가능 옵션은 다음 세가지이다. r : 읽기 w : 쓰기 a : 이어쓰기 주의 사항 : rw, wa와 같이 조합해서 사용 불가하며 반드시 소문자로 쓴다. 결과값 성공적으로 수행할 경우 데이터 타입이 FILE_TYPE 인 파일 핸들을 반환한다. 예외 처리 FOPEN 에서 정의된 EXCEPTION 은 다음과 같다. INVALID_PATH ACCESS_DENIED INVALID_OPERATION INVALID_MODE 파일 제어 177 예외 처리에 대한 자세한 설명은 이 장의 ‘파일 제어의 예외 처리’ 절을 참조한다. 예제 파일을 읽거나 쓰기 위해서는 우선 FOPEN 을 사용해 다음과 같이 파일을 연 후에 사용할 수 있다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 FILE_TYPE; V2 VARCHAR(1024); BEGIN V1 := FOPEN( 'ALTI_DIR', 'schema.sql', 'r' ); GET_LINE( V1, V2, 100 ); PRINTLN(V2); FCLOSE(V1); END; / 178 Stored Procedure User’s Manual FREMOVE 해당 파일을 삭제하는 기능을 제공하는 저장 프로시저다. 구문 FREMOVE ( location IN VARCHAR, filename IN VARCHAR); 파라미터 이름 입출력 데이터 타입 설명 location IN VARCHAR 파일의 디렉토리 경로 filename IN VARCHAR 파일의 이름 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 처리 FREMOVE 에 정의된 EXCEPTION 은 다음과 같다. INVALID_PATH ACCESS_DENIED DELETE_FAILED 예외 처리에 대한 자세한 설명은 이 장의 ‘파일 제어의 예외 처리’ 절을 참조한다. 예제 다음은 파일을 삭제하는 예제이다. --## 현재 디렉토리 정보 $ ls a.sql a.txt b.txt schema.sql --## FREMOVE 실행 파일 제어 179 iSQL> EXEC FREMOVE('ALTI_DIR','b.txt'); Execute success. --# 저장 프로시저 수행 후 디렉토리 정보 $ ls a.sql a.txt schema.sql 180 Stored Procedure User’s Manual FRENAME Unix 시스템의 mv 명령어와 동일한 기능을 가지며, file 의 이름을 바꾸거나, 다른 위치로 옮기는 기능을 제공하는 저장 프로시저다. 구문 FRENAME ( location IN VARCHAR, filename IN VARCHAR, dest_dir IN VARCHAR, dest_file IN VARCHAR, overwrite IN BOLEAN DEFAULT FALSE ); 파라미터 이름 입출력 데이터 타입 설명 location IN VARCHAR 소스 파일의 디렉토리 경로 filename IN VARCHAR 소스 파일의 이름 dest_dir IN VARCHAR 결과 파일의 디렉토리 경로 dest_file IN VARCHAR 결과 파일의 이름 overwrite IN BOOLEAN 이미 파일이 존재하는 경우 덮어 쓸지 여부를 나타내는 파라미터로 TRUE이면 기존 파일을 새로운 파일로 덮어 쓰고, FALSE이면 덮어 쓰지 않는다. 기본값 : FALSE 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 처리 FRENAME 에서 발생 가능한 EXCEPTION 은 다음과 같다. INVALID_PATH ACCESS_DENIED RENAME_FAILED 예외 처리에 대한 자세한 설명은 이 장의 ‘파일 제어의 예외 처리’ 파일 제어 181 절을 참조한다. 예제 다음은 a.txt 파일을 result.txt 로 이름을 변경하는 예제이다. --## 현재 디렉토리 정보 $ ls a.sql a.txt schema.sql --## FRENAME 수행 iSQL> EXEC FRENAME('ALTI_DIR','a.txt','ALTI_DIR','result.txt',TRUE); Execute success. --# 저장 프로시저 수행 후 디렉토리 정보 $ ls a.sql result.txt schema.sql 182 Stored Procedure User’s Manual GET_LINE 해당 파일에서 한 줄씩 읽어오는 기능을 제공하는 저장 프로시저다. 구문 GET_LINE ( file IN FILE_TYPE, buffer OUT VARCHAR, len IN INTEGER DEFAULT NULL); 파라미터 이름 입출력 데이터 타입 설명 file IN FILE_TYPE 파일 핸들 buffer OUT VARCHAR 파일에서 읽은 한 라인을 저장할 버퍼 len IN INTEGER 파일에서 읽을 한 라인의 최대 bytes 수로 NULL을 입력할 경우 1024 bytes 크기만큼 읽어온다. 기본값 : NULL 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 처리 GET_LINE 에서 발생가능한 EXCEPTION 은 다음과 같다. NO_DATA_FOUND READ_ERROR INVALID_FILEHANDLE 예외 처리에 대한 자세한 설명은 이 장의 ‘파일 제어의 예외 처리’ 절을 참조한다. 파일 제어 183 예제 다음은 파일의 한 라인에서 100 bytes 를 읽어 출력하는 예제이다. iSQL> CREATE OR REPLACE PROCEDURE PROC1 2 AS 3 V1 FILE_TYPE; 4 V2 VARCHAR(1024); 5 BEGIN 6 V1 := FOPEN( 'ALTI_DIR', 'schema.sql', 'r' ); 7 GET_LINE( V1, V2, 100 ); 8 PRINTLN(V2); 9 FCLOSE(V1); 10 END; 1 / Create success. iSQL> EXEC PROC1; create table t1 (i1 integer, i2 integer, i3 integer); Execute success. 184 Stored Procedure User’s Manual IS_OPEN 파일이 열려 있는지 여부를 검사하는 기능을 제공하는 저장 함수다. 구문 BOOLEAN variable := IS_OPEN ( file IN FILE_TYPE ); 파라미터 이름 입출력 데이터 타입 설명 file IN FILE_TYPE 파일 핸들 결과값 결과값은 BOOLEAN 데이터 타입으로 열려있으면 TRUE, 열려있지 않으면 FALSE 를 반환한다. 예외 처리 파일 핸들이 정상적으로 열려 있는 경우에 TRUE 를 반환하며 그 외의 경우에는 모두 FALSE 를 반환하므로 수행 시 오류가 발생하지 않는다. 예제 다음은 파일 핸들이 열려 있는지 없는지 검사하는 예제이다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 FILE_TYPE; BEGIN IF IS_OPEN(V1) = FALSE THEN PRINTLN('V1 IS NOT OPENED.'); ELSE PRINTLN('V1 IS OPENED.'); END IF; V1 := FOPEN( 'ALTI_DIR', 'a.txt', 'w' ); PRINTLN('FOPEN FUNCTION CALLED.'); IF IS_OPEN(V1) = FALSE THEN PRINTLN('V1 IS NOT OPENED.'); 파일 제어 185 ELSE PRINTLN('V1 IS OPENED.'); END IF; FCLOSE( V1 ); PRINTLN('FCLOSE FUNCTION CALLED.'); IF IS_OPEN(V1) = FALSE THEN PRINTLN('V1 IS NOT OPENED.'); ELSE PRINTLN('V1 IS OPENED.'); END IF; END; / 186 Stored Procedure User’s Manual NEW_LINE 파일에 해당 라인 수만큼 기록하는 기능을 제공하는 저장 프로시저다. 구문 NEW_LINE ( file IN FILE_TYPE, lines IN INTEGER DEFAULT 1 ); 파라미터 이름 입출력 데이터 타입 설명 file IN FILE_TYPE 파일 핸들 lines IN INTEGER 기록할 라인의 수 기본값 : 1 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 처리 NEW_LINE 에서 발생가능한 EXCEPTION 은 다음과 같다. INVALID_FILEHANDLE WRITE_ERROR 예외 처리에 대한 자세한 설명은 이 장의 ‘파일 제어의 예외 처리’ 절을 참조한다. 예제 다음은 파일에 문자열을 기록하는 예제이다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 FILE_TYPE; BEGIN V1 := FOPEN( 'ALTI_DIR', 'a.txt', 'w' ); PUT_LINE( V1, 'REPORT', TRUE ); 파일 제어 187 NEW_LINE( V1, 3 ); PUT_LINE( V1, '---', TRUE ); FCLOSE( V1 ); END; / --## 위의 저장 프로시저 수행 후 a.txt 파일 결과 $ cat a.txt REPORT ------ $ 188 Stored Procedure User’s Manual PUT 파일에 문자열을 기록하는 기능을 제공하는 저장 프로시저다. 구문 PUT ( file IN FILE_TYPE, buffer IN VARCHAR); 파라미터 이름 입출력 데이터 타입 설명 file IN FILE_TYPE 파일 핸들 buffer IN VARCHAR 기록할 문자열을 저장하고 있는 버퍼 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 처리 PUT 에서 정의된 EXCEPTION 은 다음과 같다. INVALID_FILEHANDLE WRITE_ERROR 예외 처리에 대한 자세한 설명은 이 장의 ‘파일 제어의 예외 처리’ 절을 참조한다. 예제 다음은 파일에 문자열을 기록하는 예제이다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 FILE_TYPE; BEGIN V1 := FOPEN( 'ALTI_DIR', 'a.txt', 'w' ); PUT( V1, 'REPORT'); 파일 제어 189 PUT( V1, '-->'); PUT_LINE( V1, 'SUCESS', TRUE ); FCLOSE( V1 ); END; / --## 위의 저장 프로시저 수행 후 a.txt 파일 결과 $ cat a.txt REPORT-->SUCCESS $ 190 Stored Procedure User’s Manual PUT_LINE 파일에 문자열을 포함한 한 라인을 기록하는 기능을 제공하는 저장 프로시저다. 구문 PUT_LINE ( file IN FILE_TYPE, buffer IN VARCHAR, autoflush IN BOOLEAN DEFAULT FALSE); 파라미터 이름 입출력 데이터 타입 설명 file IN FILE_TYPE 파일 핸들 Buffer IN VARCHAR 기록할 문자열을 저장한 버퍼 autoflush IN BOOLEAN 호출할 때마다 flush할지 여부 기본값: FALSE 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 처리 PUT_LINE 에서 정의된 EXCEPTION 은 다음과 같다. INVALID_FILEHANDLE WRITE_ERROR 예외 처리에 대한 자세한 설명은 이 장의 ‘파일 제어의 예외 처리’ 절을 참조한다. 예제 다음은 파일에 문자열을 기록하는 예제이다. CREATE OR REPLACE PROCEDURE PROC1 파일 제어 191 AS V1 FILE_TYPE; BEGIN V1 := FOPEN('ALTI_DIR', 'a.txt', 'w'); PUT_LINE(V1, '1-ABCDEFG'); PUT_LINE(V1, '2-ABCDEFG'); PUT_LINE(V1, '3-ABCDEFG'); PUT_LINE(V1, '4-ABCDEFG'); PUT_LINE(V1, '5-ABCDEFG'); PUT_LINE(V1, '6-ABCDEFG'); PUT_LINE(V1, '7-ABCDEFG'); PUT_LINE(V1, '8-ABCDEFG'); PUT_LINE(V1, '9-ABCDEFG'); PUT_LINE(V1, '10-ABCDEFG'); FCLOSE(V1); END; / 위의 저장 프로시저를 수행한 후 파일 내용은 다음과 같다. $ cat a.txt 1-ABCDEFG 2-ABCDEFG 3-ABCDEFG 4-ABCDEFG 5-ABCDEFG 6-ABCDEFG 7-ABCDEFG 8-ABCDEFG 9-ABCDEFG 10-ABCDEFG 192 Stored Procedure User’s Manual 파일 제어 예외 처리 저장 프로시저에서 파일 제어와 관련해 발생할 수 있는 오류들에 대한 예외 처리 항목들을 설명한다 Exception Name Description INVALID_PATH 해당 디렉토리 객체가 존재하지 않음 (CREATE DIRECTORY문으로 만들어진 디렉토리가 아님) INVALID_MODE OPEN_MODE가 무효함 ( r, w, a모드가 아님) INVALID_FILEHANDLE 파일 핸들이 무효함 (정상적으로 열린 파일이 아님) INVALID_OPERATION 실제 디렉토리 및 파일이 파일 시스템 상에 존재하지 않거나 파일 시스템에 의해 접근 거부됨 READ_ERROR 열기에 성공한 파일이 READ 시 존재하지 않거나, 파일 시스템에 의해 파일이 접근 거부되거나 read모드로 open한 파일이 아닌 경우 WRITE_ERROR 열기에 성공한 파일이 WRITE 시 존재하지 않거나, 파일 시스템에 의해 파일이 접근 거부되거나 write모드로 open한 파일이 아닌 경우 ACCESS_DENIED 디렉토리 접근 권한에 의해 access 거부됨 (GRANT문으로 객체 접근 권한을 부여받아야 함) DELETE_FAILED 실제 파일이 존재하지 않거나, 파일 시스템에 의해 파일이 접근 거부되는 경우 RENAME_FAILED overwrite옵션을 주지 않았는데 이미 바꿀 파일이 존재하거나, 기타 운영 체제 에러가 발생한 경우 파일 관련 저장 프로시저 및 저장 함수 수행 시 오류가 발생할 수 있으며 이러한 오류들에 대해 위에 정의된 예외 처리들에 대해서는 사용자가 명시적인 오류 처리를 수행할 수 있다. 예제 다음은 디렉토리와 파일 이름을 입력으로 받아 파일을 열어 파일의 내용을 조회하는 프로시저다. 파일 오픈 시 잘못된 경로 또는 파일 내에 데이터가 존재하지 않는 빈 파일인 경우 오류가 팔생할 수 있는데 이 때 INVALID_PATH 및 NO_DATA_FOUND 예외 처리를 명시적으로 할 수 있다. -# CREATE VERIFY PROCEDURE 파일 제어 193 CREATE OR REPLACE PROCEDURE PROC2( PATH VARCHAR(40), FILE VARCHAR(40) ) AS V1 FILE_TYPE; V2 VARCHAR(10); BEGIN V1 := FOPEN( PATH, FILE, 'r' ); LOP GET_LINE( V1, V2, 10 ); PRINT( V2 ); END LOP; EXCEPTION WHEN INVALID_PATH THEN PRINTLN('CANNOT OPEN FILE.'); WHEN NO_DATA_FOUND THEN PRINTLN('NO DATA FOUND.'); FCLOSE( V1 ); END; / 194 Stored Procedure User’s Manual 부록 A 195 A. 부록 196 Stored Procedure User’s Manual 저장 프로시저 예제 예제 1 리플리케이션의 생성 스크립트를 출력하기 위한 dumpReplScrip 저장 프로시저를 생성한다. 지역서버의 IP 주소가 192.168.1.12 이고 포트번호가 35524, 원격서버의 IP 주소가 192.168.1.60 이고 포트번호가 25524 인 두 서버간의 EMPLOYEE 테이블과 DEPARTMENT 테이블을 이중화 한다고 가정한다. iSQL> CREATE REPLICATION rep1 WITH '192.168.1.12',35524 FROM SYS.EMPLOYE TO SYS.EMPLOYEE, FROM SYS.DEPARTMENT TO SYS.DEPARTMENT; Create success. iSQL> ALTER REPLICATION rep1 START; Alter success. iSQL> CREATE REPLICATION rep1 WITH '192.168.1.60',25524 FROM SYS.EMPLOYE TO SYS.EMPLOYEE, FROM SYS.DEPARTMENT TO SYS.DEPARTMENT; Create success. iSQL> ALTER REPLICATION rep1 START; Alter success. iSQL> create or replace procedure dumpReplScript (p1 varchar(40)) as cursor c1 is select system_.sys_replications_.replication_name, system_.sys_replications_.host_ip, system_.sys_replications_.port_no, system_.SYS_REPLICATIONS_.ITEM_COUNT from system_.sys_replications_ where system_.sys_replications_.replication_name = UPPER(P1); r_name varchar(40); r_ip varchar(40); r_port varchar(20); r_item_cnt integer; r_local_user_name varchar(40); r_local_table_name varchar(40); r_remote_user_name varchar(40); r_remote_table_name varchar(40); cursor c2 is select system_.SYS_REPL_ITEMS_.LOCAL_USER_NAME, system_.SYS_REPL_ITEMS_.LOCAL_TABLE_NAME, system_.SYS_REPL_ITEMS_.REMOTE_USER_NAME, system_.SYS_REPL_ITEMS_.REMOTE_TABLE_NAME from system_.sys_repl_items_ where system_.SYS_REPL_ITEMS_.replication_name = r_name; begin open c1; SYSTEM_.PRINTLN('------------------------------'); 부록 A 197 SYSTEM_.PRINTLN(''); loop fetch C1 into r_name, r_ip, r_port, r_item_cnt; exit when C1%NOTFOUND; SYSTEM_.PRINT(' CREATE REPLICATION '); SYSTEM_.PRINT(r_name); SYSTEM_.PRINT(' WITH '''); SYSTEM_.PRINT(r_ip); SYSTEM_.PRINT(''','); SYSTEM_.PRINT(r_port); SYSTEM_.PRINTLN(' '); open c2; for i in 1 .. r_item_cnt lop fetch c2 into r_local_user_name, r_local_table_name, r_remote_user_name, r_remote_table_name; SYSTEM_.PRINT(' FROM '); SYSTEM_.PRINT(r_local_user_name); SYSTEM_.PRINT('.'); SYSTEM_.PRINT(r_local_table_name); SYSTEM_.PRINT(' TO '); SYSTEM_.PRINT(r_remote_user_name); SYSTEM_.PRINT('.'); SYSTEM_.PRINT(r_remote_table_name); if i r_item_cnt then SYSTEM_.PRINTLN(','); else SYSTEM_.PRINTLN(';'); end if; end lop; close c2; end loop; close c1; SYSTEM_.PRINTLN(''); SYSTEM_.PRINTLN('------------------------------'); end; / dumpReplScript 저장 프로시저를 실행한 출력 결과이다. iSQL> exec dumpReplScript('rep1'); ------------------------------------------- CREATE REPLICATION REP1 WITH '192.168.1.60',2524 FROM SYS.DEPARTMENT TO SYS.DEPARTMENT, FROM SYS.EMPLOYEE TO SYS.EMPLOYEE; ------------------------------------------- Execute success. 예제 2 리플리케이션의 이름과 정보를 출력하기 위한 showReplications 를 생성한다. create or replace procedure showReplications as cursor c1 is select system_.sys_replications_.replication_name, system_.sys_replications_.host_ip, system_.sys_replications_.port_no, decode(system_.sys_replications_.is_started,1,'Runing',0,'Not Running') 198 Stored Procedure User’s Manual from system_.sys_replications_; r_name varchar(40); r_ip varchar(40); r_port varchar(20); r_status varchar(20); r_local_user_name varchar(40); r_local_table_name varchar(40); r_remote_user_name varchar(40); r_remote_table_name varchar(40); cursor c2 is select system_.SYS_REPL_ITEMS_.LOCAL_USER_NAME, system_.SYS_REPL_ITEMS_.LOCAL_TABLE_NAME, system_.SYS_REPL_ITEMS_.REMOTE_USER_NAME system_.SYS_REPL_ITEMS_.REMOTE_TABLE_NAME from system_.sys_repl_items_ where system_.SYS_REPL_ITEMS_.replication_name = r_name; begin open c1; SYSTEM_.PRINTLN('-----------------------------'); SYSTEM_.PRINTLN(' Replications Infos'); SYSTEM_.PRINTLN('-----------------------------'); SYSTEM_.PRINTLN(' Name Ip Port Status'); SYSTEM_.PRINTLN('-----------------------------'); SYSTEM_.PRINTLN(''); loop fetch C1 into r_name, r_ip, r_port, r_status; exit when C1%NOTFOUND; SYSTEM_.PRINT(' '); SYSTEM_.PRINT(r_name); SYSTEM_.PRINT(' '); SYSTEM_.PRINT(r_ip); SYSTEM_.PRINT(' '); SYSTEM_.PRINT(r_port); SYSTEM_.PRINT(' '); SYSTEM_.PRINTLN(r_status); SYSTEM_.PRINTLN('+++++++++++++++ ++++'); SYSTEM_.PRINTLN(' Local Table Name Remote Table Name'); SYSTEM_.PRINTLN('+++++++++++++++ ++++'); open c2; loop fetch c2 into r_local_user_name, r_local_table_name, r_remote_user_name, r_remote_table_name; exit when C2%NOTFOUND; SYSTEM_.PRINT(' '); SYSTEM_.PRINT(r_local_user_name); SYSTEM_.PRINT('.'); SYSTEM_.PRINT(r_local_table_name); SYSTEM_.PRINT(' '); SYSTEM_.PRINT(r_remote_user_name); SYSTEM_.PRINT('.'); SYSTEM_.PRINTLN(r_remote_table_name); end loop; close c2; end loop; close c1; SYSTEM_.PRINTLN(''); SYSTEM_.PRINTLN('-----------------------------'); 부록 A 199 end; / 다음은 showReplications 저장 프로시저를 이용한 출력 결과이다. iSQL> exec showReplications; ------------------------------------------- Replications Infos ------------------------------------------- Name Ip Port Status ------------------------------------------- REP1 192.168.1.60 2524 Runing +++++++++ +++++++ Local Table Name Remote Table Name +++++++++ +++++++ SYS.DEPARTMENT SYS.DEPARTMENT SYS.EMPLOYE SYS.EMPLOYE ------------------------------------------- Execute success. 예제 3 해당 사용자의 테이블을 출력하기 위한 SHOWTABLES 저장 프로시저를 생성한다. create or replace procedure SHOWTABLES(p1 in varchar(40)) as cursor c1 is select SYSTEM_.SYS_TABLES_.TABLE_NAME from SYSTEM_.SYS_TABLES_ where SYSTEM_.SYS_TABLES_.USER_ID = (select SYSTEM_.SYS_USERS_.USER_ID from SYSTEM_.SYS_USERS_ where SYSTEM_.SYS_USERS_.USER_NAME = upper(p1) AND system_.SYS_TABLES_.TABLE_TYPE = 'T'); v1 CHAR(40); begin open c1; SYSTEM_.PRINTLN('-------------'); SYSTEM_.PRINT(p1); SYSTEM_.PRINTLN(' Table'); SYSTEM_.PRINTLN('-------------'); loop fetch C1 into v1; exit when C1%NOTFOUND; SYSTEM_.PRINT(' '); SYSTEM_.PRINTLN(v1); end loop; SYSTEM_.PRINTLN('-------------'); close c1; end; / 200 Stored Procedure User’s Manual 다음은 showTables 저장 프로시저를 이용한 출력 결과이다. iSQL> exec showTables('SYS'); ---------------- SYS Table ---------------- CUSTOMER GODS DUMY ORDERS EMPLOYE DEPARTMENT ---------------- Execute success. 예제 4 특정 프로시저의 내용을 출력하는 showProcBody 저장 프로시저를 생성한다. create or replace procedure showProcBody(p1 in varchar(40)) as cursor c1 is select system_.sys_proc_parse_.parse from system_.sys_proc_parse_ where system_.sys_proc_parse_.proc_oid = ( select SYSTEM_.sys_procedures_.proc_oid from system_.sys_procedures_ where SYSTEM_.sys_procedures_.proc_name = upper(p1)) order by system_.sys_proc_parse_.seq_no; v1 varchar(4000); begin open c1; SYSTEM_.PRINTLN('---------------------------'); system_.print(p1); SYSTEM_.PRINTLN(' Procedure'); SYSTEM_.PRINTLN('---------------------------'); SYSTEM_.PRINTLN(''); loop fetch C1 into v1; exit when C1%NOTFOUND; SYSTEM_.PRINTLN(v1); end loop; close c1; SYSTEM_.PRINTLN(''); SYSTEM_.PRINTLN('---------------------------'); end; / 다음은 저장 프로시저 텍스트 정보가 존재하는 메타 테이블 조회 결과이다. select system_.sys_proc_parse_.proc_oid, system_.sys_proc_parse_.parse from system_.sys_proc_parse_ where system_.sys_proc_parse_.proc_oid = ( select SYSTEM_.sys_procedures_.proc_oid from system_.sys_procedures_ where SYSTEM_.sys_procedures_.proc_name = upper('proc1')); PROC_OID ----------------- PARSE 부록 A 201 -------------------------------------------- 7695216 create or replace procedure PROC1 (P1 in NUMBER, P2 in VARCHAR(10), P3 in DATE) as begin if P1 > 7695216 0 then insert into T1 values (P1, P2, P3); end if; end 2 rows selected. 다음은 showProcBody 저장 프로시저를 이용한 출력 결과이다. iSQL> exec showProcBody('proc1'); --------------------------- proc1 Procedure --------------------------- create or replace procedure PROC1 (P1 in NUMBER, P2 in VARCHAR(10), P3 in DATE) as begin if P1 > 0 then insert into T1 values (P1, P2, P3); end if; end --------------------------- Execute success. 예제 5 커서 변수를 사용하는 저장 프로시저를 생성한다. 이 프로시저를 실행하여 커서변수를 열고, ODBC 에서 커서변수를 통해 데이터를 읽는다. CREATE OR REPLACE TYPESET MY_TYPE AS TYPE MY_CUR IS REF CURSOR; END; / CREATE OR REPLACE PROCEDURE OPENCURSOR2 ( P1 OUT MY_TYPE.MY_CUR, P2 IN INTEGER ) AS BEGIN OPEN P1 FOR 'SELECT C1 FROM T1 WHERE C1 <= ?' USING P2; END; / iSQL> EXEC OPENCURSOR2(4); C1 --------- 1 202 Stored Procedure User’s Manual 2 3 4 4 rows selected. /* ODBC 프로그램 */ ... SQLINTEGER c1; SQLINTEGER param1; /* allocate Statement handle */ if (SQL_ERROR == SQLAllocStmt(dbc, printf("SQLAlocStmt eror!n"); return SQL_ERROR; } sprintf(query,"EXEC OPENCURSOR2(?)"); if (SQLPrepare(stmt, (SQLCHAR *) query, SQL_NTS)== SQL_ERROR) { printf("ERROR: prepare stmtn"); execute_err(dbc, stmt, query); return SQL_ERROR; } if (SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, NUL) = SQL_ERROR) { printf("ERROR: Bind Parameter 1n"); execute_err(dbc, stmt, query); return SQL_ERROR; } param1 = 4; if (SQLExecute( stmt ) != SQL_SUCCESS) { execute_er(dbc, stmt, query); SQLFreStmt(stmt, SQL_DROP); return SQL_ERROR; } if (SQL_EROR = SQLBindCol(stmt, 1, SQL_C_SLONG, L)) { printf("ERROR: Bind 1 Columnn"); } while ( (rc = SQLFetch(stmt)) != SQL_NO_DATA) { if ( rc != SQL_SUCCES ) { execute_err(dbc, stmt, query); break; } printf(" Result Set : [ %d ] n", c1 ); } SQLFreeStmt(stmt, SQL_DROP); 부록 A 203 .... $ refcursor ============================================ Result Set : [ 1 ] Result Set : [ 2 ] Result Set : [ 3 ] Result Set : [ 4 ] 204 Stored Procedure User’s Manual 파일 제어 예제 다음은 파일에 테이블의 내용을 기록하고 읽어오는 예제이다. 사용자 생성 및 권한 부여 CONNECT SYS/MANAGER; CREATE USER JEJEONG IDENTIFIED BY JEJEONG; GRANT CREATE ANY DIRECTORY TO JEJEONG; GRANT DROP ANY DIRECTORY TO JEJEONG; 테이블, 디렉토리 생성 및 프로시저 생성 CONNECT JEJEONG/JEJEONG; CREATE TABLE T1( ID INTEGER, NAME VARCHAR(40) ); INSERT INTO T1 VALUES( 1, 'JEJEONG'); INSERT INTO T1 VALUES( 2, 'EJPARK' ); INSERT INTO T1 VALUES( 3, 'WSKIM' ); INSERT INTO T1 VALUES( 4, 'KKSHIM' ); INSERT INTO T1 VALUES( 5, 'CSKIM' ); INSERT INTO T1 VALUES( 6, 'KDHONG' ); CREATE DIRECTORY MYDIR AS '/home/JEJEONG'; 테이블 T1 에 있는 레코드를 읽어서 t1.txt 에 기록하는 프로시저 CREATE OR REPLACE PROCEDURE WRITE_T1 AS V1 FILE_TYPE; ID INTEGER; NAME VARCHAR(40); BEGIN DECLARE CURSOR T1_CUR IS SELECT * FROM T1; BEGIN OPEN T1_CUR; V1 := FOPEN( 'MYDIR', 't1.txt', 'w' ); LOOP FETCH T1_CUR INTO ID, NAME; EXIT WHEN T1_CUR%NOTFOUND; PUT_LINE( V1, 'ID : '||ID||' NAME : '||NAME); END LOOP; CLOSE T1_CUR; FCLOSE(V1); END; END; / 파일 t1.txt 에 있는 내용을 읽어 화면에 출력하는 프로시저 CREATE OR REPLACE PROCEDURE READ_T1 AS BUFFER VARCHAR(200); V1 FILE_TYPE; BEGIN V1 := FOPEN('MYDIR', 't1.txt', 'r' ); LOOP GET_LINE( V1, BUFFER, 200 ); PRINT( BUFFER ); 부록 A 205 END LOOP; FCLOSE( V1 ); EXCEPTION WHEN NO_DATA_FOUND THEN FCLOSE( V1 ); END; / 결과 위와 같이 저장 프로시저를 생성하고 실행하면 다음과 같은 결과가 나온다. iSQL> exec write_t1; Execute success. iSQL> exec read_t1; ID : 1 NAME : JEJEONG ID : 2 NAME : EJPARK ID : 3 NAME : WSKIM ID : 4 NAME : KKSHIM ID : 5 NAME : CSKIM ID : 6 NAME : KDHONG Execute success. 시스템 상의 디렉토리 위치로 가서 파일을 확인하면 결과는 다음과 같다. $ cd /home/JEJEONG $ cat t1.txt ID : 1 NAME : JEJEONG ID : 2 NAME : EJPARK ID : 3 NAME : WSKIM ID : 4 NAME : KKSHIM ID : 5 NAME : CSKIM ID : 6 NAME : KDHONG 206 Stored Procedure User’s Manual 스키마 목적 알티베이스 SQL 구문과 기능을 설명한다. 제공 스크립트 파일 스키마 생성파일 $ALTIABSE_HOME/sample/schema.sql 파일로 제공된다. 이 파일은 본 매뉴얼에서 사용된 테이블을 생성하고 예제 데이터를 삽입하는 파일이다. 따라서, 본 매뉴얼에 기술되어있는 예제를 실행하고자 한다면 제공된 파일을 수행 후 예제를 따라해 볼 수 있다. 스키마 기 능 : 고객, 주문관리 테이블 : 사원, 부서, 고객, 주문, 상품 사원(EMPLOYEE) 테이블 기본 키 : 사원번호(eno) 레코드 크기 : 20 개 컬럼명 데이터 타입 설명 기타 eno INTEGER 사원번호 PRIMARY KEY ename CHAR(20) 사원이름 NOT NULL emp_job CHAR(15) 직책 NULL emp_tel NIBBLE(15) 전화번호 NULL dno BYTE(2) 부서번호 NULL, INDEX, ASC salary NUMBER(10,2) 월급 NULL, DEFAULT 0 sex CHAR(1) 성별 NOT NULL, ‘M’ or ‘F’, DEFAULT ‘M’ 부록 A 207 birth BYTE(2) 생일 NULL join_date DATE 입사날짜 NULL status CHAR(1) 지위 DEFAULT ‘H’ 부서(DEPARTMENT) 테이블 기본 키 : 부서번호(dno) 레코드 크기 : 5 개 컬럼명 데이터 타입 설명 기타 dno BYTE(2) 부서번호 PRIMARY KEY dname CHAR(20) 부서명 NOT NULL dep_location CHAR(9) 부서위치 NULL mgr_no INTEGER 관리자번호 NULL, BTREE INDEX, ASC 고객(CUSTOMER) 테이블 기본 키: 주민등록번호(cno) 레코드 크기 : 20 개 컬럼명 데이터 타입 설명 기타 cno CHAR(14) 주민등록번호 PRIMARY KEY cname CHAR(20) 고객이름 NOT NULL cus_job CHAR(20) 직업 NULL cus_tel NIBBLE(15) 전화번호 NOT NULL sex CHAR(1) 성별 NOT NULL, ‘M’ or ‘F’, DEFAULT ‘M’ birth BYTE(2) 생일 NULL post BYTE(3) 우편번호 NULL address CHAR(60) 주소 NULL 주문(ORDERS) 테이블 기본 키 : 주문일자 + 주문번호 (ono, order_date) 레코드 크기 : 30 개 컬럼명 데이터 타입 설명 기타 ono NIBBLE(10) 주문번호 PRIMARY KEY order_date DATE 주문일자 PRIMARY KEY eno INTEGER 판매사원 NOT NULL, 208 Stored Procedure User’s Manual BTREE INDEX, ASC cno CHAR(14) 고객주민번호 NOT NULL, BTREE INDEX, DESC gno BYTE(5) 상품번호 NOT NULL, INDEX, ASC qty INTEGER 주문수량 NULL, DEFAULT 1 arrival_date DATE 도착예정일자 NULL processing CHAR(1) 주문상태 NULL, O: ORDER, R: PREPARE, D: DELIVERY, C: COMPLETE, DEFALT ‘O’ 상품(GOODS) 테이블 기본 키 : 상품번호(gno) 레코드 크기 : 30 개 컬럼명 데이터 타입 설명 기타 gno BYTE(5) 상품번호 PRIMARY KEY gname CHAR(20) 상품이름 NOT NULL, UNIQUE goods_location CHAR(9) 보관위치 NULL stock INTEGER 보관수량 NULL, DEFAULT 0 price NUMERIC(10,2) 원가 NULL DUAL 테이블 레코드 크기 : 1 개 컬럼명 데이터 타입 설명 기타 x CHAR(1) E-R 다이어그램 부록 A 209 DEPARTMENT EMPLOYEE ORDERS CUSTOMERGOODS dno dep_location dnamemgr_no emp_tel salary birth join_date statusdno sex emp_job eno ename cno ono processing qtygno arrival_date order_date birth sex post cname address cus_job cno cus_telgoods_locationprice gno stockgname WORKS_FOR TAKE_ORDERS ORDER_FORORDER_GOODS eno 1 N N NN MM 210 Stored Procedure User’s Manual 샘플 데이터 사원테이블 iSQL> SELECT * FROM employee; EMPLOYE.ENO EMPLOYEE.ENAME EMPLOYEE.EMP_JOB EMPLOYE.EMP_TEL -------------------------------------------- EMPLOYE.DNO EMPLOYEE.SALARY EMPLOYEE.SEX EMPLOYEE.BIRTH -------------------------------------------- EMPLOYE.JOIN_DATE EMPLOYEE.STATUS -------------------------------- 1 SWNO CEO 019562365 M R 2 HJNO DESIGNER 013654540 C002 15000 F 1219 1999/11/18 0:00:00 H 3 HSCHOI ENGINER 0162581369 D001 20000 M 026 2000/01/11 0:00:00 H 4 KSKIM ENGINER 0182563984 D001 18000 M 0730 H 5 SJKIM ENGINEER 01145582310 D001 25000 M 1999/12/20 0:00:00 H 6 HYCHOI PROGRAMER 01978532 A001 1700000 M 082 2000/09/09 0:00:00 H 7 HJMIN MANAGER 01752102 A001 500000 M 0417 2000/01/24 0:00:00 H 8 JDLE MANAGER 01782963 D01 M 0726 1999/11/29 0:00:00 H 9 KMLE PLANER 016529368 C002 12000 M 0102 2000/06/14 0:00:00 H 10 YHBAE PROGRAMER 01674520 A001 4000000 F 0213 2000/01/05 0:00:00 H 1 MSKIM WEBMASTER 01453206 C001 2750000 M 2000/04/28 0:00:00 H 12 MYLE SALESMAN 017456230 F001 1890000 F 0211 1999/12/14 0:00:00 H 13 KWKIM CEO 018763650 C001 9800 M 102 H 14 KCJUNG CEO 019764120 D001 2003000 M H 15 JHSEOUNG WEBMASTER 01956840 부록 A 211 C001 10000 M 0514 H 16 JHCHOI DESIGNER 01956210 C002 23000 F 0509 H 17 DIKIM CEO 016529386 C002 14000 M 1026 2000/05/07 0:00:00 H 18 CHLE PLANER 017523104 C002 19000 M 2000/10/30 0:00:00 H 19 KMKIM SALESMAN 018569850 F001 18000 M 2000/11/18 0:00:00 H 20 DIKIM SALESMAN 01541236 F01 M 2000/11/18 0:00:00 H selected row count [20] 부서테이블 iSQL> SELECT * FROM department; DEPARTMENT.DNO DEPARTMENT.DNAME DEPARTMENT.DEP_LOCATION DEPARTMENT.MGR_NO -------------------------------------------- A01 응용기술팀 마포 1 D01 엔진개발팀 여의도 10 C01 마케팅팀 강남 9 C02 기획관리팀 강남 15 F01 영업팀 신촌 9 5 rows selected. 고객테이블 iSQL> SELECT * FROM customer; CUSTOMER.CNO CUSTOMER.CNAME CUSTOMER.CUS_JOB -------------------------------------------- CUSTOMER.CUS_TEL CUSTOMER.SEX CUSTOMER.BIRTH CUSTOMER.POST -------------------------------------------- CUSTOMER.ADRES -------------------------------------------- 730828-120145 CHLE ENGINER 0514685282 M 0828 601033 부산 동구 수정 3 71215-1345471 YSKIM DOCTOR 023242121 M 1215 121011 서울 마포구 아현 1 71-1431202 DJKIM DESIGNER 023442542 M 1 135010 서울 강남구 논현동 720305-21014 JHPARK ENGINER 022326393 F 0305 121758 서울 마포구 공덕 2 제일빌딩 761012-120475 BSYOUN WEBMASTER 0233452141 M 1012 121021 서울 마포구 공덕 1 690209-1234567 IJLE WEBPD 025743215 M 0209 136751 서울 성북구 돈암 2 한신아파트 73125-24021 JHCHOI PLANER 212 Stored Procedure User’s Manual 0231436 F 125 15672 서울 동작구 사당 2 극동아파트 730801-1015 HYCHOI PD 024721114 M 0801 135747 서울 강남구 신사 성도빌딩 6021-2417214 MYLE DESIGNER 0512543734 F 021 603 부산 중구 광복 3 620815-1724174 KSKIM 0516232256 M 0815 608703 부산 남구 대연 3 부산시차량등록사업소 70101-10101 LSPARK MANAGER 027664545 M 01 142704 서울 강북구 미아 9 성바오로딸수도회 670905-2101013 DHCHO BANKER 02343214 F 0905 152761 서울 구로구 구로 1 구로주공아파트 791230-214547 YDPARK ENGINER 0232019 F 1230 15360 서울 금천구 서울구로우체국사서함 740508-132014 DHKIM BANKER 024720112 M 0508 135740 서울 강남구 삼성 1 강남병원 750625-12143 DKIM MANAGER 0518064398 M 0625 606796 부산 영도구 동삼 2 한국해양대학교 78125-1304 SMCHO PLANER 027544147 M 1225 157703 서울 강서구 화곡 6 강서의료보험조합 76101-101 JHKIM 023543541 M 1001 157717 서울 강서구 등촌 2 국군수도통합병원 740419-2146506 JHKIM ENGINER 024560207 F 0419 138701 서울 송파구 가락 1 가락동농수산물시장 731231-1515123 DJKIM 022371234 M 1231 138742 서울 송파구 신천 서울시교통회관건물 70405-232123 DKHAN WEBMASTER 0245602 F 0405 135757 서울 강남구 삼성 1 종합무역센타빌딩 20 rows selected. 주문테이블 iSQL> SELECT * FROM orders; ORDERS.ONO ORDERS.ORDER_DATE ORDERS.ENO ORDERS.CNO -------------------------------------------- ORDERS.GNO ORDERS.QTY ORDERS.ARIVAL_DATE ORDERS.PROCESSING -------------------------------------------- 0011290007 2000/11/29 00:00:00 12 711111-1431202 A111100002 70 2000/12/02 00:0:00 C 0011290011 2000/11/29 00:00:00 12 761001-10001 E111100001 100 2000/12/05 00:00:00 D 0011290100 2000/11/29 00:00:00 19 700101-1001001 E111100001 500 2000/12/07 00:00:00 D 0012100277 2000/12/10 00:00:00 19 761012-1220475 D111100008 2500 2000/12/12 00:0:00 C 부록 A 213 0012300001 20/12/01 00:00:00 19 730828-1201145 D111100004 1000 2001/01/02 00:0:00 P 0012300002 20/12/29 00:00:00 12 771215-1345471 C11100001 30 2001/01/02 00:0:0 P 0012300003 20/12/29 00:00:00 20 740508-1332014 E111100002 900 2001/01/02 00:00:00 P 0012300004 20/12/30 00:00:00 20 750625-1122143 D111100002 1000 2001/01/02 00:0:00 P 0012300005 20/12/30 00:00:00 19 720305-2101114 D111100008 4000 2001/01/02 00:0:00 P 0012300006 20/12/30 00:00:00 20 791230-2114547 A111100002 20 2001/01/02 00:0:00 P 0012300007 20/12/30 00:00:00 12 731225-2402221 D111100002 2500 2001/01/02 00:0:00 P 0012300008 20/12/30 00:00:00 20 700101-1001001 D111100011 300 2001/01/02 00:0:00 P 0012300009 20/12/30 00:00:00 20 731231-15123 D111100003 500 2001/01/02 00:0:00 P 0012300010 20/12/30 00:00:00 19 781225-1333044 D111100010 2000 2001/01/02 00:0:00 P 0012300011 20/12/30 00:00:00 20 750625-1122143 C11100001 1000 2001/01/02 00:0:0 P 0012300012 20/12/30 00:00:00 12 711111-1431202 E111100012 130 2001/01/02 00:00:00 P 0012300013 20/12/30 00:00:00 20 690209-1234567 C11100001 5000 2001/01/02 00:0:0 P 0012300014 20/12/30 00:00:00 12 670905-21013 F111100001 800 2001/01/02 00:0:0 P 0012310001 20/12/31 00:00:00 20 750625-1122143 A111100002 50 2000/12/09 00:0:00 O 0012310002 20/12/31 00:00:00 12 620815-1724174 D111100008 10000 2001/01/03 00:0:00 O 0012310003 20/12/31 00:00:00 20 740419-2146506 E111100009 150 2001/01/03 00:00:00 O 0012310004 20/12/31 00:00:00 19 761012-1220475 E111100010 500 2001/12/08 00:00:00 O 0012310005 20/12/31 00:00:00 20 740508-1332014 E111100007 940 2001/01/03 00:00:00 O 0012310006 20/12/31 00:00:00 20 771215-1345471 D111100004 500 2001/01/03 00:0:00 O 0012310007 20/12/31 00:00:00 12 731231-15123 E111100012 140 2001/01/03 00:00:00 O 0012310008 20/12/31 00:00:00 19 730828-1201145 D111100003 100 2001/01/03 00:0:00 O 0012310009 20/12/31 00:00:00 12 761012-1220475 E111100013 500 2001/01/03 00:00:00 O 0012310010 20/12/31 00:00:00 20 690209-1234567 D111100010 1500 2001/01/03 00:0:00 O 0012310011 20/12/31 00:00:00 19 750625-1122143 E1110012 10000 2001/01/03 00:0:0 O 0012310012 20/12/31 00:00:00 19 730828-1201145 C11100001 250 2001/01/03 00:0:0 O 30 rows selected. 상품테이블 iSQL> SELECT * FROM goods; GOODS.GNO GOODS.GNAME GOODS.GOODS_LOCATION GOODS.STOCK -------------------------------------------- 214 Stored Procedure User’s Manual GOODS.PRICE --------- A111100001 IM-300 AC0001 1000 780 A111100002 IM-310 D0001 100 980 B101 NT-H50 AC02 780 3580 C1111001 IT-U950 FA0001 35000 7820.55 C102 IT-U20 AC03 10 9455.21 D111100001 TM-H5000 AC0004 7800 120 D111100002 TM-T88 BF001 10000 720 D103 TM-L60 BF02 650 4510 D104 TM-U950 D02 80 9620 D105 TM-U925 AC05 980 230 D106 TM-U375 EB01 120 5740 D111100007 TM-U325 EB0002 20000 8450 D111100008 TM-U200 AC06 61000 10 D109 TM-U30 D03 90 50 D1010 TM-U590 D04 790 3680 D101 TM-U295 FA02 10 4560 E101 M-T245 AC07 90 2290.54 E102 M-150 FD01 430 7527.35 E103 M-180 BF03 10 2300.55 E111100004 M-190G CE0001 88000 5638.76 E111100005 M-U310 CE0002 11200 1450.5 E111100006 M-T153 FD0002 900 2338.62 E107 M-T102 BF04 7890 966.99 E108 M-T50 EB03 50 1000.54 E109 M-T30 FA03 70 3099.88 E1010 M-T260 AC08 40 9200.5 E101 M-780 AC09 980 9832.98 E111100012 M-U420 CE0003 43200 3566.78 E111100013 M-U290 FD0003 12000 부록 A 215 1295.44 F111100001 AU-10 AC0010 10000 100000 30 rows selected. DUAL 테이블 iSQL> SELECT * FROM dual; DUAL.X --------- X selected row count [1] 부록 A 217 찾아보기 ㄷ 동적SQL 제약사항 143 디렉토리 관리 166 ㅂ 블록 바디 38 ㅅ 사용자 정의 타입 114 사용자 정의 타입 호환성 123 사용자 정의 타입의 활용 123 사용자정의EXCEPTION 156 사용자정의EXCEPTION코드 156 선언부 37 스키마 209 시스템정의EXCEPTION코드 157 ㅇ 예외처리 148 ㅈ 저장 프로시저 8 저장 프로시저의 구조 11 저장 프로시저의 특징 8 저장 함수 8 지역 변수 선언 39 ㅌ 타입 세트 132 타입 세트 구조 132 ㅍ 파일 제어 168 파일 제어의 예외 처리 194 A ALTER FUNCTION statement 32 ALTER PROCEDURE statement 23 ASSIGNMENT statement 51 Associative Array 114 ASSOCIATIVE ARRAY TYPE 함수 119 B Block Body 38 C CASE statement 70 CLOSE CURSOR 94 CLOSE CURSOR statement 107 CONTINUE statement 86 CREATE FUNCTION statement 28 CREATE PROCEDURE statement 16 CREATE TYPESET statement 135 CURSOR Attribute 108 CURSOR FOR LOOP 95 CURSOR FOR LOOP statement 104 D DECLARE CURSOR 94 DECLARE CURSOR statement 96 Declare Exception 149 DECLARE EXCEPTION statement 151 Declare Section 37 218 Stored Procedure User’s Manual DECLARE TYPE 116 declaring a local variable 39 DROP FUNCTION statement 34 DROP PROCEDURE statement 25 DROP TYPESET statement 137 E Exception 148 Exception Handler 38, 150, 161 EXECUTE IMMEDIATE statement 142 EXECUTE statement 26 EXIT statement 83 F FCLOSE procedure 171 FCLOSE_ALL procedure 172 FCOPY 173 FETCH 94 FETCH statement 102 FFLUSH procedure 176 FILE_TYPE 168 FOPEN function 178 FOR LOOP statement 78 FREMOVE procedure 180 FRENAME procedure 182 G GET_LINE procedure
-
- [Altibase 5.5.1] Stored Procedure User's Manual_KOR ㅣ 2012-09-27
- CREATE PROCEDURE ····················································································································
-
미리보기
ALTIBASE HDB Application Development Stored Procedures Manual Release 5.5.1 (January 16, 2013) ----------------------------------------------------------- ALTIBASE Application Development Stored Procedures Manual Release 5.5.1 Copyright ⓒ 2001~2011 ALTIBASE Corp. All Rights Reserved. 본 문서의 저작권은 ㈜알티베이스에 있습니다. 이 문서에 대하여 당사의 동의 없이 무단으로 복제 또는 전용할 수 없습니다. ㈜알티베이스 152-790 서울시 구로구 구로동 182-13 대륭포스트타워Ⅱ 10층 전화: 02-2082-1114 팩스: 02-2082-1099 고객서비스포털: http://support.altibase.com homepage: http://www.altibase.com ----------------------------------------------------------- 목차 I 목 차 서문 ················································································································· i 이 매뉴얼에 대하여 ······························································································································ ii 1. 저장 프로시저 ························································································· 1 저장 프로시저의 개요 ·························································································································2 저장 프로시저의 구조 ·························································································································5 저장 프로시저 사용시 주의 사항 ··································································································7 2. 저장 프로시저 SQL문 ············································································ 9 개요 ··························································································································································· 10 CREATE PROCEDURE ························································································································· 12 ALTER PROCEDURE ···························································································································· 20 DROP PROCEDURE····························································································································· 22 EXECUTE ·················································································································································· 23 CREATE FUNCTION ···························································································································· 25 ALTER FUNCTION ······························································································································· 29 DROP FUNCTION ································································································································ 30 3. 저장 프로시저 블록 ·············································································· 31 저장 프로시저 블록 ··························································································································· 32 지역 변수 선언 ···································································································································· 35 SELECT INTO ········································································································································· 43 할당문 ······················································································································································· 49 LABEL ························································································································································ 52 PRINT ························································································································································ 55 RETURN ··················································································································································· 58 4. 흐름 제어································································································ 61 개요 ··························································································································································· 62 II Stored Procedures Manual IF ································································································································································· 63 CASE ························································································································································· 69 LOOP ························································································································································ 73 WHILE LOOP ········································································································································· 75 FOR LOOP ·············································································································································· 77 EXIT ··························································································································································· 83 CONTINUE ············································································································································· 87 GOTO ······················································································································································· 89 NULL ························································································································································· 93 5. 커서 ········································································································· 95 커서의 개요 ··········································································································································· 96 CURSOR ·················································································································································· 98 OPEN ······················································································································································ 101 FETCH ····················································································································································· 104 CLOSE····················································································································································· 107 Cursor FOR LOOP····························································································································· 108 커서 속성 ············································································································································· 111 6. 사용자 정의 타입 ················································································· 117 개요 ························································································································································· 118 사용자 정의 타입의 정의 ············································································································· 120 Associative Array 관련 함수 ······································································································· 123 RECORD 타입 변수 및 Associative Array변수의 사용 ··················································· 128 REF CURSOR ······································································································································· 132 7. 타입 세트 ······························································································ 139 개요 ························································································································································· 140 CREATE TYPESET ······························································································································· 143 DROP TYPESET ··································································································································· 146 8. 동적 SQL ······························································································· 149 동적 SQL의 개요 ······························································································································ 150 EXECUTE IMMEDIATE ····················································································································· 153 OPEN FOR ············································································································································ 156 목차 III 9. 예외 처리······························································································· 159 개요 ························································································································································ 160 EXCEPTION ·········································································································································· 163 RAISE ······················································································································································ 164 RAISE_APPLICATION_ERROR ······································································································· 166 사용자정의 예외 ······························································································································· 168 SQLCODE와 SQLERRM·················································································································· 171 Exception Handler ··························································································································· 174 10. 내장 함수와 저장 프로시저 ································································ 179 파일 제어 ············································································································································· 180 DataPort ··············································································································································· 205 그 외 함수들 ······································································································································ 224 A. 예제 ········································································································ 227 저장 프로시저 예제 ························································································································ 228 파일 제어 예제 ································································································································· 238 찾아보기 ······································································································ 241 서문 i 서문 ii Stored Procedures Manual 이 매뉴얼에 대하여 이 매뉴얼은 저장 프로시저의 개념 및 사용 방법에 대해 설명한다. 대상 사용자 이 매뉴얼은 다음과 같은 알티베이스 사용자를 대상으로 작성되었다. 데이터베이스 관리자 성능 관리자 데이터베이스 사용자 응용 프로그램 개발자 기술지원부 다음과 같은 배경 지식을 가지고 이 매뉴얼을 읽는 것이 좋다. 컴퓨터, 운영 체제 및 운영 체제 유틸리티 운용에 필요한 기본 지식 관계형 데이터베이스 사용 경험 또는 데이터베이스 개념에 대한 이해 컴퓨터 프로그래밍 경험 데이터베이스 서버 관리, 운영 체제 관리 또는 네트워크 관리 경험 소프트웨어 환경 이 매뉴얼은 데이터베이스 서버로 알티베이스 버전 5.5.1을 사용한다는 가정 하에 작성되었다. 이 매뉴얼의 구성 이 매뉴얼은 다음과 같이 구성되어 있다. 제 1장 저장 프로시저 이 장은 저장 프로시저의 개념 및 구조, 사용 시 주의 사항에 대해 설명한다. 제 2장 저장 프로시저 SQL문 이 장은 저장 프로시저 SQL문에 대한 사용 방법에 대해 설명한다. 서문 iii 제 3장 저장 프로시저 블록 이 장은 저장 프로시저 블록의 개념, 저장 프로시저 바디 내에서 선언하는 지역 변수 및 사용가능한 문장에 대해 설명한다. 제 4장 흐름 제어 이 장은 저장 프로시저 바디 내에서 절차적 프로그램 작성이 가능하도록 프로그램 흐름을 제어할 수 있는 흐름 제어문에 대해 설명한다. 제 5장 커서 이 장은 저장 프로시저 내에서 조회 레코드 건수가 여러 개인 SELECT문을 처리할 수 있도록 커서를 정의하고 레코드를 제어할 수 있는 커서 관련문들에 대해 설명한다. 제 6장 사용자 정의 타입 이 장은 저장 프로시저 내에서 사용자 정의 타입인 record 및 associative array의 정의 및 사용 방법에 대해 설명한다. 제 7장 타입 세트 이 장은 사용자 정의 타입의 집합인 타입 세트의 정의 및 사용 방법에 대해 설명한다. 제 8장 동적 SQL 이 장은 실행 시간에 사용자가 원하는 질의를 만들어서 실행하기 위한 동적 SQL에 대해 설명한다. 제 9장 예외 처리 이 장은 저장 프로시저 실행 중 오류 발생 시 저장 프로시저 내에서 오류에 대한 예외 처리가 가능하도록 하는 예외 처리 관련문에 대해 설명한다. 제 10장 파일 제어 이 장은 저장 프로시저의 운영체제 텍스트 파일에 대한 읽기 및 쓰기 기능에 대하여 설명한다. A. 부록 이 장은 이 매뉴얼의 예제에서 사용한 스키마에 대한 설명과 저장 프로시저를 이용한 예제 프로그램을 설명한다. 문서화 규칙 이 절에서는 이 매뉴얼에서 사용하는 규칙에 대해 설명한다. 이 규칙을 이해하면 이 매뉴얼과 설명서 세트의 다른 매뉴얼에서 정보를 쉽게 찾을 수 있다. 여기서 설명하는 규칙은 다음과 같다. 구문 다이어그램 샘플 코드 규칙 iv Stored Procedures Manual 구문 다이어그램 이 매뉴얼에서는 다음 구성 요소로 구축된 다이어그램을 사용하여, 명령문의 구문을 설명한다. 구성 요소 의미 예약어 명령문이 시작한다. 완전한 명령문이 아닌 구문 요소는 화살표로 시작한다. 명령문이 다음 라인에 계속된다. 완전한 명령문이 아닌 구문 요소는 이 기호로 종료한다. 명령문이 이전 라인으로부터 계속된다. 완전한 명령문이 아닌 구문 요소는 이 기호로 시작한다. ; 명령문이 종료한다. SELECT 필수 항목 NOT 선택적 항목 ADD DROP 선택사항이 있는 필수 항목. 한 항목만 제공해야 한다. ASC DESC 선택사항이 있는 선택적 항목. , ASC DESC 선택적 항목. 여러 항목이 허용된다. 각 반복 앞부분에 콤마가 와야 한다. 샘플 코드 규칙 코드 예제는 SQL, Stored Procedure, iSQL, 또는 다른 명령 라인 구문들을 예를 들어 설명한다. 아래 테이블은 코드 예제에서 사용된 인쇄 규칙에 대해 설명한다. 규칙 의미 예제 [ ] 선택 항목을 표시 VARCHAR [(size)] [[FIXED |] VARIABLE] 서문 v { } 필수 항목 표시. 반드시 하나 이상을 선택해야 되는 표시 { ENABLE | DISABLE | COMPILE } | 선택 또는 필수 항목 표시의 인자 구분 표시 { ENABLE | DISABLE | COMPILE } [ ENABLE | DISABLE | COMPILE ] . . . 그 이전 인자의 반복 표시 예제 코드들의 생략되는 것을 표시 SQL> SELECT ename FROM employee; ENAME ------------------------ SWNO HJNO HSCHOI . . . 20 rows selected. 그 밖에 기호 위에서 보여진 기호 이 외에 기호들 EXEC :p1 := 1; acc NUMBER(11,2); 기울임 꼴 구문 요소에서 사용자가 지정해야 하는 변수, 특수한 값을 제공해야만 하는 위치 지정자 SELECT * FROM table_name; CONNECT userID/password; 소문자 사용자가 제공하는 프로그램의 요소들, 예를 들어 테이블 이름, 칼럼 이름, 파일 이름 등 SELECT ename FROM employee; 대문자 시스템에서 제공하는 요소들 또는 구문에 나타나는 키워드 DESC SYSTEM_.SYS_INDICES_; 샘플 스키마 이 매뉴얼 내의 예제중의 일부는 employees, departments 및 orders테이블 같은 샘플 테이블에 기반하여 작성되었다. 이들 테이블은 $ALTIBASE_HOME/sample/APRE/schema 디렉터리의 schema.sql을 사용하여 생성할 수 있다. 샘플 스키마에 대한 온전한 정보는 General Reference를 참고하기 바란다. 관련 자료 자세한 정보를 위하여 다음 문서 목록을 참조하기 바란다. vi Stored Procedures Manual Installation Guide Getting Started Guide SQL Reference iSQL User’s Manual Error Message Reference 온라인 매뉴얼 알티베이스 고객서비스포털(http://support.altibase.com/)에서 국문 및 영문 매뉴얼(PDF, HTML)을 받을 수 있다. 알티베이스는 여러분의 의견을 환영합니다. 이 매뉴얼에 대한 여러분의 의견을 보내주시기 바랍니다. 사용자의 의견은 다음 버전의 매뉴얼을 작성하는데 많은 도움이 됩니다. 보내실 때에는 아래 내용과 함께 고객서비스포털(http://support.altibase.com/)로 보내주시기 바랍니다. 사용 중인 매뉴얼의 이름과 버전 매뉴얼에 대한 의견 사용자의 성함, 주소, 전화번호 이 외에도 알티베이스 기술지원 설명서의 오류와 누락된 부분 및 기타 기술적인 문제들에 대해서 이 주소로 보내주시면 정성껏 처리하겠습니다. 기술적인 부분과 관련하여 즉각적인 도움이 필요한 경우에는 기술지원센터로 연락하시기 바랍니다. 여러분의 의견에 항상 감사드립니다. 저장 프로시저 1 1. 저장 프로시저 2 Stored Procedures Manual 저장 프로시저의 개요 저장 프로시저(Stored Prodedure)란 SQL문들과 흐름 제어문, 할당문, 오류 처리 루틴 등으로 구성된 데이터베이스 객체 (object) 중의 하나이다. 저장 프로시저는 생성될 때 컴파일 되어 바로 실행 가능한 상태로 데이터베이스에 저장되며 여러 세션에서 동시에 하나의 저장 프로시저를 실행하는 것도 가능하다 “저장 프로시저(Stored Prodedure)”라는 용어는 때때로 저장 프로시저와 저장 함수(Stored Function)을 모두 지칭하기도 한다. 저장 프로시저와 저장 함수는 저장 함수가 실행 시 값을 반환하는 것 외에는 차이가 없다. 저장 프로시저와 저장 함수는 각각 CREATE PROCEDURE 와 CREATE FUNCTION 구문을 사용해서 생성할 수 있다. 이 구문에 대한 자세한 설명은 2장 “저장 프로시저 SQL문”을 참고하기 바란다. 저장 프로시저의 종류 저장 프로시저 저장 프로시저는 SQL구문이나 다른 저장 프로시저 내에서 입력 인자, 출력 인자, 입출력 인자를 가지고 실행할 수 있다. 저장 프로시저 호출 시, 프로시저의 바디 부분에 정의된 절차에 따라서 SQL문을 수행하게 된다. 저장 프로시저는 반환 값을 가지지 않지만, 출력 인자와 입출력 인자들을 통해 프로시저를 호출한 클라이언트에게 값을 전달할 수도 있다. 이는 반환 값을 갖지 않기 때문에 SQL문의 연산식 (expression) 내에서 피연산자로 사용될 수 없다. 저장 함수 값을 반환하는 것만 제외하면 저장 프로시저와 동일하다. 저장 프로시저와 달리 하나의 반환 값을 가지므로 SQL문의 연산식 (expression)내에서 피연산자로 사용할 수 있다. 타입 세트 저장 프로시저의 사용자 정의 타입들을 정의한 집합이다. 이는 주로 저장 프로시저끼리 인자 또는 리턴 값으로 사용자 정의 타입을 주고받을 때 사용한다. 자세한 내용은 7장 “타입 세트”에서 다룬다. 저장 프로시저 3 저장 프로시저의 특징 SQL 구문을 이용한 절차적 프로그램 알티베이스 PSM (Persistent Stored Module)은 흐름 제어문과 예외 처리문을 제공하므로 SQL문을 사용해서 절차적 프로그래밍이 가능하다. 성능 여러 SQL문을 순차적으로 수행하는 클라이언트 프로그램의 경우에는 각 SQL문 수행 시 마다 데이터베이스 서버와 통신을 해야 하므로 통신 비용이 많이 발생한다. 반면, 저장 프로시저로 작성된 프로그램은 프로시저 호출 시 한번의 통신만으로 여러 SQL문을 수행할 수 있다. 따라서, 저장 프로시저를 사용하면 통신 부하의 감소와 함께 데이터베이스 서버와 클라이언트 응용 프로그램간의 데이터 타입의 차이로 인해 발생하는 내부적인 데이터 타입 변환의 부하도 줄일 수 있다. 모듈화 업무 절차를 구현하는데 필요한 모든 SQL작업을 하나의 저장 프로시저로 묶어 모듈화하여 관리할 수 있다. 소스 코드 관리의 용이성 저장 프로시저는 데이터베이스 서버에 저장되는 모듈이기 때문에, 업무 로직의 변경 시 여러 클라이언트에 설치된 프로그램들을 모두 수정할 필요 없이 저장 프로시저만 변경하면 되므로 프로그램 관리가 용이하다. 공유와 생산성 한 사용자가 생성한 저장 프로시저는 데이터베이스에 저장되므로 접근 권한이 부여된 다른 사용자도 해당 저장 프로시저를 실행할 수 있어 서로 공유할 수 있을 뿐만 아니라, 한 저장 프로시저 내에서 다른 저장 프로시저의 호출이 가능하므로 같은 업무 절차의 재 프로그래밍이 필요 없으므로 생산성을 높일 수 있다. SQL과의 통합성 저장 프로시저 내의 흐름 제어문의 조건절은 SELECT문의 조건절을 4 Stored Procedures Manual 그대로 사용할 수 있다. 즉, C/C++등의 주 언어의 흐름 제어문의 조건절에서는 사용할 수 없는 SQL문 스타일의 기능을 사용할 수 있다. 또한, 연산식에 부질의 (subquery)를 사용하거나 SQL문이 지원하는 시스템 제공 함수들을 그대로 사용할 수 있다는 점 등 SQL문과 밀착된 프로그래밍이 가능하다. 에러 및 예외처리 저장 프로시저 내에서 Exception Handler를 제공하므로 SQL문 수행 도중 오류가 발생했을 때 적절한 대응 조치를 서버 내에서 바로 처리할 수 있다. 저장성 저장 프로시저 또한 데이터베이스 객체이기 때문에 사용자가 삭제하기 전까지 데이터베이스 내에 저장된다. 따라서 업무 절차 또한 데이터베이스에 저장하여 보존시킬 수 있다. 저장 프로시저 5 저장 프로시저의 구조 저장 프로시저는 블록으로 구조화된 언어로, 저장 프로시저의 바디는 여러 개의 논리적인 블록들로 구성된다. 저장 프로시저는 크게 헤더와 바디로 나뉘어진다. 저장 프로시저의 바디는 하나의 큰 블록으로서 선언부, 프로시저의 실제 바디, 예외 처리부로 구성된다. 바디는 다시 여러 개의 하위 블록들을 가질 수 있다. 저장 프로시저 구조를 예를 들어 설명하면 다음과 같다. CREATE FUNCTION myCheck (id IN INTEGER, ...... ) RETURN INTEGER AS v_name CHAR(20); v_salary INTEGER; comm_missing EXCEPTION; CURSOR ...... BEGIN ...... IF id > 10000 THEN ...... DECLARE ...... BEGIN ...... END; ...... END IF; RAISE comm_missing; ...... RETURN v_salary; ...... EXCEPTION ...... WHEN comm_missing THEN ...... ...... END; 블록2는 블록1의 하위 블록으로 블록1의 구조와 같이 선언부, 바디, 예외 처리부로 구성될 수도 있다. 흐름 제어문도 명시적인 시작과 끝을 알 수 있는 하나의 블록이다. 저장 프로시저 헤더 저장 프로시저 바디 (블록1) 블록1 선언부 블록1 바디 블록1 예외 처리부 블록2 6 Stored Procedures Manual 저장 프로시저 7 저장 프로시저 사용시 주의 사항 트랜잭션 관리 저장 프로시저 내에서 사용 가능한 트랜잭션 제어문은 COMMIT, ROLLBACK문이다. 저장 프로시저 내에서 사용한 트랜잭션 제어문은 저장 프로시저 밖의 작업에도 영향을 미칠 수 있다. 예를 들어서 NON-AUTOCOMMIT 모드에서 다음과 같은 작업을 수행했다고 가정하자. iSQL> INSERT INTO t1 values (1); iSQL> INSERT INTO t1 values (2); iSQL> EXECUTE proc1; proc1이 “INSERT INTO t1 values (3)” 구문과 “ROLLBACK” 문을 수행한다면 프로시저 내에서 입력한 3 뿐만 아니라 프로시저 외부의 iSQL에서 입력한 1과 2도 ROLLBACK된다. 즉, 위의 두 INSERT문과 EXECUTE문은 하나의 트랜잭션으로 처리된다. 제약 사항 커서가 OPEN 된 상태에서는 COMMIT 또는 ROLLBACK을 실행할 수 없다. 즉, 커서를 사용하는 트랜잭션은 커서의 OPEN, FETCH, CLOSE로 구성된 전체 커서 블록을 모두 포함하고 있어야 한다. SELECT문 내에서 호출되는 저장 함수의 경우 저장 함수 내에 INSERT, UPDATE, DELETE문은 사용할 수 없으며, 트랜잭션 제어문도 수행할 수 없다. INSERT, UPDATE, DELETE문 내에서 호출되는 저장 함수내에서도 트랜잭션 제어문을 수행할 수 없다. LOB 타입의 변수는 저장 프로시저 선언부에서 선언할 수 없으며, LOB 타입 칼럼 또는 다른 변수와 함께 %TYPE, %ROWTYPE을 이용해서도 변수를 선언할 수 없다. LOB 타입의 변수는 선언을 할 수 없으므로 커서에서 값을 받아 올 수 없다. 따라서, LOB 타입의 칼럼은 커서문에서 참조할 수 없다. 8 Stored Procedures Manual 관련 메타 테이블 저장 프로시저 관련 메타 테이블에 대한 자세한 내용은 General Reference 의 데이터 딕셔너리 부분을 참조한다. 저장 프로시저 SQL문 9 2. 저장 프로시저 SQL문 10 Stored Procedures Manual 개요 저장 프로시저 SQL문 아래 표는 저장 프로시저, 함수와 타입 세트를 생성하고 관리하는 데 사용하는 DDL문을 보여준다. CREATE TYPESET 과 DROP TYPESET 구문에 대한 설명은 7장 타입 세트를 참고하기 바란다. 종류 관련문장 설명 생성 CREATE [OR REPLACE] PROCEDURE 문 새로운 저장 프로시저를 생성하거나 이미 생성된 저장 프로시저의 정의를 변경하는 문장이다. CREATE [OR REPLACE] FUNCTION 문 새로운 저장 함수를 생성하거나 이미 생성된 저장 함수의 정의를 변경하는 문장이다. CREATE [OR REPLACE] TYPESET 문 타입 세트를 생성 또는 변경하는 문장이다. 변경 ALTER PROCEDURE 문 이 구문은 저장 프로시저를 재컴파일하여 프로시저의 실행 계획을 최적화 할 때 사용한다. 저장 프로시저 생성 후에 저장 프로시저가 참조하는 객체의 정의가 변경되었다면, 그 상황에서의 저장 프로시저의 실행 계획은 최적화 상태가 아닐 수 있다. ALTER FUNCTION 문 ALTER PROCEDURE문과 동일하다. 삭제 DROP PROCEDURE 문 생성된 저장 프로시저를 삭제 하는 문장이다. DROP FUNCTION 문 생성된 저장 함수를 삭제 하는 문장이다. DROP TYPESET 문 생성된 타입 세트를 삭제 하는 문장이다. 실행 EXECUTE 문 저장 프로시저 또는 저장 함수를 실행하는 문장이다. 저장 프로시저 SQL문 11 function_name SQL문 내에서 호출할 때 이름으로 참조할 수 있다. 데이터 타입 저장 프로시저에서는 다음과 같은 데이터 타입을 지원한다. Primitive 타입 - 일반 데이터 타입: SQL 구문에서 사용가능한 데이터 타입으로 자세한 사항은 SQL Reference에 있는 데이터 타입 부분을 참조한다. - BOOLEAN 타입: 저장 프로시저 내에서만 사용 가능하며, TRUE, FALSE의 값을 가진다. FILE_TYPE 저장 프로시저 내에서만 사용 가능하며, 파일 제어를 위한 타입이다. 자세한 내용은 9장 “파일 제어”를 참조한다. 사용자 정의 타입 저장 프로시저 내에서만 사용 가능하며, RECORD 및 ASSOCIATIVE ARRAY를 지원한다. 자세한 내용은 6장 “사용자 정의 타입”을 참조한다. 12 Stored Procedures Manual CREATE PROCEDURE 구문 CREATEprocedure_name create_procedure::= PROCEDURE parameter_declaration() , BEGIN declaration_section statement EXCEPTIONexception_handler END procedure_name parameter_declaration ::= parameter_name IN OUT INOUT data_type DEFAULT := expression REPLACEuser_name.OR AS IS 기능 저장 프로시저를 새로 생성하거나 이미 생성되어 있는 저장 프로시저를 새로운 저장 프로시저로 변경하는 기능을 수행한다. 저장 프로시저 SQL문 13 parameter_declaration 인자는 생략할 수 있으며, 인자를 명시할 경우엔 인자의 명칭, 데이터 타입 및 입출력 구분을 명시해야 한다. 사용 가능한 입출력 구분 값은 다음 세가지 중의 하나이고 생략 시에 IN이 기본값이 된다. IN: 프로시저 호출시 입력값으로 주어지는 입력 인자 OUT: 프로시저 실행 후 출력 값을 반환하는 인자 INOUT: 입출력 공용 인자로 프로시저 호출시 입력값을 줄 수 있고, 실행 후에 출력 값을 반환할 수 있다 저장 프로시저가 실행될 때, IN 인자를 사용해서 프로시저에 값을 전달하고, 프로시저는 OUT인자를 사용해서 호출한 루틴에 값을 반환한다. IN 인자는 저장 프로시저 내에서 상수처럼 동작하므로, 프로시저 내에서 할당문을 사용해 인자에 값을 대입할 수 없으며 SELECT문의 INTO 절에도 사용할 수 없다. 인자는 기본 값을 가질 수 있다. 저장 프로시저가 호출될 때 기본값이 정의된 인자에 값을 넘겨 주지 않을 경우, 기본 값이 사용된다. declaration_section 3장의 “지역 변수 선언”절 참고 data_type 3장의 “지역 변수 선언”절 참고 Exception Handler 9장 “Exception Handler” 참고 CREATE PROCEDURE 문의 실행 저장 프로시저 생성 구문은 텍스트 편집기에서 작성해서 iSQL에 붙여넣거나, iSQL에서 직접 한 라인씩 입력할 수도 있다. 각 SQL문, 저장 프로시저 제어문, 그리고 블록 (END)의 끝에 세미콜론(“;”)을 입력한다. iSQL에서 CREATE PROCEDURE문을 실행할 때는 마지막 END; 문의 다음 라인에 반드시 슬래시(“/”)를 입력해야 프로시저 생성문이 14 Stored Procedures Manual 실행된다. CREATE PROCEDURE문 실행 시 컴파일 오류가 발생하지 않고 블록이 성공적으로 컴파일 되면 “Create Success” 메시지가 출력된다. 저장 프로시저 바디 부분에 대해서는 다음 장에서부터 각각 블록, 흐름 제어문, 커서, Exception Handler 부분으로 구분해서 설명한다. 예제 예제1 (IN 인자 사용) CREATE TABLE t1 (i1 INTEGER UNIQUE, i2 INTEGER, i3 INTEGER); INSERT INTO t1 VALUES (1,1,1); INSERT INTO t1 VALUES (2,2,2); INSERT INTO t1 VALUES (3,3,3); INSERT INTO t1 VALUES (4,4,4); INSERT INTO t1 VALUES (5,5,5); SELECT * FROM t1; CREATE OR REPLACE PROCEDURE proc1 (p1 IN INTEGER, p2 IN INTEGER, p3 IN INTEGER) AS v1 INTEGER; v2 t1.i2%type; v3 INTEGER; BEGIN SELECT * INTO v1, v2, v3 FROM t1 WHERE i1 = p1 AND i2 = p2 AND i3 = p3; IF v1 = 1 AND v2 = 1 AND v3 = 1 THEN UPDATE t1 SET i2 = 7 WHERE i1 = v1; ELSIF v1 = 2 AND v2 = 2 AND v3 = 2 THEN UPDATE t1 SET i2 = 7 WHERE i1 = v1; ELSIF v1 = 3 AND v2 = 3 AND v3 = 3 THEN UPDATE t1 SET i2 = 7 WHERE i1 = v1; ELSIF v1 = 4 AND v2 = 4 AND v3 = 4 THEN UPDATE t1 SET i2 = 7 WHERE i1 = v1; ELSE DELETE FROM t1; END IF; INSERT INTO t1 VALUES (p1+10, p2+10, p3+10); 저장 프로시저 SQL문 15 END; / iSQL> EXEC proc1 (2,2,2); Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ---------------------------------------- 1 1 1 3 3 3 4 4 4 5 5 5 2 7 2 12 12 12 6 rows selected. 예제2 (기본값이 있는 인자 사용) CREATE TABLE t1 (i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE OR REPLACE PROCEDURE proc1 (p1 IN INTEGER DEFAULT 1, p2 IN INTEGER DEFAULT 1, p3 IN INTEGER DEFAULT 1) AS BEGIN INSERT INTO t1 VALUES (p1, p2, p3); END; / EXEC proc1; SELECT * FROM t1; EXEC proc1(2); SELECT * FROM t1; EXEC proc1(3,3); SELECT * FROM t1; EXEC proc1(4,4,4); iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ---------------------------------------- 1 1 1 2 1 1 3 3 1 4 4 4 16 Stored Procedures Manual 4 ro w s s e l e c t e d . 예제3 CREATE OR REPLACE PROCEDURE proc1 (emp_id INTEGER, amount NUMBER(10,2)) AS BEGIN UPDATE employee SET salary = salary + amount WHERE eno = emp_id; END; / iSQL> EXEC proc1(15, '250000'); Execute success. iSQL> SELECT * FROM employee WHERE eno=15; ENO ENAME EMP_JOB EMP_TEL ------------------------------------------------------------------------- DNO SALARY SEX BIRTH JOIN_DATE STATUS ---------------------------------------------------------------- 15 JHSEOUNG WEBMASTER 0119556884 1003 1250000 M 1212 H 1 row selected. 예제4 (출력, 입출력 인자 사용) CREATE TABLE t4(i1 INTEGER, i2 INTEGER); INSERT INTO t4 VALUES(1,1); INSERT INTO t4 VALUES(1,1); INSERT INTO t4 VALUES(1,1); INSERT INTO t4 VALUES(1,1); INSERT INTO t4 VALUES(1,1); CREATE OR REPLACE PROCEDURE proc1(a1 OUT INTEGER, a2 IN OUT INTEGER) AS BEGIN SELECT COUNT(*) INTO a1 FROM t4 WHERE i2 = a2; END; / iSQL> VAR t3 INTEGER; iSQL> VAR t4 INTEGER; iSQL> EXEC :t4 := 1; 저장 프로시저 SQL문 17 Execute success. iSQL> EXEC proc1(:t3, :t4); Execute success. iSQL> PRINT t3; NAME TYPE VALUE ----------------------------------------------- T3 INTEGER 5 예제5 CREATE OR REPLACE PROCEDURE proc1(p1 INTEGER, p2 IN OUT INTEGER, p3 OUT INTEGER) AS BEGIN p2 := p1; p3 := p1 + 100; END; / iSQL> VAR v1 INTEGER; iSQL> VAR v2 INTEGER; iSQL> VAR v3 INTEGER; iSQL> EXEC :v1 := 3; Execute success. iSQL> EXEC proc1(:v1, :v2, :v3); Execute success. iSQL> PRINT VAR; [ HOST VARIABLE ] ----------------------------------------------- NAME TYPE VALUE ----------------------------------------------- V1 INTEGER 3 V2 INTEGER 3 V3 INTEGER 103 예제6 (입출력 인자 사용) CREATE TABLE t3(i1 INTEGER); INSERT INTO t3 VALUES(1); INSERT INTO t3 VALUES(1); INSERT INTO t3 VALUES(1); CREATE OR REPLACE PROCEDURE proc1(a1 IN OUT INTEGER) AS BEGIN 18 Stored Procedures Manual SELECT COUNT(*) INTO a1 FROM t3 WHERE i1 = a1; END; / iSQL> VAR p1 INTEGER; iSQL> EXEC :p1 := 1; Execute success. iSQL> EXEC proc1(:p1); Execute success. iSQL> PRINT p1; NAME TYPE VALUE ----------------------------------------------- P1 INTEGER 3 예제7 CREATE OR REPLACE PROCEDURE proc1(p1 INTEGER, p2 IN OUT INTEGER, p3 OUT INTEGER) AS BEGIN p2 := p1 + p2; p3 := p1 + 100; END; / iSQL> VAR v1 INTEGER; iSQL> VAR v3 INTEGER; iSQL> EXEC :v1 := 3; Execute success. iSQL> EXEC :v2 := 5; Execute success. iSQL> EXEC proc1(:v1, :v2, :v3); Execute success. iSQL> PRINT VAR; [ HOST VARIABLE ] ----------------------------------------------- NAME TYPE VALUE ----------------------------------------------- V1 INTEGER 3 V2 INTEGER 8 V3 INTEGER 103 저장 프로시저 SQL문 19 주의사항 CREATE PROCEDURE구문에서는 인자로 LOB 타입을 사용할 수 없다. 20 Stored Procedures Manual ALTER PROCEDURE 구문 ALTERPROCEDUREprocedure_name alter_procedure_statement ::= ;COMPILE user_name. 기능 저장 프로시저 생성 이후에 이 프로시저 내에서 참조하는 테이블, 시퀀스 등의 데이터베이스 오브젝트 혹은 이 저장 프로시저가 호출하는 다른 저장 프로시저, 저장 함수 등의 정의가 변경되어서, 현재 이 저장 프로시저의 실행 계획으로는 이를 실행할 수 없는 경우에 이 저장 프로시저는 무효한 (invalid) 상태라고 한다. 이는 프로시저 내의 SQL문을 위한 실행 계획은 프로시저 생성 시점에 만들어졌고, 더 이상 최적화된 상태도 아니고 실행할 수도 없기 때문이다. 예를 들면 처음 저장 프로시저 생성 시 존재하던 인덱스가 삭제된 경우 이전 실행 계획은 인덱스를 통해 테이블에 접근하도록 계획되어 있으므로 이전의 실행 계획을 이용해서 테이블에 접근할 수 없게 된다. 무효한 상태의 프로시저가 호출되면, 알티베이스 서버는 바로 자동으로 이를 재 컴파일 한다. 그러나 런타임 시에 컴파일하는 것은 심각한 성능 이슈를 불러올 수 있으므로, 프로시저가 무효한 상태가 되었을 때 수동으로 컴파일 하는 것이 좋다. ALTER PROCEDURE 문은 사용자가 명시적으로 저장 프로시저를 컴파일 때 사용된다. 예제 예제 1 저장 프로시저 SQL문 21 CREATE TABLE t1 (i1 NUMBER, i2 VARCHAR(10), i3 DATE); CREATE OR REPLACE PROCEDURE proc1 (p1 IN NUMBER, p2 IN VARCHAR(10), p3 IN DATE) AS BEGIN IF p1 > 0 then INSERT INTO t1 VALUES (p1, p2, p3); END IF; END; / iSQL> EXECUTE proc1 (1, 'seoul', '20-JUN-2002'); Execute success. iSQL> EXECUTE proc1 (-3, 'daegu', '21-APR-2002'); Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ----------------------------------------------- 1 seoul 20-JUN-2002 1 row selected. 예제 2 CREATE TABLE t1 (i1 NUMBER, i2 VARCHAR(10), i3 DATE DEFAULT SYSDATE); ALTER PROCEDURE proc1 COMPILE; iSQL> EXECUTE proc1 (2, 'incheon', SYSDATE); Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ----------------------------------------------- 2 incheon 28-DEC-2010 1 row selected. 22 Stored Procedures Manual DROP PROCEDURE 구문 DROPPROCEDUREprocedure_name drop_procedure_statement ::= ; user_name. 기능 데이터베이스에서 저장 프로시저를 삭제하는 구문이다. 삭제하고자 하는 프로시저를 다른 저장 프로시저에서 참조하고 있다 하더라도, 그 프로시저는 삭제된다. 삭제되고 없는 저장 프로시저나 함수가 호출될 때 알티베이스는 오류 코드를 반환한다. 예제 DROP PROCEDURE proc1; 저장 프로시저 SQL문 23 EXECUTE 구문 EXEC(UTE)procedure_name; execute_procedure_statement ::= EXEC(UTE)function_name; execute_function_statement ::= variable:= user_name. user_name.() expression , () expression , 기능 저장 프로시저 또는 저장 함수를 실행한다. 예제 CREATE OR REPLACE PROCEDURE proc1(eid INTEGER, amount NUMBER(10,2)) AS current_salary NUMBER(10,2); BEGIN SELECT salary INTO current_salary FROM employee WHERE eno = eid; UPDATE employee SET salary = salary + amount 24 Stored Procedures Manual WHERE eno = eid; END; / iSQL> SELECT * FROM employee WHERE eno = 15; ENO ENAME EMP_JOB EMP_TEL ------------------------------------------------------------------------- DNO SALARY SEX BIRTH JOIN_DATE STATUS ---------------------------------------------------------------- 15 JHSEOUNG WEBMASTER 0119556884 1003 1000000 M 1212 H 1 row selected. iSQL> EXEC proc1(15, 333333); Execute success. iSQL> SELECT * FROM employee WHERE eno = 15; ENO ENAME EMP_JOB EMP_TEL ------------------------------------------------------------------------- DNO SALARY SEX BIRTH JOIN_DATE STATUS ---------------------------------------------------------------- 15 JHSEOUNG WEBMASTER 0119556884 1003 1333333 M 1212 H 1 row selected. 저장 프로시저 SQL문 25 CREATE FUNCTION 구문 CREATEfunction_name create_function::= FUNCTION parameter_declaration() , BEGIN declaration_section statement EXCEPTIONexception_handler END function_name parameter_declaration ::= parameter_name IN OUT INOUT data_type DEFAULT := expression REPLACEuser_name.OR AS IS RETURNdata_type 기능 저장 함수를 새로 생성하거나 이미 생성되어 있는 저장 함수를 대체한다. 26 Stored Procedures Manual parameter_declaration 저장 프로시저와 마찬가지로, 저장 함수의 인자도 IN, IN/OUT, OUT 인자로 정의할 수 있다. 인자 타입과 주의 사항에 대한 설명은 CREATE PROCEDURE 구문 설명의 “parameter_declaration”을 참고하기 바란다. RETURN data_type 저장 함수는 저장 프로시저와 달리 실행 후 하나의 값을 반환한다. 그러므로 반드시 반환 데이터 타입을 명시해야 한다. Declaration Section 3장의 “지역 변수 선언” 절 참고 Data Types 3장의 “지역 변수 선언” 절 참고 Exception Handler 9장 Exception Handler 참고 CREATE FUNCTION 문의 실행 저장 함수 생성 구문의 실행은 저장 프로시저 구문과 동일하다. CREATE PROCEDURE 절의 “CREATE PROCEDURE 문의 실행”을 참고하기 바란다. 예제 CREATE TABLE t1( seq_no INTEGER, user_id VARCHAR(9), rate NUMBER, start_date DATE, end_date DATE); INSERT INTO t1 VALUES(0, '000000500', 200.50, '23-May-2002', '23-Apr-2002'); INSERT INTO t1 VALUES(0, '000000501', 190, '23-Nov-2002', '23-Dec-2002'); INSERT INTO t1 VALUES(0, '000000523', 100, '12-Dec-2001', '12-Jan-2001'); INSERT INTO t1 VALUES(0, '000000532', 100, '11-Dec-2001', '11-Jan-2002'); 저장 프로시저 SQL문 27 INSERT INTO t1(seq_no, user_id, start_date, end_date) VALUES(0, '000000524', '30-Oct-2001', '30-Nov-2001'); INSERT INTO t1 VALUES(0, '000000524', 200.50, '30-Apr-2002', '30-May-2002'); INSERT INTO t1 VALUES(0, '000000524', 200.50, '30-Apr-2002', '30-May-2002'); INSERT INTO t1 VALUES(1, '000000524', 100, '30-Apr-2002', '30-May-2002'); INSERT INTO t1 VALUES(1, '000000524', 115.0, '19-Jan-2002', '19-Mar-2002'); INSERT INTO t1 VALUES(0, '000000502', 120.0, '27-Jan-2002', '27-Feb-2002'); INSERT INTO t1 VALUES(1, '000000504', 150.0, '26-Nov-2001', '26-Dec-2001'); iSQL> SELECT * FROM t1; T1.SEQ_NO T1.USER_ID T1.RATE T1.START_DATE ------------------------------------------------------------ T1.END_DATE ----------------------- 0 000000500 200.5 2002/05/23 00:00:00 2002/04/23 00:00:00 0 000000501 190 2002/11/23 00:00:00 2002/12/23 00:00:00 0 000000523 100 2001/12/12 00:00:00 2001/01/12 00:00:00 0 000000532 100 2001/12/11 00:00:00 2002/01/11 00:00:00 0 000000524 2001/10/30 00:00:00 2001/11/30 00:00:00 0 000000524 200.5 2002/04/30 00:00:00 2002/05/30 00:00:00 0 000000524 200.5 2002/04/30 00:00:00 2002/05/30 00:00:00 1 000000524 100 2002/04/30 00:00:00 2002/05/30 00:00:00 1 000000524 115 2002/01/19 00:00:00 2002/03/19 00:00:00 0 000000502 120 2002/01/27 00:00:00 2002/02/27 00:00:00 1 000000504 150 2001/11/26 00:00:00 2001/12/26 00:00:00 11 rows selected. CREATE OR REPLACE FUNCTION get_rate (p1 IN CHAR(30), p2 IN CHAR(30), p3 IN VARCHAR(9)) RETURN NUMBER AS v_rate NUMBER; BEGIN 28 Stored Procedures Manual SELECT NVL(SUM(rate), 0) INTO v_rate FROM (SELECT rate FROM t1 WHERE start_date = TO_DATE(p1) AND end_date = TO_DATE(p2) AND user_id = '000000' || p3 AND seq_no = 0); RETURN v_rate; END; / iSQL> VAR res NUMBER; iSQL> EXECUTE :res := get_rate('30-Apr-2002', '30-May-2002', '524'); Execute success. iSQL> PRINT res; NAME TYPE VALUE ----------------------------------------------- RES NUMBER 401 주의 사항 CREATE PROCDURE구문처럼, CREATE FUNCTION 구문에서도 인자로 LOB 타입을 사용할 수 없다. 또한 반환 값으로 LOB 타입을 사용할 수도 없다. 저장 프로시저 SQL문 29 ALTER FUNCTION 구문 ALTERFUNCTIONfuntion_name alter_function_statement ::= ;COMPILE user_name. 기능 저장 프로시저와 마찬가지로, 저장 함수 생성 후에 함수 내에서 참조하는 데이터베이스 객체의 정의가 변경되어 현재 이 저장 함수의 실행 계획으로는 더 이상 실행할 수 없는 경우에 이 저장 함수는 무효한 상태라고 한다. ALTER FUNCTION문은 저장 함수를 명시적으로 재 컴파일 하여 유효한 상태의 실행 계획을 다시 생성하기 위해 사용된다. 더 자세한 설명은 ALTER PROCECURE 절을 참고한다. 예제 ALTER FUNCTION get_dept_name COMPILE; 30 Stored Procedures Manual DROP FUNCTION 구문 DROPFUNCTIONfunction_name drop_function_statement ::= ; user_name. 기능 저장 함수를 삭제하는 구문이다. 삭제하고자 하는 저장 함수를 다른 저장 프로시저 또는 저자 함수에서 참조하고 있다 하더라도, 그 저장 함수는 삭제된다. 이미 삭제된 저장 함수를 참조하고 있던 임의의 저장 프로시저 또는 저장 함수가 실행될 때 알티베이스는 오류를 출력한다. 예제 DROP FUNCTION get_dept_name; 저장 프로시저 블록 31 3. 저장 프로시저 블록 저장 프로시저와 저장 함수는 한 개 이상의 블록으로 구성된다. 이 장에서는 블록을 사용해서 저장 프로시저 내에 절차화된 프로그램을 작성하는 방법을 설명한다. 32 Stored Procedures Manual 저장 프로시저 블록 구문 block ::= label_name<<>>DECLAREdeclaration_section statementBEGIN EXCEPTION exception_handler END label_name statement ::= print_statement label_name<<>> sql_statement control_flow_statement open_cursor_statement fetch_statement close_cursor_statement assignment_statement return_statement block raise_statement ; open_for_statement 저장 프로시저 블록 33 sql_statement ::= select_into_statement insert_statement delete_statement update_statement commit_statement enqueue_statement rollback_statement execute_imme_statement 블록은 크게 선언부(Declare Section), 블록 바디(Block Body), 예외 처리부(Exception Handler Section)의 세 부분으로 나뉘어진다. DECLARE, BEGIN, EXCEPTION 등의 키워드 뒤에는 세미콜론을 사용하지 않지만 END 및 기타 모든 프로시저 내의 명령문 뒤에는 세미콜론이 있어야 한다. 저장 프로시저의 코드에 주석 처리를 할 수 있다. 단일 행 주석에는 ‘--‘를 문장 앞에 붙이고, 여러 행을 주석 처리 할 경우는 ‘/*’와 ‘*/’ 사이에 주석 처리할 문장이 놓이도록 작성한다. 이 장에서는 선언부와 블록 바디에 사용할 수 있는 구문 중 SELECT INTO문, 변수 할당문, LABEL문, PRINT문, RETURN문에 대해서 설명한다. 저장 프로시저 내에서 사용 가능한 흐름 제어문, 커서 관련문, 예외 처리에 관련된 내용은 다음 장에서 순서대로 설명하고 있다. 그 외 SQL문들에 대한 자세한 내용은 SQL Reference을 참조한다. 선언부 선언부는 메인 블록에서는 AS와 BEGIN 키워드 사이에 위치하며, 하위 블록에서는 DECLARE와 BEGIN 키워드 사이에 위치한다. 해당 블록 내에서 사용하는 지역 변수, 커서와 사용자 정의 예외 등을 선언한다. 34 Stored Procedures Manual 이 장에서는 지역 변수만을 설명한다. 커서와 예외 처리는 5장 커서와 9장 예외 처리에서 각각 설명한다. 블록 바디 BEGIN과 END사이의 부분으로 SQL문과 흐름 제어문을 포함한다. 블록 바디 내에 기술 가능한 SQL문과 흐름 제어문은 다음과 같다. DML 문: SELECT/INSERT/DELETE/UPDATE 트랜잭션 처리문: COMMIT/ROLLBACK 흐름 제어문: IF, CASE, FOR, LOOP, WHILE, EXIT, CONTINUE, NULL 할당문 출력문: PRINT, RETURN 커서 관련문: OPEN, FETCH, CLOSE, Cursor FOR LOOP 동적 SQL문: EXECUTE IMMEDIATE 예외처리 구문: RAISE, RAISE_APPLICATION_ERROR 저장 프로시저의 장점 중 하나는 SQL문과 달리 블록을 사용하여 명령문을 중첩할 수 있다는 점이다. 명령문을 사용할 수 있는 위치에는 블록을 중첩할 수 있으므로, 명령문을 블록으로 만들어서 중첩시키면 된다. 예외처리부 EXCEPTION과 END 사이의 부분으로 저장 프로시저 또는 함수 실행 중에 오류가 발생했을 때 처리할 루틴을 기술한다. 저장 프로시저 블록 35 지역 변수 선언 구문 declaration_section ::= variable_declaration constant_declaration cursor_declaration exception_declaration variable_declaration ::= variable_namedata_type expression DEFAULT := constant_declaration ::= constant_nameCONSTANTdata_typeexpression DEFAULT := data_type ::= sql_data_type type_attribute rowtype_attribute 36 Stored Procedures Manual type_attreibute ::= table_name .column_name record_name variable_name rowtype_attribute ::= %TYPE cursor_name table_name %ROWTYPE 기능 variable_name 변수의 이름을 명시할 때 사용된다. 변수의 이름은 하나의 블록 범위 내에서 유일해야 한다. 칼럼과 같은 이름을 가지는 변수를 SQL 문장 내에서 사용할 경우, 이것은 칼럼 명으로 인식된다. 다음의 예에서 eno는 칼럼 명으로 인식되어 employee테이블의 모든 레코드가 삭제된다. DECLARE eno INTEGER := 100; BEGIN DELETE FROM employee WHERE eno = eno; … 다음과 같은 방법으로 모호성을 없앨 수 있다. 저장 프로시저 블록 37 <> DECLARE eno INTEGER := 100; BEGIN DELETE FROM employee WHERE eno = del_block.eno; 블록 이름에 관한 설명은 이 장의 “LABEL” 절을 참고한다. data_type 변수의 데이터 타입을 명시한다. 저장 프로시저 내에서 사용 가능한 데이터 타입은 아래와 같다: SQL문에서 사용할 수 데이터 타입 BOOLEAN 타입 %TYPE 속성을 사용해서 이미 데이터 타입이 지정된 칼럼이나 변수와 같은 타입 사용 %ROWTYPE 속성을 사용해서 여러 개의 칼럼이 모인 레코드 타입을 정의 사용자 정의 타입 %TYPE과 %ROWTYPE 속성은 테이블 정의가 변경될 때마다 저장 프로시저 내에서 코드를 변경해야 하는 번거로움을 예방한다. 즉, 칼럼의 데이터 타입이 변경될 때, %TYPE속성을 사용해서 정의된 변수는 자동으로 변경된 타입에 맞추어 진다. 이는 데이터의 독립성을 실현하고 유지 보수 비용을 낮추는데 기여한다. CONSTANT 이 옵션은 특정 변수를 값을 할당할 수 없는 상수로 사용하고자 하는 경우에 사용할 수 있다. 이렇게 정의된 변수는 읽기 전용변수로만 사용할 수 있다. 다음과 같이 max_val을 선언하면 max_val에는 임의의 값을 할당할 수 없고 선언 시 할당한100의 값을 가지는 상수처럼 취급된다. max_val CONSTANT integer := 100; DEFAULT 다음과 같이 변수 선언 시 초기값을 설정할 때 사용된다. curr_val INTEGER DEFAULT 100; count_val INTEGER := 0; Cursor Declaration 38 Stored Procedures Manual 5장의 “CURSOR” 절 참고 Exception Declaration 9장의 “EXCEPTION” 절 참고 중첩 블록 및 변수의 범위 선언부에 명시한 변수들의 영향력은 자신이 선언된 BLOCK문의 BEGIN에서 시작되고 END에서 종료된다. 만약 block2가 block1 내에 정의되어 있고 각각의 블록 내에 같은 이름을 가지는 v_result 변수를 선언하였다면, block2의 밖에서 사용되는 v_result는 block1 에 정의된 변수를 참조하고 block2내에서 사용된 v_result는 block2에서 선언한 변수이다. 아래에 block2 (중첩 블록)에 있는 변수 y는 block1 (외부 블록)에 있는 변수 x를 참조할 수 있지만, 변수 x는 변수 y를 참조할 수 없다. 중첩 블록 내에 외부 블록에 있는 변수 x와 동일한 이름이 주어지면 그 값은 중첩 블록 내에서만 유효하다. /* start of block1 */ DECLARE v_result¹ integer; x integer; BEGIN .... v_result¹ := 1; .... /* start of sub-block (block2) */ DECLARE v_result² integer; y number; BEGIN ... v_result² := 2; ... END; /* end of block2 */ ... v_result¹ := 3; ... END; /* end of block1 */ Scope of y Scope of x Area can refer to v_result² Area can refer to v_result¹ Area can refer to v_result¹ 저장 프로시저 블록 39 제약 조건 다음과 같은 기능은 변수 선언부에서 지원하지 않는 기능들이다. 변수에 NOT NULL 속성을 지정할 수 없다. 여러 개의 변수들을 한꺼번에 선언할 수 없다. 즉 다음과 같이 선언할 수 없다. i, j, k INTEGER; 예제 %TYPE 사용 DECLARE my_title books.title%TYPE; my_title은 books테이블의 title 칼럼과 같은 데이터 타입을 가지는 변수로 정의된다. %ROWTYPE 사용 DECLARE dept_rec department%ROWTYPE dept_rec은 레코드 타입의 변수로서 department 테이블 또는 department 라는 이름의 커서와 동일한 레코드 타입을 가지게 된다. 예제 1 CONSTANT와 %ROWTYPE 을 사용해서 변수를 선언하는 예제이다. CREATE TABLE t1(i1 INTEGER, i2 INTEGER); CREATE OR REPLACE PROCEDURE proc1 AS v1 constant INTEGER := 1; v2 constant t1.i1%TYPE := 1; BEGIN INSERT INTO t1 VALUES (v1, v2); END; / EXEC proc1; iSQL> SELECT * FROM t1; T1.I1 T1.I2 40 Stored Procedures Manual --------------------------- 1 1 1 row selected. --DROP TABLE t1; CREATE TABLE t1 (i1 INTEGER, i2 INTEGER, i3 INTEGER); INSERT INTO t1 VALUES(1,1,1); CREATE OR REPLACE PROCEDURE proc1 AS r1 t1%ROWTYPE; BEGIN INSERT INTO t1 VALUES(3,3,3); < > DECLARE r1 t1%ROWTYPE; BEGIN SELECT i1, i2, i3 INTO s.r1.i1, s.r1.i2, s.r1.i3 FROM t1 WHERE i1 = 1; INSERT INTO t1 VALUES (s.r1.i1, s.r1.i2, s.r1.i3); END; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ---------------------------------------- 1 1 1 3 3 3 1 1 1 3 rows selected. 예제 2 %ROWTYPE 속성을 사용하는 예제이다. CREATE TABLE emp( eno INTEGER, ename CHAR(10), emp_job CHAR(15), join_date DATE, salary NUMBER(10,2), 저장 프로시저 블록 41 dno BYTE(2)); CREATE TABLE emp401( eno INTEGER, ename CHAR(10), emp_job CHAR(15), join_date DATE, leave_date DATE, salary NUMBER(10,2), dno BYTE(2), fund NUMBER(10,2) DEFAULT 0); INSERT INTO emp VALUES (10, 'DKLEE', 'ENGINEER', '01-Jul-2000', 30000000, BYTE'D001'); INSERT INTO emp VALUES (20, 'SWMYUNG', 'MANAGER', '01-Nov-1999', 50000000, BYTE'C002'); CREATE OR REPLACE PROCEDURE proc1(p1 INTEGER) AS BEGIN DECLARE emp_rec emp%ROWTYPE; BEGIN SELECT * INTO emp_rec FROM emp WHERE eno = p1; INSERT INTO emp401(eno, ename, emp_job, join_date, leave_date, salary, dno) VALUES(emp_rec.eno, emp_rec.ename, emp_rec.emp_job, emp_rec.join_date, sysdate, emp_rec.salary, emp_rec.dno); END; END; / iSQL> EXEC proc1(10); Execute success. iSQL> SELECT * FROM emp401; EMP401.ENO EMP401.ENAME EMP401.EMP_JOB EMP401.JOIN_DATE ----------------------------------------------- EMP401.LEAVE_DATE EMP401.SALARY EMP401.DNO EMP401.FUND ----------------------------------------------- 10 DKLEE ENGINEER 2000/07/01 00:00:00 2005/01/27 16:26:26 30000000 D001 0 42 Stored Procedures Manual 1 ro w s e l e c t e d . 저장 프로시저 블록 43 SELECT INTO 구문 select_into_statement ::= select_listINTO record_name variable_name FROMrest_of_select_statement; ; select_list와 rest_of_select_statement는 SELECT 구문의 문법과 동일하므로 SQL Reference을 참고한다. 기능 저장 프로시저에서 SELECT 문을 사용할 경우, SELECT문은 INTO 절을 포함해야 한다. 저장 프로시저 또는 함수 내에서 INTO 절을 가지는 SELECT 문은 하나의 레코드만 검색할 수 있다. INTO 절이 사용된 SELECT 구문이 여러 행을 반환하거나 한 행도 반환하지 않으면 오류가 발생한다. SELECT 절의 select_list와 INTO 절의 상응하는 variable_name은 개수가 동일해야 하며 호환 가능한 데이터 타입이어야 한다. %ROWTYPE속성으로 정의한 변수를 INTO 절에 사용하는 경우에도 %ROWTYPE변수 내의 칼럼의 개수와 select_list의 칼럼의 개수가 동일해야 하며, 상응하는 칼럼의 데이터 타입은 호환 가능해야 한다. 저장 프로시저는 표준 예외 사항이 발생하는 경우 오류를 발생 시킨다. NO_DATA_FOUND 및 TOO_MANY_ROWS 등의 예외 44 Stored Procedures Manual 사항을 사용하여 블록의 예외 처리부에서 오류를 처리할 수 있다. 예외 처리에 대한 상세한 설명은 9장 예외 처리를 참조한다. 예제 예제 1 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); INSERT INTO t1 VALUES(1,1,1); CREATE OR REPLACE PROCEDURE proc1 AS v1 INTEGER; r1 t1%ROWTYPE; BEGIN INSERT INTO t1 VALUES (3,3,3); <> DECLARE v1 proc1.r1.i1%TYPE; r1 t1%ROWTYPE; BEGIN SELECT i1,i2,i3 INTO s.r1.i1, s.r1.i2, s.r1.i3 FROM t1 WHERE i1 = 1; INSERT INTO t1 VALUES(s.r1.i1, s.r1.i2, s.r1.i3); END; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ---------------------------------------- 1 1 1 3 3 3 1 1 1 3 rows selected. 예제 2 저장 프로시저 블록 45 CREATE TABLE t1 (i1 INTEGER, i2 INTEGER, i3 INTEGER); INSERT INTO t1 VALUES(100, 100, 100); CREATE SEQUENCE seq1; CREATE SEQUENCE seq2; CREATE SEQUENCE seq3; CREATE OR REPLACE PROCEDURE proc1 AS BEGIN <> DECLARE nextval INTEGER; BEGIN nextval := 10; INSERT INTO t1 VALUES (seq1.NEXTVAL,0,0); END; END; / CREATE OR REPLACE PROCEDURE proc2 AS BEGIN INSERT INTO t1 VALUES (seq1.NEXTVAL, seq2.NEXTVAL, seq3.NEXTVAL); INSERT INTO t1 VALUES (seq1.NEXTVAL, seq2.NEXTVAL, seq3.NEXTVAL); INSERT INTO t1 VALUES (seq1.NEXTVAL, seq2.NEXTVAL, seq3.NEXTVAL); END; / CREATE OR REPLACE PROCEDURE proc3 AS v1 INTEGER; v2 INTEGER; v3 INTEGER; BEGIN SELECT seq1.currval, seq2.NEXTVAL, seq3.NEXTVAL INTO v1, v2, v3 FROM t1 WHERE i1 = 100; INSERT INTO t1 VALUES (v1, v2, v3); SELECT seq1.currval, seq1.NEXTVAL, seq1.currval INTO v1, v2, v3 FROM t1 WHERE i1 = 100; INSERT INTO t1 VALUES (v1, v2, v3); 46 Stored Procedures Manual SELECT seq1.currval, seq2.NEXTVAL, seq3.NEXTVAL INTO v1, v2, v3 FROM t1 WHERE i1 = 100; INSERT INTO t1 VALUES (v1, v2, v3); END; / EXEC proc1; SELECT * FROM t1; EXEC proc2; SELECT * FROM t1; EXEC proc3; SELECT * FROM t1; EXEC proc2; SELECT * FROM t1; EXEC proc3; iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ---------------------------------------- 100 100 100 10 0 0 1 1 1 2 2 2 3 3 3 3 4 4 4 4 4 4 5 5 5 6 6 6 7 7 7 8 8 7 9 9 8 8 8 8 10 10 14 rows selected. 예제 3 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE TABLE t2(i1 INTEGER, i2 INTEGER, i3 INTEGER); INSERT INTO t1 VALUES (1,1,1); INSERT INTO t1 VALUES (2,2,2); 저장 프로시저 블록 47 CREATE OR REPLACE PROCEDURE proc1 AS v1 INTEGER; r1 t1%ROWTYPE; BEGIN SELECT i1 INTO v1 FROM t1 WHERE i1 = 1; SELECT * INTO r1 FROM t1 WHERE i1 = 1; INSERT INTO t2 VALUES (v1, r1.i2, r1.i3); < > DECLARE r1 t1%ROWTYPE; BEGIN SELECT i1, i2, i3 INTO s.r1.i1, s.r1.i2, s.r1.i3 FROM t1 WHERE i1 = 2; INSERT INTO t2 VALUES (s.r1.i1, s.r1.i2, s.r1.i3); END; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t2; T2.I1 T2.I2 T2.I3 ---------------------------------------- 1 1 1 2 2 2 2 rows selected. 예제 4 CREATE TABLE t3(i1 INTEGER); CREATE OR REPLACE PROCEDURE proc1 AS max_qty orders.qty%TYPE; BEGIN SELECT MAX(qty) INTO max_qty FROM orders; INSERT INTO t3 VALUES(max_qty); END; / 48 Stored Procedures Manual i SQ L > e x e c p r o c 1 ; Ex e c u t e s u c c e s s i SQ L > SE L EC T * F R O M t 3 ; T 3 . I 1 -------------- 10000 1 row selected. 예제 5 CREATE TABLE delayed_processing( cno CHAR(14), order_date DATE); CREATE OR REPLACE PROCEDURE proc1 AS de_cno CHAR(14); de_order_date DATE; BEGIN INSERT INTO delayed_processing SELECT cno, order_date INTO de_cno, de_order_date FROM orders WHERE processing = 'D'; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM delayed_processing; DELAYED_PROCESSING.CNO DELAYED_PROCESSING.ORDER_DATE ----------------------------------------------- 7610011000001 2000/11/29 00:00:00 7001011001001 2000/11/29 00:00:00 2 rows selected. 저장 프로시저 블록 49 할당문 구문 assignment_statement ::= variable_name parameter_name record_name column_name. SET variable_name parameter_name record_name column_name. := = expression; 기능 지역변수, OUT 또는 IN/OUT 형의 인자에 값을 할당하고자 할 때 사용하는 할당문이다. 다음의 두 가지 방법을 사용해서 변수 또는 인자에 값을 할당할 수 있다. “:=” 연산자 사용 variable_name := value; parameter_name := value; SET 키워드 사용 SET variable_name = value; SET parameter_name := value; %ROWTYPE속성을 사용해서 정의된 RECORD 타입 변수의 각 칼럼 값은 record_variable_name.field_name으로 참조할 수 있다. 50 Stored Procedures Manual 예제 예제 1 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE OR REPLACE PROCEDURE proc1 AS i INTEGER; BEGIN i := 5; WHILE i <= 10 LOOP INSERT INTO t1 VALUES (i, i+1, i+2); i := i + 1; END LOOP; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ---------------------------------------- 5 6 7 6 7 8 7 8 9 8 9 10 9 10 11 10 11 12 6 rows selected. 예제 2 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE OR REPLACE FUNCTION plus20(p1 IN INTEGER) RETURN INTEGER AS v1 INTEGER; BEGIN v1 := p1 + 20; RETURN v1; 저장 프로시저 블록 51 END; / CREATE OR REPLACE PROCEDURE proc1 AS v1 INTEGER; in_arg INTEGER; BEGIN in_arg := 80; v1 := plus20(in_arg); INSERT INTO t1 VALUES (v1, v1, v1); END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ---------------------------------------- 100 100 100 1 row selected. 52 Stored Procedures Manual LABEL LABLE문은 저장 프로시저 내부의 특정 위치에 명칭을 지정하는데 사용된다. LABEL은 블록 내에 다음과 같이 지정할 수 있다. << User_defined_label_name >> 기능 사용자가 정의한 LABEL 명은 다음 3가지 경우에 사용된다. 같은 이름의 여러 변수들의 범위를 제한하거나, 변수 이름과 칼럼 이름이 같아서 발생하는 모호성을 없애기 위한 경우 중첩된 LOOP에서 빠져나오고 싶은 경우 GOTO 문장을 사용하는 경우 제약조건 1. 동일 블록 내에 같은 이름의 LABEL이 존재하면 안 된다. 아래 예제의 경우 LABEL1이 동일 블록 내에 두 번 지정되어 컴파일 시 에러가 출력된다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 INTEGER; BEGIN << LABEL1>> V1 := 1; <> V1 := V1 + 1; … 2. 같은 이름을 가지는 변수의 범위를 제한하기 위하여 사용하는 경우, 반드시 DECLARE 문 위에 LABEL을 선언해야 한다. 단, LABEL을 여러 개 선언하는 것은 허용된다. 아래 예제에서는 (1)에서 선언한 변수 V1이 2번 참조된다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 INTEGER; BEGIN < > --- LABLE 지정 저장 프로시저 블록 53 < > DECLARE V1 INTEGER; .......(1) BEGIN < > DECLARE V1 INTEGER; ......(2) BEGIN LABEL1.V1 := 1; -- (1)의 V1 참조 LABEL2.V1 := 2; -- (1)의 V1 참조 LABEL3.V1 := 3; -- (2)의 V1 참조 END; END; END; / 아래 예제의 경우 DECLARE문 위에 LABEL을 지정하지 않아서 에러가 나게 된다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 INTEGER; BEGIN < > V1 := 1; DECLARE V1 INTEGER; BEGIN LABEL1.V1 := 1; --- ERROR. 3. 2의 경우와 유사하게 중첩된 LOOP에서 빠져나올 때 사용하는 경우 반드시 LOOP 시작 직전에 LABEL을 선언해야 한다. LOOP전에 LABEL을 여러 개 선언하는 것은 허용된다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 INTEGER; BEGIN V1 := 0; < > < > FOR I IN 1 .. 10 LOOP V1 := V1 + 1; FOR I IN 1 .. 10 LOOP V1 := V1 + 1; EXIT LABEL1 WHEN V1 = 30; 54 Stored Procedures Manual EN D L O O P; EN D L O O P; EN D ; / 아래 예제는 한 LABEL이 LOOP 시작 직전에 선언되지 않은 경우이다. 이 LABEL을 이용해서는 중첩된 LOOP를 빠져나올 수 없으므로, 저장 프로시저 컴파일 시 에러가 발생하게 된다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 INTEGER; BEGIN < > V1 := 0; < > FOR I IN 1 .. 10 LOOP V1 := V1 + 1; FOR I IN 1 .. 10 LOOP V1 := V1 + 1; EXIT LABEL1 WHEN V1 = 30; -- ERROR END LOOP; END LOOP; END; / 저장 프로시저 블록 55 PRINT 구문 print_statement ::= PRINT PRINTLN ()string; 기능 PRINT구문은 저장 프로시저 실행 시에 사용자가 원하는 텍스트를 해당 프로시저를 호출한 클라이언트에게 출력한다. PRINT 구문은 주로 디버깅 및 테스트 목적으로 사용하도록 알티베이스가 제공하는 시스템 프로시저이다. PRINTLN은 PRINT와 동일하나 출력 메시지의 마지막에 개행 문자 (윈도우는 “\r\n”, 유닉스는 “\n”)를 붙여서 출력한다. PRINT, PRINTLN의 소유자는 SYSTEM_이므로 사용 시 아래처럼 이를 명시해도 된다: SYSTEM_.PRINTLN(‘Hello World’); 그러나 이들에 대한 PUBLIC시노님이 기본적으로 생성되어 있기 때문에 SYSTEM_을 굳이 명시하지 않아도 된다. String 클라이언트로 출력할 문자열을 기술한다. 사용자 메시지 출력 시 문자열과 함께 변수 값 등을 출력하고 싶은 경우에는 예제2와 같이 문자열 연결 연산자인 “||” 를 사용해서 하나의 문자열로 만들어서 출력하면 된다. 56 Stored Procedures Manual 예제 예제1 CREATE OR REPLACE PROCEDURE proc1 AS v1 BIGINT; BEGIN v1 := BIGINT'9223372036854775807'; system_.println ('1'); system_.println (v1); system_.println ('2'); END; / iSQL> EXEC proc1; 1 9223372036854775807 2 Execute success. 예제2 CREATE OR REPLACE PROCEDURE proc1 AS eno_count INTEGER; BEGIN SELECT COUNT(eno) INTO eno_count FROM employee; println('The NUMBER of Employees: ' || eno_count); END; / iSQL> EXEC proc1; The NUMBER of Employees: 20 Execute success. 예제3 다음 예제는 쿼리 결과를 형식에 맞춰 출력하기 위해 PRINT와 PRINTLN구문과 함께 loop를 사용하는 방법을 보여준다. CREATE OR REPLACE PROCEDURE showProcedures AS CURSOR c1 IS 저장 프로시저 블록 57 SELECT SYSTEM_.sys_procedures_.proc_name, decode(SYSTEM_.sys_procedures_.object_TYPE, 0, 'Procedure',1,'Function') FROM system_.sys_procedures_ ; v1 CHAR(40); v2 CHAR(20); BEGIN OPEN c1; SYSTEM_.PRINTLN('--------------------------'); SYSTEM_.PRINT('Proc_Name'); SYSTEM_.PRINTLN(' Procedure/Function'); SYSTEM_.PRINTLN('--------------------------'); LOOP FETCH C1 INTO v1, v2; EXIT WHEN C1%NOTFOUND; PRINT(' '); PRINT(v1); PRINTLN(v2); END LOOP; PRINTLN('------------------------'); CLOSE c1; END; / iSQL> EXEC showProcedures; ----------------------------------------------- Proc_Name Procedure/Function ----------------------------------------------- PRINT Procedure PRINTLN Procedure . . SHOWPROCEDURES Procedure ----------------------------------------------- Execute success. 58 Stored Procedures Manual RETURN 구문 return_statement ::= () ;expressionRETURN 기능 저장 프로시저의 수행을 도중에 중단 하거나, 저장 함수에서 값을 반환하고 수행을 중단하려 하는 경우에 사용하는 제어문이다. 저장 프로시저는 RETURN문에 반환할 값을 지정하게 되면 에러가 발생한다. 반면 저장 함수는 항상 값을 반환해야 하기 때문에 RETURN 문에 반환할 값을 명시하여야 한다. expression 저장 함수의 경우에 반환할 값을 기술한다. 반환 값은 연산식의 형태로도 기술할 수 있다. 예제 예제1 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); INSERT INTO t1 VALUES(1,1,1); CREATE OR REPLACE FUNCTION times_half(p1 IN INTEGER) RETURN INTEGER AS 저장 프로시저 블록 59 BEGIN RETURN p1 / 2; END; / iSQL> SELECT times_half(times_half(8)) FROM t1; TIMES_HALF(TIMES_HALF(8)) ---------------------------- 2 1 row selected. 예제2 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); INSERT INTO t1 VALUES(1,1,1); INSERT INTO t1 VALUES(10,10,10); INSERT INTO t1 VALUES(100,100,100); CREATE OR REPLACE FUNCTION max_all_val RETURN INTEGER AS v1 INTEGER; BEGIN SELECT MAX(i1) INTO v1 FROM t1; RETURN v1; END; / iSQL> SELECT max_all_val FROM t1; MAX_ALL_VAL -------------- 100 100 100 3 rows selected. 예제3 CREATE TABLE t4(i1 INTEGER, i2 INTEGER); INSERT INTO t4 VALUES(3, 0); INSERT INTO t4 VALUES(2, 0); INSERT INTO t4 VALUES(1, 0); INSERT INTO t4 VALUES(0, 0); 60 Stored Procedures Manual C R EAT E O R R EP L AC E F U N C T I O N f u n c _ p l u s _ 1 0 (p 1 I N T EG ER ) R ET U R N I N T EG ER AS BEGIN RETURN p1+10; END; / iSQL> SELECT func_plus_10(i1) FROM t4; FUNC_PLUS_10(I1) ------------------- 13 12 11 10 4 rows selected. 흐름 제어 61 4. 흐름 제어 62 Stored Procedures Manual 개요 이 장은 저장 프로시저 바디 내에서의 흐름 제어 방법을 설명한다. 구문 control_flow_statement ::= if_statement case_statement simple_loop_statement while_loop_statement for_loop_statement exit_statement continue_statement null_statement 저장 프로시저에서 사용할 수 있는 흐름 제어문은 다음과 같다. 조건 분기문인 IF문과 CASE문 조건을 만족할 때 반복 수행하는 LOOP문, WHILE문과 FOR문 반복 수행문의 흐름을 제어하는 EXIT문와 CONTINUE문 아무것도 수행하지 않음을 명시적으로 나타낼 수 있는 NULL문 특정 위치로 이동할 수 있는 GOTO문 제약 사항 부질의(subquery)가 포함된 조건은 IF문 또는 CASE문의 조건으로 사용될 수 없다. 흐름 제어 63 IF 구문 if_statement ::= IFconditionTHENstatement ELS(E)IFconditionTHENstatement END IF ELSEstatement ; 기능 조건을 만족하는 경우와 그렇지 않은 경우에 따라 처리 흐름을 분기하는 조건 분기문이다. IF절은 조건은 검사해서 true이면 THEN절로 제어를 이동하고, false이거나 NULL이면 ELSE절로 제어를 이동한다. condition 조건절에는 SQL문의 WHERE절에서 사용 가능한 모든 술어(predicate)들을 사용할 수 있다. 지원하는 술어들에 대한 상세 내용은 SQL Reference의 SELECT 구문을 참조한다. ELS(E)IF ELS(E)IF절의 경우 이전의 IF문의 조건과는 다른 조건을 명시할 수 있다. 64 Stored Procedures Manual ELSIF는 한 단어이며 하나의 IF문 내에 여러 개의 ELS(E)IF절을 사용할 수 있다. ELSE 앞서IF와 ELS(E)IF에서 열거된 모든 조건을 만족하지 않는 경우 ELSE절의 문장이 수행된다. ELSE절은 명시하거나, 하나의 IF문 내에 한번만 기술 가능하다. 중첩 IF 문 IF문은 다른 IF문 내에 중첩되어 기술할 수 있다. 즉, IF문은 다른 IF, ELS(E)IF 또는 ELSE 문의 결과로 수행되는 일련의 작업 내에 포함될 수 있다. 각 IF 문은 END IF로 종료해야 한다. 예제 예제1 CREATE OR REPLACE PROCEDURE proc1 AS CURSOR c1 IS SELECT eno, emp_job, salary FROM employee; emp_id employee.eno%TYPE; e_job employee.emp_job%TYPE; e_salary employee.salary%TYPE; BEGIN OPEN c1; LOOP FETCH c1 INTO emp_id, e_job, e_salary; EXIT WHEN c1%NOTFOUND; IF e_salary IS NULL THEN IF e_job = 'CEO' THEN e_salary := 5000000; ELSIF e_job = 'MANAGER' THEN e_salary := 4500000; ELSIF e_job = 'ENGINEER' THEN e_salary := 4300000; ELSIF e_job = 'PROGRAMMER' THEN e_salary := 4100000; ELSE e_salary := 4000000; END IF; 흐름 제어 65 UPDATE employee SET salary = e_salary WHERE eno = emp_id; END IF; END LOOP; CLOSE c1; END; / iSQL> SELECT eno, emp_job FROM employee WHERE salary IS NULL; ENO EMP_JOB -------------------------------- 1 CEO 8 MANAGER 20 SALESMAN 3 rows selected. iSQL> EXEC proc1; Execute success. iSQL> SELECT eno, emp_job, salary FROM employee WHERE eno=1 OR eno=8 OR eno=20; ENO EMP_JOB SALARY --------------------------------------------- 1 CEO 5000000 8 MANAGER 4500000 20 SALESMAN 4000000 3 rows selected. 예제2 CREATE TABLE t1 (i1 VARCHAR(20), i2 NUMBER, i3 DATE); CREATE TABLE t2 (i1 VARCHAR(20), i2 NUMBER, i3 DATE); INSERT INTO t1 VALUES ('21-JUL-2001', 2, '01-JUL-2000'); INSERT INTO t2 VALUES (NULL,NULL,'01-FEB-1990'); INSERT INTO t2 VALUES (NULL,NULL,'02-FEB-1990'); CREATE OR REPLACE FUNCTION func2 (p1 IN DATE, p2 IN CHAR(30)) RETURN NUMBER AS BEGIN RETURN (TO_NUMBER(TO_CHAR(p1, 'dd')) + TO_NUMBER(p2)); END; / 66 Stored Procedures Manual C R EAT E O R R EP L AC E F U N C T I O N f u n c 1 (p 1 I N D A T E, p 2 I N D AT E) R ET U R N D A T E AS BEGIN IF p1 >= p2 THEN RETURN add_months(p1, 3); ELSE RETURN add_months(p1, 4); END IF; END; / CREATE OR REPLACE PROCEDURE proc1 AS v1 VARCHAR(20); v2 NUMBER; v3 DATE; BEGIN SELECT i1, func2(TO_DATE(i1), TO_CHAR(i3, 'yyyy')), i3 INTO v1,v2,v3 FROM t1 WHERE i2 = 2; INSERT INTO t2 VALUES (v1,v2,v3); IF v2 not in (2001, 2002, 2003) AND v1 = '21-JUL-2001' THEN UPDATE t2 SET i1 = func1(v1, '17-JUL-2001'), i2 = nvl(i2, 10) WHERE i3 = '01-FEB-1990'; UPDATE t2 SET i1 = func1(v1, '27-JUL-2001'), i2 = nvl(i2, 10*2) WHERE i3 = '02-FEB-1990'; END IF; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t2; T2.I1 T2.I2 T2.I3 ----------------------------------------------- 21-JUL-2001 2021 2000/07/01 00:00:00 흐름 제어 67 21-OCT-01 10 1990/02/01 00:00:00 21-NOV-01 20 1990/02/02 00:00:00 3 rows selected. 예제3 CREATE TABLE payroll( eno INTEGER, bonus NUMBER(10, 2)); CREATE OR REPLACE PROCEDURE proc1 AS BEGIN DECLARe CURSOR c1 IS SELECT DISTINCT(eno), SUM(qty) FROM orders GROUP BY eno; emp_id orders.eno%TYPE; sum_qty orders.qty%TYPE; bonus NUMBER(10, 2); BEGIN OPEN c1; IF c1%ISOPEN THEN LOOP FETCH c1 INTO emp_id, sum_qty; EXIT WHEN c1%NOTFOUND; IF sum_qty > 25000 THEN bonus := 1000; ELSIF sum_qty > 15000 THEN bonus := 500; ELSE bonus := 200; END IF; INSERT INTO payroll VALUES(emp_id, bonus); END LOOP; END IF; END; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT DISTINCT(eno), SUM(qty) sum FROM orders GROUP BY eno; ENO SUM 68 Stored Procedures Manual ------------------------------------ 12 17870 19 25350 20 13210 3 rows selected. iSQL> SELECT * FROM payroll; PAYROLL.ENO PAYROLL.BONUS ----------------------------- 12 500 19 1000 20 200 3 rows selected. 흐름 제어 69 CASE 구문 case_statement_1 ::= CASEWHENconditionTHENstatement ELSEstatement ENDCASE; case_statement_2::= CASEcase_variableWHENwhen_valueTHENstatement ELSEstatement ENDCASE; 기능 특정 변수의 값에 따라서 처리 경로를 결정하는 조건 분기문이다. IF문과 동일한 기능이지만 CASE문을 사용하면 프로그램의 가독성을 높일 수 있다. CASE문은 위의 다이어그램에서 보여 주 듯이 다음 두 가지가 있다. case_statement_1: 조건식이 참일 때에 특정한 문장을 수행하는 방식 70 Stored Procedures Manual case_statemen_2: 하나의 변수가 특정한 값이 되었을 때에 특정한 문장을 수행하는 방식 단, 하나의 CASE문에서 이 두 가지 방식을 혼용할 수 없다. CASE 절을 모두 만족하지 못하면 ELSE절의 문장을 수행하게 되며, ELSE절이 없는 경우에는 어떠한 문장도 수행하지 않는다. condition 검사할 조건을 기술한다. SELECT 구문의 WHERE절과 같이 술어 문으로 명시한다. case_variable 저장 프로지저의 처리를 분기시키는 기준이 되는 변수명을 기술한다. when_value case_variable 변수와 비교할 실제 상수 값을 기술한다. ELSE CASE 조건식이 모두 거짓이 되었을 때 수행해야 할 처리 구문을 ELSE 절에 기술한다. ELSE절은 없어도 상관 없으며 하나의 CASE문에 한번 사용 가능하다. 조건식이 모두 거짓이 되었음에도 ELSE절이 없을 경우 CASE문은 어떠한 문장도 수행하지 않고 지나간다. 예제 예제1 CREATE OR REPLACE PROCEDURE proc1 AS CURSOR c1 IS SELECT eno, emp_job, salary FROM employee; emp_id employee.eno%TYPE; e_job employee.emp_job%TYPE; e_salary employee.salary%TYPE; BEGIN OPEN c1; LOOP 흐름 제어 71 FETCH c1 INTO emp_id, e_job, e_salary; EXIT WHEN c1%NOTFOUND; IF e_salary IS NULL THEN CASE WHEN e_job = 'CEO' THEN e_salary := 5000000; WHEN e_job = 'MANAGER' THEN e_salary := 4500000; WHEN e_job = 'ENGINEER' THEN e_salary := 4300000; WHEN e_job = 'PROGRAMMER' THEN e_salary := 4100000; ELSE e_salary := 4000000; END CASE; UPDATE employee SET salary = e_salary WHERE eno = emp_id; END IF; END LOOP; CLOSE c1; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT eno, emp_job, salary FROM employee WHERE eno=1 OR eno=8 OR eno=20; ENO EMP_JOB SALARY --------------------------------------------- 1 CEO 5000000 8 MANAGER 4500000 20 SALESMAN 4000000 3 rows selected. 예제2 @schema.sql CREATE OR REPLACE PROCEDURE proc1 AS CURSOR c1 IS SELECT eno, emp_job, salary FROM employee; emp_id employee.eno%TYPE; e_job employee.emp_job%TYPE; e_salary employee.salary%TYPE; BEGIN OPEN c1; 72 Stored Procedures Manual LOOP FETCH c1 INTO emp_id, e_job, e_salary; EXIT WHEN c1%NOTFOUND; IF e_salary IS NULL THEN CASE e_job WHEN 'CEO' THEN e_salary := 5000000; WHEN 'MANAGER' THEN e_salary := 4500000; WHEN 'ENGINEER' THEN e_salary := 4300000; WHEN 'PROGRAMMER' THEN e_salary := 4100000; ELSE e_salary := 4000000; END CASE; UPDATE employee SET salary = e_salary WHERE eno = emp_id; END IF; END LOOP; CLOSE c1; END; / iSQL> SELECT eno, emp_job FROM employee WHERE salary IS NULL; ENO EMP_JOB -------------------------------- 1 CEO 8 MANAGER 20 SALESMAN 3 rows selected. iSQL> EXEC proc1; Execute success. iSQL> SELECT eno, emp_job, salary FROM employee WHERE eno=1 OR eno=8 OR eno=20; ENO EMP_JOB SALARY --------------------------------------------- 1 CEO 5000000 8 MANAGER 4500000 20 SALESMAN 4000000 3 rows selected. 흐름 제어 73 LOOP 구문 label_name<<>> LOOPstatement END LOOP label_name ; loop_statement:= 기능 LOOP구문은 조건을 따로 지정하지 않고 반복적으로 구문(들)을 수행하고자 하는 경우에 사용된다. 그러나 LOOP 구문에서 EXIT문을 사용하지 않게 되면 무한 LOOP에 빠져서 시스템에 문제를 일으킬 수 있으므로 주의한다. 예제 CREATE TABLE item(id INTEGER, counter NUMBER(2)); CREATE OR REPLACE PROCEDURE proc1 AS BEGIN DECLARE v_id item.id%TYPE := 501; v_counter NUMBER(2) := 1; BEGIN LOOP INSERT INTO item VALUES(v_id, v_counter); 74 Stored Procedures Manual v _ c o u n t e r : = v _ c o u n t e r + 1 ; EX I T W H EN v _ c o u n t e r > 1 0 ; EN D L O O P; END; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM item; ITEM.ID ITEM.COUNTER ---------------------------- 501 1 501 2 … 501 9 501 10 10 rows selected. 흐름 제어 75 WHILE LOOP 구문 while_loop_statement ::= label_name<<>> LOOPcondition label_name ; WHILE END LOOPstatement 기능 조건이 참인 경우만 LOOP을 수행하고자 할 때 사용하는 반복문이다. 만약 처음부터 이 조건이 참이 아니면, WHILE문은 한 번도 수행되지 않는다. condition LOOP을 수행할 지의 여부를 결정하는 조건절을 명시한다. 조건절에는 SQL문의 WHERE절에서 사용 가능한 모든 술어(predicate)들을 사용할 수 있다. 예제 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE OR REPLACE PROCEDURE proc1 AS v1 INTEGER; BEGIN v1 := 1; 76 Stored Procedures Manual WHILE v1 < 3 LOOP v1 := v1 + 1; INSERT INTO t1 VALUES (v1, v1, v1); IF v1 = 2 THEN CONTINUE; END IF; END LOOP; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ---------------------------------------- 2 2 2 3 3 3 2 rows selected. 흐름 제어 77 FOR LOOP 구문 for_loop_statement ::= label_name<< INcounter_nameFOR >> REVERSE lower_bound..upper_bound STEPstep_size LOOP statementEND LOOP label_name ; 기능 FOR LOOP문은 일정 횟수만큼 반복해서 구문(들)을 수행하고자 할 때 사용된다. 두 개의 점 (“..”)을 사용해서 범위를 지정할 수 있는데 이는 FOR loop에 진입하기 전에 한 번만 체크된다. 범위의 작은 값과 큰 값을 같은 값으로 지정하면, loop내부는 한 번만 실행된다. counter_name 일정한 값만큼 증가, 혹은 감소시킬 정수형 변수를 하나 기술한다. 이 변수는 블록의 선언부에 선언할 필요가 없다. 또한 이 변수의 범위는 LOOP과 END LOOP으로 둘러싸여 있는 문장들에만 한정된다. FOR LOOP내에서 이 변수에 새로운 값을 할당할 수 없다. REVERSE 78 Stored Procedures Manual REVERSE 모드를 지정하면 counter_name 변수의 값을 upper_bound에서 lower_bound까지 감소시키면서 FOR 문을 수행한다. lower_bound counter_name 변수가 가질 수 있는 값 중 가장 작은 값이다. 정수형 또는 정수형과 호환성이 있는 표현식을 지정하여야 한다. 여기에는 지역변수를 사용할 수도 있으나, 이 값은 FOR 문이 맨 처음으로 실행될 때에 딱 한번 그 값을 계산하여 저장해 두고 사용하므로, 추후 해당 지역변수를 변경하여도 FOR문의 동작에는 영향을 미치지 않는다. 여기에 실수형을 명시하면 반올림된 정수값으로 변환된다. upper_bound counter_name 변수가 가질 수 있는 값 중 가장 큰 값이다. lower_bound와 마찬가지로 정수형 또는 정수형과 호환성이 있는 표현식을 지정하여야 한다. 정수형이 아닌 수를 명시하면 반올림된 정수값으로 변환된다. 만약 FOR 문이 맨 처음으로 수행될 때 upper_bound가 lower_bound보다 작으면 어떠한 에러도 내지 않고 단지 FOR 문을 건너 뛰고 다음 문장을 수행한다. lower_bound 처럼, 여기에는 지역변수를 사용할 수도 있으나, 이 값은 FOR 문이 맨 처음으로 실행될 때에 딱 한번 그 값을 계산하여 저장해 두고 사용하므로, 추후 해당 지역변수를 변경하여도 FOR문의 동작에는 영향을 미치지 않는다. step_size 증가 또는 감소시킬 값을 지정한다. 생략하면 기본적으로 FOR 문은 counter_name 변수를 1씩 증, 혹은 감소 시킨다. 단, step size는 1보다 작을 수 없다. 정수형이 아닌 수를 명시한 경우에는 반올림된 정수값으로 변환된다. 예제 --###################### -- FOR LOOP --###################### 흐름 제어 79 예제1 CREATE TABLE t6(i1 INTEGER, sum INTEGER); CREATE OR REPLACE PROCEDURE proc1 AS v1 INTEGER; sum INTEGER := 0; BEGIN FOR i IN 1 .. 50 LOOP v1 := 2 * i - 1; sum := sum + v1; INSERT INTO t6 VALUES(v1, sum); END LOOP; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t6; T6.I1 T6.SUM --------------------------- 1 1 3 4 5 9 … 97 2401 99 2500 50 rows selected. 예제2 CREATE OR REPLACE PROCEDURE proc1 AS eno_count INTEGER; BEGIN SELECT COUNT(eno) INTO eno_count FROM employee; FOR i IN 1 .. eno_count LOOP UPDATE employee SET salary = salary * 1.2 WHERE eno = i; END LOOP; END; / iSQL> SELECT eno, salary FROM employee WHERE eno in (11,12,13); ENO SALARY 80 Stored Procedures Manual --------------------------- 11 2750000 12 1890000 13 980000 3 rows selected. iSQL> EXEC proc1; Execute success. iSQL> SELECT eno, salary FROM employee WHERE eno IN (11,12,13); ENO SALARY --------------------------- 11 3300000 12 2268000 13 1176000 3 rows selected. 예제3 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE OR REPLACE PROCEDURE proc1 AS BEGIN <> INSERT INTO t1 VALUES (1,1,1); IF 1 = 1 THEN NULL; END IF; <> FOR v1 IN 1 .. 3 LOOP < > FOR v1 IN 1 .. 3 LOOP INSERT INTO t1 VALUES (b.v1, b.v1, c.v1); END LOOP; END LOOP; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ---------------------------------------- 1 1 1 흐름 제어 81 1 1 1 1 1 2 1 1 3 2 2 1 2 2 2 2 2 3 3 3 1 3 3 2 3 3 3 10 rows selected. --##################### -- reverse --##################### CREATE TABLE t6(i1 INTEGER, sum INTEGER); CREATE OR REPLACE PROCEDURE proc1 AS sum INTEGER := 0; BEGIN FOR i IN reverse 1 .. 100 LOOP sum := sum + i; INSERT INTO t6 VALUES(i, sum); END LOOP; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t6; T6.I1 T6.SUM --------------------------- 100 100 99 199 98 297 … 3 5047 2 5049 1 5050 100 rows selected. --##################### -- step 82 Stored Procedures Manual --##################### CREATE TABLE t6(i1 INTEGER, sum INTEGER); CREATE OR REPLACE PROCEDURE proc1 AS sum INTEGER := 0; BEGIN FOR i IN 1 .. 100 STEP 2 LOOP sum := sum + i; INSERT INTO t6 VALUES(i, sum); END LOOP; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t6; T6.I1 T6.SUM --------------------------- 1 1 3 4 5 9 … 97 2401 99 2500 50 rows selected. 흐름 제어 83 EXIT 구문 EXIT exit_statement ::= label_nameWHENcondition ; 기능 EXIT문을 감싸고 있는 가장 가까운 LOOP 문을 빠져나간다. 그러나 label_name이 명시적으로 주어진 경우에는 label_name이 정의된 LOOP을 빠져나간다. LOOP문 내부가 아닌 다른 블록에서 EXIT문을 사용하면 오류가 발생한다. < > LOOP ... LOOP ... EXIT outer WHEN ... -- EXIT both LOOPs END LOOP; ... END LOOP outer; EXIT WHEN count > 100; IF count > 100 THEN EXIT; END IF; EXIT문은 아래의 LOOP문 내에서 사용 가능하다. 84 Stored Procedures Manual LOOP WHILE LOOP FOR LOOP CURSOR FOR LOOP label_name EXIT문을 감싸고 있는 가장 가까운 LOOP가 아닌 그보다 더 바깥의 LOOP으로 빠져나가야 하는 경우, 해당 LOOP의 바로 앞에 LABEL을 정의하고 그 이름을 여기에 명시한다. WHEN condition 특정 조건이 참인 경우 루프를 빠져나갈 수 있도록 WHEN 절에 조건식을 지정할 수 있다. 조건절에는 SELECT문의 WHERE절에서 사용 가능한 모든 술어들을 사용할 수 있다. EXIT 문을 만나서 WHEN 절에 명시된 조건이 참이면 저장 프로시저는 가장 가까운 루프나 지정된 LABEL 블록문을 빠져 나가서 그 다음 문장을 수행한다. EXIT WHEN은 간단한 IF 구문과 유사하다. 다음의 두 문장들은 동일한 기능을 한다. EXIT WHEN count > 100; IF count > 100 THEN EXIT; END IF; 예제 CREATE TABLE stock( gno BYTE(5) primary key, stock INTEGER, price numeric(10,2)); CREATE OR REPLACE PROCEDURE proc1 AS CURSOR c1 IS SELECT gno, stock, price FROM goods; rec1 c1%ROWTYPE; BEGIN OPEN c1; LOOP 흐름 제어 85 FETCH c1 INTO rec1; IF c1%found THEN IF rec1.stock > 0 AND rec1.stock < 1000 THEN INSERT INTO stock VALUES(rec1.gno, rec1.stock, rec1.price); END IF; ELSIF c1%NOTFOUND THEN EXIT; END IF; END LOOP; CLOSE c1; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM stock; STOCK.GNO STOCK.STOCK STOCK.PRICE ---------------------------------------- A111100002 100 98000 B111100001 780 35800 D111100003 650 45100 E111100001 900 2290.54 E111100006 900 2338.62 5 rows selected. --##################### -- EXIT WHEN --##################### CREATE OR REPLACE PROCEDURE proc1 AS CURSOR c1 IS SELECT gno, stock, price FROM goods; rec1 c1%ROWTYPE; BEGIN OPEN c1; IF c1%ISOPEN THEN LOOP FETCH c1 INTO rec1; EXIT WHEN c1%NOTFOUND; IF rec1.stock > 0 AND rec1.stock < 1000 THEN INSERT INTO stock VALUES(rec1.gno, rec1.stock, rec1.price); END IF; END LOOP; END IF; 86 Stored Procedures Manual CLOSE c1; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM stock; STOCK.GNO STOCK.STOCK STOCK.PRICE ---------------------------------------- A111100002 100 98000 B111100001 780 35800 D111100003 650 45100 E111100001 900 2290.54 E111100006 900 2338.62 5 rows selected. 흐름 제어 87 CONTINUE 구문 CONTINUE continue_statement ::= ; 기능 현재 CONTINUE문을 감싸고 있는 LOOP에서 CONTINUE문 이후의 문장들을 전부 무시하고 LOOP의 맨 처음으로 되돌아간다. CONTINUE문을 사용할 수 있는 LOOP문은 다음과 같다. LOOP WHILE LOOP FOR LOOP CURSOR FOR LOOP LOOP문 내부가 아닌 다른 곳에서 CONTINUE문을 사용하면 오류가 발생한다. 예제 CREATE TABLE t8(i1 INTEGER, mathpower INTEGER default 0); INSERT INTO t8(i1) VALUES(7); INSERT INTO t8(i1) VALUES(3); INSERT INTO t8(i1) VALUES(20); INSERT INTO t8(i1) VALUES(15); INSERT INTO t8(i1) VALUES(6); INSERT INTO t8(i1) VALUES(1); INSERT INTO t8(i1) VALUES(9); 88 Stored Procedures Manual CREATE OR REPLACE PROCEDURE proc1 AS BEGIN DECLARE CURSOR c1 IS SELECT i1 FROM t8; rec c1%ROWTYPE; BEGIN OPEN c1; LOOP FETCH c1 INTO rec; EXIT WHEN c1%NOTFOUND; IF power(rec.i1, rec.i1) > 50000 THEN continue; ELSE UPDATE t8 SET mathpower = power(rec.i1, rec.i1) WHERE i1 = rec.i1; END IF; END LOOP; CLOSE c1; END; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t8; T8.I1 T8.MATHPOWER ---------------------------- 7 0 20 0 15 0 9 0 3 27 6 46656 1 1 7 rows selected. 흐름 제어 89 GOTO 구문 goto_statement ::= GOTOlabel_name; 기능 지정된 LABEL로 제어를 이동하는 분기문이다. label_name 제어를 이동할 LABEL의 이름이다. 제약 사항 GOTO는 다음과 같은 제약사항을 가진다. IF 나 CASE 블록 내에서 사용될 때, THEN, ELS(E)IF, ELSE 또는 WHEN 문에 해당하는 블록 안으로 이동할 수 없다. 따라서 아래 예제의 경우 에러를 출력한다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 INTEGER; BEGIN V1 := 1; IF V1 = 1 THEN GOTO LABEL1; ELSE < > PRINTLN(V1); END IF; END; 90 Stored Procedures Manual / [ ER R - 3 1 2 0 F : I l l e g a l G O T O s t a t e m e n t . I n PR O C 1 0 0 0 7 : G O T O L AB EL 1 ; ^ ^ ] 외부 블록에서 내부 블록으로 이동할 수 없다. 모든 BEGIN/END 블록 및 LOOP 문장에 이 제약 조건이 적용된다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 INTEGER; BEGIN V1 := 1; DECLARE V2 INTEGER; BEGIN < > V2 := 1; END; GOTO LABEL1; END; / [ERR-3120F : Illegal GOTO statement. In PROC1 0012 : GOTO LABEL1; ^ ^ ] 예외 처리부에서 분기하는 경우, 그 예외 처리부가 속한 블록으로는 분기할 수 없다. 따라서 다음의 예제1은 에러를 반환한다. <예제 1> CREATE OR REPLACE PROCEDURE PROC1 AS E1 EXCEPTION; BEGIN RAISE E1; < > PRINTLN('END'); EXCEPTION WHEN E1 THEN GOTO LABEL1; END; 흐름 제어 91 / [ERR-3120F : Illegal GOTO statement. In PROC1 0010 : GOTO LABEL1; ^ ^ ] 그러나, 예외 처리부로부터 그 상위 블록으로의 분기는 가능하다. 예제2 의 경우 V1이 5가 될 때까지 EXCEPTION을 4번 발생시키고 정상으로 종료한다. <예제 2> CREATE OR REPLACE PROCEDURE PROC1 AS E1 EXCEPTION; V1 INTEGER; BEGIN V1 := 1; < > V1 := V1 + 1; PRINTLN('BLOCK1'); BEGIN PRINTLN('BLOCK2'); PRINTLN(V1); IF V1 = 5 THEN PRINTLN('goto label2 '||v1); GOTO LABEL2; ELSE RAISE E1; END IF; EXCEPTION WHEN E1 THEN PRINTLN('goto label1 '|| v1); GOTO LABEL1; END; < > PRINTLN('BLOCK1 AFTER BLOCK2'); END; / iSQL> EXEC PROC1; BLOCK1 BLOCK2 2 92 Stored Procedures Manual g o t o l a b e l 1 2 BL O C K 1 BL O C K 2 3 g o t o l a b e l 1 3 BL O C K 1 BL O C K 2 4 g o t o l a b e l 1 4 BL O C K 1 BL O C K 2 5 g o t o l a b e l 2 5 BL O C K 1 AF T ER BL O C K 2 Ex e c u t e s u c c e s s . 흐름 제어 93 NULL 구문 null_statement ::= NULL; 기능 NULL문은 흐름에 영향을 미치지 않고 아무것도 수행하지 않고 다음으로 넘어감을 명시적으로 나타내기 위해서 사용한다. 프로그램의 가독성을 높이기 위해서 사용한다. 예제 CREATE OR REPLACE PROCEDURE bonus (amount NUMBER(10,2)) AS CURSOR c1 IS SELECT eno, sum(qty) FROM orders group by eno; order_eno orders.eno%TYPE; order_qty orders.qty%TYPE; BEGIN OPEN c1; LOOP FETCH c1 INTO order_eno, order_qty; EXIT WHEN c1%NOTFOUND; IF order_qty > 20000 THEN UPDATE employee SET salary = salary + amount WHERE eno = order_eno; ELSE NULL; END IF; END LOOP; 94 Stored Procedures Manual CLOSE c1; END; / iSQL> SELECT e.eno, salary, sum(qty) FROM employee e, orders o WHERE e.eno = o.eno group by e.eno, salary; ENO SALARY SUM(QTY) ----------------------------------------------- 12 1890000 17870 19 1800000 25350 20 13210 3 rows selected. iSQL> EXEC bonus(55550); Execute success. iSQL> SELECT eno, salary FROM employee WHERE eno = 19; ENO SALARY --------------------------- 19 1855550 1 row selected. 저장 프로시저 내에서 테이블의 레코드를 읽어 오는 방법에는 SELECT INTO문을 사용하는 방법과 커서를 사용하는 방법이 있다. 커서 95 5. 커서 이 장은 커서를 관리하고 사용하는 방법을 설명한다. 96 Stored Procedures Manual 커서의 개요 저장 프로시저 내에서 테이블의 레코드를 읽어 오는 방법에는 SELECT INTO문을 사용하는 방법과 커서를 사용하는 방법이 있다. SELECT INTO문의 경우 결과 레코드의 수는 반드시 한 개여야 하고 그 이외의 경우 오류가 발생한다. 따라서 여러 개의 레코드를 검색해야 할 경우 커서를 사용해야 한다. 커서 선언 커서는 저장 프로시저 블록의 선언부에 커서명과 함께 수행할 SELECT문을 정의해야 한다. 이는 선언된 후에, 아래 2가지 방법 중의 하나로 관리될 수 있다. OPEN, FETCH, CLOSE 사용 Cursor FOR LOOP 사용 OPEN, FETCH와 CLOSE로 커서 관리하기 커서는 블록 바디 내에서 OPEN, FETCH, CLOSE 문을 사용해서 관리할 수 있다. OPEN문은 커서를 초기화하는데 사용된다. FETCH문은 레코드를 반복적으로 가져오는데 사용된다. 마지막으로 CLOSE문은 커서를 해제한다. OPEN 커서를 사용하기 위해서 커서와 관련된 모든 리소스들을 초기화 하는 단계이다. 커서 정의 시에 사용자 파라미터를 지정한 경우 OPEN 구문에서 파라미터의 값을 전달해야 한다. FETCH 커서의 SELECT문을 만족하는 결과 집합으로부터 한 번에 한 레코드씩 가져와서 사용자 변수에 저장한다. 별도의 변수에 각 칼럼을 저장할 수도 있고, 전체 레코드를 %ROWTYPE을 사용해서 정의한 RECORD 타입 변수에 저장할 수도 있다. RECORD 타입 변수에 대한 설명은 6장 사용자 정의 타입으 참고하기 바란다. 커서 97 CLOSE 사용이 끝난 커서의 리소스를 해제한다. OPEN문을 사용해서 열어둔 커서는 해당 프로시저가 종료되기 전에 반드시 CLOSE문을 사용해서 닫아야 한다. Cursor FOR LOOP 로 커서 관리하기 커서의 OPEN, FETCH, CLOSE 단계를 한번에 수행하는 LOOP문의 하나이다. 결과 레코드가 존재하지 않을 때까지 LOOP를 반복 수행한다. 커서에 대해서 명시적으로 OPEN문이나 CLOSE문을 사용할 필요가 없는 경우에 편리한 구문이다. 98 Stored Procedures Manual CURSOR 구문 cursor_declaration ::= CURSORcursor_name cursor_parameter_declaration() , ISselect_statement; cursor_parameter_declaration ::= parameter_name IN data_type DEFAULT := cursor_name data_type ::= sql_data_type type_attribute 기능 커서를 정의한다. CURSOR구문에서는 커서명과 커서가 레코드를 가져오는 데 사용할 SELECT문을 명시해야 한다. cursor_name 커서 99 OPEN, FETCH, CLOSE 및 Cursor FOR LOOP에서 참조할 커서의 이름을 지정한다. cursor_parameter_declaration 커서의 SELECT문에 인자가 필요한 경우, 프로시저의 파라미터를 지정하는 문법과 같은 방법으로 파라미터들을 지정할 수 있다. 이 인자들은 다음과 같은 제약이 있다. 커서 인자는 SELECT문 안에서만 사용할 수 있다 %ROWTYPE 은 사용할 수 없다. 커서 인자는 OUT 또는 IN/OUT 인자일 수 없다. 이 파라미터들은 커서 OPEN이나 커서 FOR문으로부터 값을 받아서 SELECT문을 수행하게 된다. DECLARE CURSOR c1 IS SELECT empno, ename, job, sal FROM emp WHERE sal > 2000; CURSOR c2 (low INTEGER DEFAULT 0, high INTEGER DEFAULT 99) IS SELECT ......; data_type 3장의 “지역 변수 선언” 절을 참고한다. 예제 CREATE TABLE highsal (eno INTEGER, ename CHAR(20), salary NUMBER(10,2)); CREATE OR REPLACE PROCEDURE proc1 AS BEGIN DECLARE CURSOR c1 IS SELECT eno, ename, salary FROM employee WHERE salary IS not NULL ORDER BY salary desc; emp_name CHAR(20); emp_no INTEGER; 100 Stored Procedures Manual e m p _ s a l N U M B ER (1 0 , 2 ); BEGIN OPEN c1; FOR i IN 1 .. 5 LOOP FETCH c1 INTO emp_no, emp_name, emp_sal; EXIT WHEN c1%NOTFOUND; INSERT INTO highsal VALUES(emp_no, emp_name, emp_sal); END LOOP; CLOSE c1; END; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM highsal; HIGHSAL.ENO HIGHSAL.ENAME HIGHSAL.SALARY ---------------------------------------------- 10 YHBAE 4000000 11 MSKIM 2750000 5 SJKIM 2500000 16 JHCHOI 2300000 14 KCJUNG 2003000 5 rows selected. 커서 101 OPEN 구문 open_cursor_statement ::= OPENcursor_name ()cursor_parameter_name ; , 기능 이 구문은 커서를 초기화하고, 쿼리를 수행하여 결과 집합을 결정하는 데 사용된다. 이 후에 FETCH문을 사용해서 데이터를 가져올 수 있다. 이 구문을 실행하면 알티베이스는 커서 사용에 필요한 모든 리소스들을 할당한다. 이미 OPEN된 커서를 다시 OPEN하려고 시도하면 CURSOR_ALREADY_OPEN 에러가 발생한다. cursor_name OPEN 하고자 하는 커서의 이름을 지정한다. 이 커서는 블록의 선언부에 선언되어 있어야 한다. cursor_parameter_name 커서에 사용자 파라미터가 명시된 경우 전달할 값을 지정한다. 인자를 가지는 커서의 경우, 아래와 같이 선언한다. DECLARE CURSOR c1(pname VARCHAR(40), pno INTEGER) IS SELECT empno, ename, job, sal FROM emp WHERE eame = pname; 102 Stored Procedures Manual BEG I N O PEN c 1 ; ...... END; 인자가 존재하는 경우 다음과 같이 커서 OPEN시 값을 넘겨준다. OPEN c1(emp_name, 100); OPEN c1('mylee', 100); OPEN c1(emp_name, dept_no); 예제 예제1 CREATE TABLE mgr (mgr_eno INTEGER, mgr_name CHAR(20), mgr_dno SMALLINT); CREATE OR REPLACE PROCEDURE proc1 AS BEGIN DECLARE CURSOR emp_cur IS SELECT eno, ename, dno FROM employee WHERE emp_job = 'MANAGER'; emp_no employee.eno%TYPE; emp_name employee.ename%TYPE; emp_dno employee.dno%TYPE; BEGIN OPEN emp_cur; LOOP FETCH emp_cur INTO emp_no, emp_name, emp_dno; EXIT WHEN emp_cur%NOTFOUND; INSERT INTO mgr VALUES(emp_no, emp_name, emp_dno); END LOOP; CLOSE emp_cur; END; END; / iSQL> EXEC proc1; Execute success. 커서 103 iSQL> select * from mgr; MGR.MGR_ENO MGR.MGR_NAME MGR.MGR_DNO ----------------------------------------------- 7 HJMIN 4002 8 JDLEE 4001 16 JHCHOI 1001 3 rows selected. 예제2 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE TABLE t2(i1 INTEGER, i2 INTEGER, i3 INTEGER); INSERT INTO t1 VALUES(1,1,1); INSERT INTO t1 VALUES(2,2,2); INSERT INTO t1 VALUES(30,30,30); INSERT INTO t1 VALUES(50,50,50); CREATE OR REPLACE PROCEDURE proc1 AS CURSOR c1(k1 INTEGER, k2 INTEGER, k3 INTEGER) IS SELECT * FROM t1 WHERE i1 <= k1 AND i2 <= k2 AND i3 <= k3; BEGIN FOR rec1 IN c1(2,2,2) LOOP INSERT INTO t2 VALUES (rec1.i1, rec1.i2, rec1.i3); END LOOP; END; / iSQL> SELECT * FROM t2; T2.I1 T2.I2 T2.I3 ---------------------------------------- No rows selected. iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t2; T2.I1 T2.I2 T2.I3 ---------------------------------------- 1 1 1 2 2 2 2 rows selected. 104 Stored Procedures Manual FETCH 구문 fetch_statement ::= FETCHcursor_nameINTO record_name variable_name ; , 기능 OPEN된 커서로부터 하나의 행을 가져와서 INTO절에 명시된 변수에 그 값을 저장한다. 이 때, 커서의 SELECT문에 명시한 칼럼들에 상응하는 사용자변수를 각각 나열해서 칼럼 별로 각각 저장하거나, RECORD 타입 변수명만 명시해서 커서로부터 가져온 한 행을 RECORD 타입 변수에 저장되도록 할 수도 있다. FETCH문에 RECORD타입 변수를 사용하는 데는 다음과 같은 제약이 있다. 가져온 한 행을 저장하는데 오직 한 개의 RECORD 타입 변수만 사용할 수 있다. SELECT문에서 가져오는 모든 칼럼들을 한 RECORD 타입 변수에 저장할 수 있어야 한다. RECORD 타입 변수는 일반 변수와 섞어서 사용할 수 없다. 만약 OPEN되지 않은 커서로부터 FETCH를 하려고 시도하면 INVALID_CURSOR 오류가 발생한다. cursor_name 커서 105 FETCH 하고자 하는 커서의 이름을 지정한다. 이 커서는 블록의 선언부에 선언되어 있어야 한다. record_name 커서의 SELECT 문이 반환하는 레코드를 받을 RECORD 타입 변수명을 지정한다. 이때 사용되는 RECORD 타입 변수는 SEELCT구문의 select list와 칼럼의 개수가 같고 타입이 순서대로 정확히 일치해야 한다. 따라서 커서의 SELECT문이 한 테이블의 모든 칼럼을 가져오는 경우, 그 테이블에 대해 %ROWTYPE속성을 사용해서 선언한 RECORD 타입 변수를 주로 사용하게 된다. variable_name 값을 저장할 변수의 이름이다. 이 변수들의 개수는 커서의 SELECT 문에서 명시한 칼럼의 개수와 동일해야 한다. 또한 나열한 변수의 순서대로 상응하는 칼럼의 타입과 각각 일치해야 한다. LOOP FETCH c1 INTO my_name, my_empno, my_deptno; EXIT WHEN c1%NOTFOUND; END LOOP; 예제 CREATE TABLE emp_temp(eno INTEGER, ename CHAR(20)); CREATE OR REPLACE PROCEDURE proc1 AS BEGIN DECLARE CURSOR c1 IS SELECT eno, ename FROM employee; emp_rec c1%ROWTYPE; BEGIN OPEN c1; LOOP FETCH c1 INTO emp_rec; EXIT WHEN c1%NOTFOUND; INSERT INTO emp_temp VALUES(emp_rec.eno, emp_rec.ename); END LOOP; CLOSE c1; END; 106 Stored Procedures Manual EN D ; / i SQ L > EX EC p r o c 1 ; Ex e c u t e s u c c e s s . i SQ L > SE L EC T * F R O M e m p _ t e m p ; EM P_ T E M P . EN O E M P_ T EM P. EN A M E -------------------------------------- 1 EJJUNG 2 HJNO 3 HSCHOI ... 19 KMKIM 20 DIKIM 20 rows selected. 커서 107 CLOSE 구문 close_cursor_statement ::= CLOSEcursor_name; 기능 열려있는 커서를 닫고 해당 커서에 할당된 리소스를 해제한다. 커서가 열려있지 않았거나, 이미 닫힌 커서를 닫으려고 하면 INVALID_CURSOR 에러가 발생한다. 사용자가 커서를 명시적으로 CLOSE하지 않아도 커서가 선언된 블록을 빠져나가게 되면 자동으로 CLOSE 되지만, 커서를 CLOSE 한다는 것은 커서와 관련된 모든 리소스를 시스템에 반납한다는 것을 의미하므로, 사용이 끝난 커서는 즉시 CLOSE해주는 것이 바람직하다. cursor_name CLOSE하고자 하는 커서의 이름을 명시한다. 예제 CLOSE c1; 108 Stored Procedures Manual Cursor FOR LOOP 구문 cursor_for_loop_statement ::= label_name<< INcounter_nameFOR >> cursor_name ()cursor_parameter_name , LOOP label_name ;END LOOPstatement 기능 커서 열기, 결과 가져오기와 커서 닫기를 자동으로 처리하는 기능을 한다. Cursor FOR LOOP은 블록 선언부에 선언한 커서를 이용하여, 한 번의 LOOP를 수행할 때마다 쿼리 실행 결과를 한 행씩 반환한다. 이 때 하나의 행은 RECORD타입의 변수에 저장되고 루프안에서 이를 자유롭게 사용할 수 있다. label_ name EXIT문이나 CONTINUE문에서 LOOP을 지칭하는데 필요한 LABEL을 명시할 수 있다. counter_name 커서 109 커서를 이용하여 FETCH된 하나의 행이 저장될 RECORD타입의 변수명을 지정한다. 이 변수는 블록의 선언부에 선언되어 있지 않아도 FETCH된 행이 가지는 칼럼의 개수와 타입에 맞춰서 자동으로 생성된다. 이렇게 생성된 변수는 counter_name.column_name 형식으로 참조할 수 있다. 이때 column_name은 SELECT구문의 select list 에 존재하는 칼럼의 이름과 동일하다. 따라서 select list에 연산식을 사용한 경우는 해당 연산식에 별칭 (alias)를 지정하여 참조 가능하도록 만들어야 한다. cursor_name 루프 안에서 사용하게 될 커서의 이름를 지정한다. 이 커서는 블록의 선언부에 선언되어 있어야 한다. cursor_parameter_name OPEN절의 “cursor_parameter_name”을 참조한다. 예제 CREATE TABLE emp_temp(eno INTEGER, ename CHAR(20)); CREATE OR REPLACE PROCEDURE proc1 AS BEGIN DECLARE CURSOR c1 IS SELECT eno, ename FROM employee; BEGIN FOR emp_rec IN c1 LOOP INSERT INTO emp_temp VALUES(emp_rec.eno, emp_rec.ename); END LOOP; END; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM emp_temp; EMP_TEMP.ENO EMP_TEMP.ENAME -------------------------------------- 1 EJJUNG 110 Stored Procedures Manual 2 H J N O ... 19 KMKIM 20 DIKIM 20 rows selected. 커서 111 커서 속성 저장 프로시저 내에서 커서와 관련된 수행 도중 커서의 상태를 파악하기 위해서 알티베이스가 관리하고 있는 속성값들을 참조할 수 있다. 구문 cursor_attribute ::= cursor_name%; FOUND ROWCOUNT NOTFOUND ISOPEN 기능 특정 커서의 속성을 참조할 수 있다. 정수형을 반환하는 ROWCOUNT커서 속성을 제외하고, 커서 속성은 커서의 상태를 나타내는 BOOLEAN형의 표현식이다. 커서의 현재 상태에 따라 각각의 속성 값은 TRUE또는 FALSE일 수 있다. 사용자는 DECLARE 구문에 직접 정의한 커서의 속성 값뿐만 아니라 시스템에서 정의한 암시적 커서의 속성 값도 참조할 수 있다. 암시적 커서는 DELETE, UPDATE, INSERT와 한 건의 레코드가 반환되는 SELECT INTO 구문을 위해 선언된다. 이는 가장 최근에 수행된 SQL 구문에 해당하는 커서의 속성 값을 갖는다. %FOUND 커서에 명시한 SELECT문의 조건을 만족하는 행이 존재하는지 여부를 나타낸다. 단, 다음의 경우에는 조건을 만족하는 행의 존재 112 Stored Procedures Manual 여부에 상관없이 %FOUND의 값이 무조건 FALSE 이다. 열지 않은 커서 FETCH 를 한번도 하지 않은 커서 이미 닫은 커서 암시적 커서의 경우는 DELETE, UPDATE, INSERT문의 수행 결과로 1건 이상이 영향을 받거나, SELECT INTO의 반환되는 레코드가 1건 이상일 때, %FOUND의 값은 TRUE가 된다. 그러나 SELECT INTO의 반환되는 레코드가 2건 이상일 때에는 %FOUND를 검사하기 전에 TOO_MANY_ROWS 예외가 발생하므로, 이 경우 %FOUND 커서 속성이 아닌 예외로 처리해야 한다. %FOUND속성의 값은 다음과 같이 참조할 수 있다. DELETE FROM emp; IF SQL%FOUND THEN -- delete succeeded INSERT INTO emp VALUES ( ...... ); ...... END IF; %NOTFOUND 커서에 명시한 SELECT문의 조건을 만족하는 행이 존재하는지 여부를 나타낸다. 항상 %FOUND와 반대의 값을 가진다. 암시적 커서의 경우는 DELETE, UPDATE, INSERT 구문이 성공적으로 수행되었으나 영향을 받은 레코드가 하나도 없거나, 또는 SELECT INTO가 반환하는 레코드가 하나도 없을 때, %NOTFOUND의 값이 TRUE로 된다. 그러나 SELECT INTO가 반환하는 레코드가 없을 때는 %NOTFOUND 속성을 검사하기 전에 NO_DATA_FOUND 예외가 발생하므로, %NOTFOUND 커서 속성이 아닌 예외로 처리해야 한다. %NOTFOUND속성의 값은 다음과 같이 참조할 수 있다. DELETE FROM emp; IF SQL%NOTFOUND THEN ...... END IF; %ISOPEN 커서가 열렸는지 여부를 나타낸다. 커서를 닫으면 이 값은 FALSE가 된다. %ISOPEN의 값은 다음과 같이 참조할 수 있다. 커서 113 OPEN c1; -- CURSOR OPEN IF c1%ISOPEN THEN ...... END IF; %ROWCOUNT 커서를 사용하여 현재 몇 개의 행을 FETCH 하였는지를 나타낸다. 주의할 점은 %ROWCOUNT는 커서의 SELECT문을 만족하는 행의 수가 아니라, FETCH를 성공할 때마다 1씩 증가한다는 것이다. FETCH 를 한번도 하지 않은 커서의 경우 %ROWCOUNT는 0이다. 열기 전의 커서와 닫은 이후의 커서에 대해 속성을 참조하면 INVALID_CURSOR 에러를 반환한다. DELETE FROM emp; IF SQL%ROWCOUNT > 10 THEN ...... END IF; 예제 예제1 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE TABLE t3(i1 INTEGER); INSERT INTO t1 VALUES(2,2,2); CREATE OR REPLACE PROCEDURE proc1 AS v1 INTEGER; BEGIN SELECT i1 INTO v1 FROM t1 WHERE i1 = 2; IF SQL%found THEN INSERT INTO t1 SELECT * FROM t1; v1 := SQL%ROWCOUNT; INSERT INTO t3 VALUES(v1); END IF; END; / 114 Stored Procedures Manual i SQ L > EX EC p r o c 1 ; Ex e c u t e s u c c e s s . i SQ L > SE L EC T * F R O M t 3 ; T 3 . I 1 -------------- 1 1 row selected. 예제2 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE TABLE t2(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE TABLE t3(i1 INTEGER); INSERT INTO t1 VALUES(1,1,1); INSERT INTO t1 VALUES(1,1,1); INSERT INTO t1 VALUES(1,1,1); CREATE OR REPLACE PROCEDURE proc1 AS CURSOR c1 IS SELECT * FROM t1; v1 INTEGER; v2 INTEGER; v3 INTEGER; BEGIN OPEN c1; IF c1%ISOPEN THEN LOOP FETCH c1 INTO v1, v2, v3; IF c1%FOUND THEN INSERT INTO t2 VALUES (v1, v2, v3); ELSIF c1%NOTFOUND THEN EXIT; END IF; END LOOP; END IF; v1 := c1%ROWCOUNT; INSERT INTO t3 VALUES (v1); CLOSE c1; END; / 커서 115 iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ---------------------------------------- 1 1 1 1 1 1 1 1 1 3 rows selected. iSQL> SELECT * FROM t2; T2.I1 T2.I2 T2.I3 ---------------------------------------- 1 1 1 1 1 1 1 1 1 3 rows selected. iSQL> SELECT * FROM t3; T3.I1 -------------- 3 1 row selected. 예제3 CREATE TABLE emp_temp(eno INTEGER, ename CHAR(20)); CREATE OR REPLACE PROCEDURE proc1 AS BEGIN DECLARE CURSOR c1 IS SELECT eno, ename FROM employee; emp_rec c1%ROWTYPE; BEGIN OPEN c1; LOOP FETCH c1 INTO emp_rec; EXIT WHEN c1%ROWCOUNT > 10 OR c1%NOTFOUND; INSERT INTO emp_temp VALUES(emp_rec.eno, emp_rec.ename); END LOOP; CLOSE c1; END; 116 Stored Procedures Manual EN D ; / i SQ L > EX EC p r o c 1 ; Ex e c u t e s u c c e s s . i SQ L > SE L EC T * F R O M e m p _ t e m p ; EM P_ T E M P . EN O E M P_ T EM P. EN A M E -------------------------------------- 1 EJJUNG 2 HJNO 3 HSCHOI 4 KSKIM 5 SJKIM 6 HYCHOI 7 HJMIN 8 JDLEE 9 KMLEE 10 YHBAE 10 rows selected. 사용자 정의 타입 117 6. 사용자 정의 타입 이 장에서는 저장 프로시저와 저장 함수에서 사용할 수 있는 사용자 정의 타입에 대해서 설명하고 있다. 118 Stored Procedures Manual 개요 사용자 정의 타입인 RECORD 타입 및 Associative Arrays는 데이터를 논리적 단위로 구성하여 처리하는 기능을 제공한다. 또한 다른 저장 프로시저 또는 함수로부터 호출되는 저장 프로시저 또는 함수의 인자 또는 리턴 타입으로 사용이 가능하다. 단, 클라이언트로는 해당 타입의 값을 전송할 수 없다. RECORD타입 RECORD타입은 칼럼의 집합으로 이루어진 사용자 정의 데이터 타입이다. 이를 이용하여 각각의 서로 다른 종류의 데이터들을 논리적 단위로 구성하여 처리할 수 있다. 예를 들어 이름 , 급여, 부서 등의 서로 다른 데이터들을 ‘사원’ 이라는 하나의 데이터로 처리하는 경우 하나의 RECORD 타입으로 묶어 처리할 수 있다. 블록안에 정의된 RECORD타입의 범위는 지역적이다. 즉, 그 타입을 정의한 블록 내에서만 사용할 수 있다. RECORD타입의 정의는 6장의 “사용자 정의 타입의 정의” 절을 참고한다. 정의하는 방법을 제외한 다른 사용방법은 %ROWTYPE속성을 사용해서 선언하는 RECORD 타입 변수와 동일하다. Associative Arrays Associative Array는 해시 테이블과 유사하다. Associative Array는 키-값 쌍의 집합이다. 각 키는 유일한 값이어야 하고, 키에 상응하는 값에 접근할 때 사용된다. 다음의 문법으로 접근할 수 있다. variable_name[index] 키 (index)의 데이터 타입은 INTEGER 또는 VARCHAR이어야 한다. Associative Array를 이용하여 데이터의 개수와 상관없이 동일한 타입의 데이터들을 하나의 단위로 묶어 처리를 할 수 있다. 예를 들어 사원들중 사원번호 1~100까지의 사원 데이터를 처리하는 경우 100개의 데이터를 하나의 Associative Array로 처리할 수 있다. Associative Array의 선언 방법은 6장의 “사용자 정의 타입의 정의” 절을 참고한다. Associative Array변수의 배열 요소 접근을 위해서는 다음과 같이 사용자 정의 타입 119 대괄호( [ ] )를 사용한다. 예) V1[ 1 ] := 1; REF CURSOR (커서 변수) 커서 변수는 다중 레코드를 검색하는 동적 SQL문에서 사용할 수 있는 변수이다. 이러한 커서 변수는 특정 질의문에 종속되지 않기 때문에 커서보다 훨씬 유연하다. 저장 프로시저 또는 함수간 인자로 전달이 가능하고, 클라이언트로도 전달할 수 있다. 커서와 다른 점은 커서는 항상 선언시에 명시한 질의문만 참조할 수 있지만, 커서 변수는 커서 OPEN 시에 다른 질의문을 참조할 수 있다는 점이다. 120 Stored Procedures Manual 사용자 정의 타입의 정의 구문 type_definition ::= associative_array_type_spec associative_array_type_spec ::= TABLEOFdata_typeINDEXBY varchar_type integer_type record_type_spec ::= RECORD()sql_data_type , column_name type_nameTYPEIS ref_cursor_type_spec record_type_spec ref_cursor_type_spec ::= REFCURSOR 사용자 정의 타입 121 type_name 사용자 정의 타입의 이름을 명시한다. associative_array_spec data_type으로 이루어진 Associative Array 타입을 정의한다. 단, data_type에 다른 Associative Array는 올 수 없다. 즉 Associative Array타입의 Associative Array는 생성할 수 없다. record_type_spec sql_data_type으로 이루어진 RECORD 타입을 정의한다. sql_data_type에는 SQL구문에서 사용 가능한 어떤 데이터 타입도 올 수 있다. 단, Associative Array, RECORD 타입은 여기에 올 수 없다. ref_cursor_type_spec REF CURSOR 타입을 정의한다. 예제 예제1 이름 (VARCHAR(20)), 부서 (INTEGER), 봉급 (NUMBER(8))을 갖는 이름이 employee인 RECORD 타입을 정의하라. DECLARE TYPE employee IS RECORD( name VARCHAR(20), dept INTEGER, salary NUMBER(8)); … BEGIN … 예제2 VARCHAR(20)을 구성 요소로 하고 INTEGER타입을 인덱스로 하는 이름이 namelist인 Associative Array를 정의하라. DECLARE TYPE namelist IS TABLE OF VARCHAR(20) INDEX BY INTEGER; … 122 Stored Procedures Manual BEG I N … 예제3 사용자 정의 RECORD타입인 employee를 구성 요소로 가지고 VARCHAR(10) 타입을 인덱스로 하는 employeelist 라는 이름의 Associative Array를 정의하라. DECLARE TYPE employee IS RECORD( name VARCHAR(20), dept INTEGER, salary NUMBER(8)); TYPE employeelist IS TABLE OF employee INDEX BY VARCHAR(10)); … BEGIN … 사용자 정의 타입 123 Associative Array 관련 함수 구문 associative_array_call_method ::= COUNT DELETE() , EXISTS(index) FIRST LAST NEXT PRIOR (index) (index) variable_name. () () () index 기능 Associative array 변수의 배열 요소 관리를 위한 여러가지 함수를 제공한다. 이 함수들은 SQL 함수와 달리 ()를 생략할 수 없다. COUNT Associative array의 구성 요소의 개수를 반환한다. DELETE DELETE()는 모든 구성 요소를 제거하고 제거된 구성 요소의 개수를 반환한다. DELETE(n)은 n에 해당하는 요소를 제거하고 제거된 구성 요소의 개수를 반환한다. 124 Stored Procedures Manual DELETE(m, n)은 범위 m부터 n에 속하는 모든 구성 요소를 제거하고 제거된 구성 요소의 개수를 반환한다. m의 값이 n보다 크면, 아무것도 제거되지 않는다. 값이 같으면, 한 요소만 제거될 것이다. EXISTS EXISTS(n) 은 n에 해당하는 구성 요소가 존재하는지 검사해서 존재하면 boolean 값 TRUE를 그렇지 않으면 FALSE를 반환한다. FIRST 정수 값으로 색인화된 경우, 가장 작은 index번호를 반환한다. 문자열로 색인화된 경우, 가장 낮은 키 값을 반환한다. 구성 요소가 하나도 없으면 NULL을 반환한다. LAST 정수 값으로 색인화된 경우, 가장 큰 index번호를 반환한다. 문자열로 색인화된 경우, 가장 높은 키 값을 반환한다. 구성 요소가 하나도 없으면 NULL을 반환한다. NEXT NEXT(n)은 n의 다음 index번호를 반환한다. VARCHAR 키를 갖는 배열의 경우, 키 문자열들을 바이너리 값 기준으로 정렬하여 바로 다음 키 값을 반환한다. 가장 다음 index가 없으면 NULL을 반환한다. PRIOR PRIOR(n)은 n의 이전 index번호를 반환한다. VARCHAR 키를 갖는 배열의 경우, 키 문자열들을 바이너리 값 기준으로 정렬하여 바로 앞의 키 값을 반환한다. 이전 index가 없으면 NULL을 반환한다. 예제 예제1 Associative array변수 V1의 요소를 삭제 CREATE OR REPLACE PROCEDURE PROC1( 사용자 정의 타입 125 P1 IN VARCHAR(10), P2 IN VARCHAR(10) ) AS TYPE MY_ARR IS TABLE OF INTEGER INDEX BY VARCHAR(10); V1 MY_ARR; V2 INTEGER; BEGIN V1['FSDGADS'] := 1; V1['AA'] := 2; V1['7G65'] := 3; V1['N887K'] := 4; V1['KU'] := 5; V1['34'] := 6; PRINTLN( 'V1 COUNT IS : '||V1.COUNT() ); V2 := V1.DELETE(P1, P2); PRINTLN( 'DELETED COUNT IS : '||V2); PRINTLN( 'V1 COUNT IS : '||V1.COUNT() ); END; / 실행 결과 EXEC PROC1('005T34', 'BC35'); -- 이 범위에 포함되는 인덱스를 가지는 요소는 V1['34'], V1['7G65'], V1['AA']으로 총 3개가 지워져야 한다. V1 COUNT IS : 6 DELETED COUNT IS : 3 V1 COUNT IS : 3 Execute success. 예제2 Associative array변수 V1을 오름차순과 내림차순으로 출력해라. CREATE OR REPLACE PROCEDURE PROC1 AS TYPE MY_ARR1 IS TABLE OF INTEGER INDEX BY INTEGER; V1 MY_ARR1; V1_IDX INTEGER; BEGIN V1[435754] := 1; V1[95464] := 2; 126 Stored Procedures Manual V1[38] := 3; V1[57334] := 4; V1[138] := 5; V1[85462] := 6; PRINTLN( 'ASCENDING ORDER V1'); V1_IDX := V1.FIRST(); LOOP IF V1_IDX IS NULL THEN EXIT; ELSE PRINTLN( 'V1 IDX IS : '||V1_IDX||' VALUE IS : '||V1[V1_IDX] ); V1_IDX := V1.NEXT(V1_IDX); END IF; END LOOP; PRINTLN( 'DESCENDING ORDER V1'); V1_IDX := V1.LAST(); LOOP IF V1_IDX IS NULL THEN EXIT; ELSE PRINTLN( 'V1 IDX IS : '||V1_IDX||' VALUE IS : '||V1[V1_IDX] ); V1_IDX := V1.PRIOR(V1_IDX); END IF; END LOOP; END; / 실행 결과 EXEC PROC1; ASCENDING ORDER V1 V1 IDX IS : 38 VALUE IS : 3 V1 IDX IS : 138 VALUE IS : 5 V1 IDX IS : 57334 VALUE IS : 4 V1 IDX IS : 85462 VALUE IS : 6 사용자 정의 타입 127 V1 IDX IS : 95464 VALUE IS : 2 V1 IDX IS : 435754 VALUE IS : 1 DESCENDING ORDER V1 V1 IDX IS : 435754 VALUE IS : 1 V1 IDX IS : 95464 VALUE IS : 2 V1 IDX IS : 85462 VALUE IS : 6 V1 IDX IS : 57334 VALUE IS : 4 V1 IDX IS : 138 VALUE IS : 5 V1 IDX IS : 38 VALUE IS : 3 Execute success. 128 Stored Procedures Manual RECORD 타입 변수 및 Associative Array변수의 사용 여기서는 사용자 정의 타입을 저장 프로시저 내에서 사용할 때의 규칙 및 예제를 다룬다. 사용자 정의 타입을 파라미터 및 리턴값으로 사용하는 부분은 7장 타입 세트를 참고한다. 타입 호환성 L_VALUE := R_VALUE; 위와 같은 할당문에 사용될 때 사용자 정의 타입의 타입 호환성을 살펴보면 다음과 같다. L_VALUE의 타입 R_VALUE의 타입 호환성 RECORD 타입 RECORD 타입 같은 이름을 갖는 동일한 레코드 타입간에만 호환된다. 레코드 타입의 내부 구조가 같은 다른 레코드 타입은 서로 호환되지 않는다. RECORD 타입 %ROWTYPE 내부에 정의된 칼럼의 개수만 동일하면 호환된다. %ROWTYPE RECORD 타입 내부에 정의된 칼럼의 개수만 동일하면 호환된다. Associative Array Associative Array 동일한 타입 (즉, 같은 이름의 타입) 간에만 호환된다. 다음 예제와 같이 사용자 정의 타입은 그 내부 구조가 동일하다 하더라도 할당문은 실패하게 된다. 예제1 RECORD 타입 변수의 할당 CREATE OR REPLACE PROCEDURE PROC1 AS TYPE emp_rec_type1 IS RECORD ( name VARCHAR(20), job_id VARCHAR(10), salary NUMBER(8) ); 사용자 정의 타입 129 TYPE emp_rec_type2 IS RECORD ( name VARCHAR(20), job_id VARCHAR(10), salary NUMBER(8) ); v_emp1 emp_rec_type1; v_emp2 emp_rec_type2; BEGIN v_emp1.name := 'smith'; v_emp1.job_id := 'RND1069'; v_emp1.salary := '10000000'; v_emp2 := v_emp1; -- 실패. 두 변수가 같은 내부 구조이지만 각각 다른 타입이므로 할당은 실패한다. 그러나 아래처럼 타입이 일치하는 요소간의 할당은 가능하다. v_emp2.name := v_emp1.name; RECORD 타입 변수 예제 예제1 사원의 이름, 급여, 부서를 저장하는 RECORD타입의 변수를 생성한다. CREATE OR REPLACE PROCEDURE PROC1 AS TYPE emp_rec_type IS RECORD ( name VARCHAR(20), job_id VARCHAR(10), salary NUMBER(8) ); v_emp emp_rec_type; BEGIN v_emp.name := 'smith'; v_emp.job_id := 'RND1069'; v_emp.salary := '10000000'; PRINTLN('NAME : '||v_emp.name||' '|| 'JOB ID : '||v_emp.job_id||' '|| 'SALARY : '||v_emp.salary ); END; 130 Stored Procedures Manual / ASSOCIATIVE ARRAY 타입 예제 예제 1 사원번호가 1에서 100 사이에 속한 사원의 이름을 출력한다. CREATE OR REPLACE PROCEDURE PROC1 AS TYPE emp_array_type IS TABLE OF VARCHAR(20) INDEX BY INTEGER; v_emp emp_array_type; BEGIN FOR I IN 1 .. 100 LOOP SELECT name INTO v_emp[I] FROM employees WHERE emp_id = I; END LOOP; FOR I IN v_emp.FIRST() .. v_emp.LAST() LOOP PRINTLN( v_emp[I] ); END LOOP; END; / 예제 2 사원번호가 1에서 100 사이에 속한 사원의 이름, 급여, 부서를 출력한다. CREATE OR REPLACE PROCEDURE PROC1 AS TYPE emp_rec_type IS RECORD ( name VARCHAR(20), job_id VARCHAR(10), salary NUMBER(8) ); TYPE emp_array_type IS TABLE OF emp_rec_type INDEX BY INTEGER; v_emp emp_array_type; 사용자 정의 타입 131 BEGIN FOR I IN 1 .. 100 LOOP SELECT name, job_id, salary INTO v_emp[I] FROM employees WHERE emp_id = I; END LOOP; FOR I IN v_emp.FIRST() .. v_emp.LAST() LOOP PRINTLN( v_emp[I].name||’ ’|| v_emp[I].job_id||’ ’|| v_emp[I].salary ); END LOOP; END; / 132 Stored Procedures Manual REF CURSOR 저장 프로시저는 동적 SQL의 실행으로 얻은 결과 집합 (Result set)을 커서 변수(REF CURSOR)를 통해서 클라이언트에게 전달할 수 있다. OPEN FOR 문으로 커서 변수를 열고 질의를 실행한 후, 커서를 OUT인자를 통해서 클라이언트로 전달하면 클라이언트는 결과집합에 접근할 수 있게 된다. 여러 개의 커서를 전달하면, 클라이언트에서는 다중 결과 집합에 접근이 가능하다. 커서 변수를 열기 위한 OPEN FOR 구문을 제외하고, 기존의 커서 관련 구문을 그대로 이용할 수 있다. 커서 변수는 저장 프로시저의 OUT또는 IN/OUT 인자로만 전달할 수 있고, 저장 함수에서 RETURN 문으로는 반환할 수 없다. 저장 프로시저에서 OPEN된 상태로 커서 변수가 클라이언트로 전달되어야 클라이언트에서 결과집합을 FETCH할 수 있다. 즉, 커서 상태가 CLOSE 상태로 전달될 경우 결과집합을 사용할 수 없다. 저장 프로시저에서 UPDATE, INSERT 문을 실행할 경우, 영향 받은 레코드 수(Affected Row Count)는 반환하지 않는다. 클라이언트에서 커서 변수로 결과집합을 받는 방법은 클라이언트의 형태에 따라 달라진다. 커서 변수를 이용하여 결과 집합을 클라이언트로 전달하는 것은 ODBC와 JDBC에서만 가능하다. Embedded SQL (Altibase Precompiler (APRE))에서는 커서 변수를 통해 프로시저의 결과 집합을 받을 수 없다. 예제 REF CURSOR를 이용한 저장 프로시저를 생성한다. 1. 테이블 emp와 staff을 생성하고 값을 입력한다. create table emp (eno integer, ename char(20), dno integer); create table staff (name char(20), dept char(20), job char(20), salary integer); insert into emp values (10, 'dulgi papa', 100); insert into emp values (20, 'kunhan' , 200); insert into emp values (30, 'okasa' , 300); insert into staff values ('dulgi papa' , '100' , 'papa', 100); insert into staff values ('shincha' , '200' , 'engineer' , 200); 사용자 정의 타입 133 insert into staff values ('ji hyung', '300', '', 0); 2. REF CUR인 사용자 정의 타입 MY_CUR 를 정의하고, 이를 포함하는 타입세트 MY_TYPE를 정의한다. create typeset MY_TYPE as type MY_CUR is REF CURSOR; end; / 3. MY_CUR타입의 OUT 인자 P1과 P2, INTEGER 타입의 IN 인자 SAL을 가지는 저장 프로시저 PROC1을 생성한다. create or replace procedure PROC1 (P1 out MY_TYPE.MY_CUR, P2 out MY_TYPE.MY_CUR, SAL in INTEGER) as sql_stmt VARCHAR2(200); begin sql_stmt := 'SELECT name,dept,job FROM staff WHERE salary > ?'; OPEN P1 FOR 'SELECT eno, ename, dno FROM emp'; OPEN P2 FOR sql_stmt USING SAL; end; / 4. 데이터베이스 서버에 연결한 후, 프로시저 PROC1을 실행한다. SQLRETURN execute_proc() { SQLCHAR errMsg[MSG_LEN]; char sql[1000]; SQLHSTMT stmt = SQL_NULL_HSTMT; int sal; int sal_len; int eno; int eno_len; int dno; int dno_len; SQLCHAR ename[ENAME_LEN+1]; SQLCHAR name[NAME_LEN+1]; SQLCHAR dept[DEPT_LEN+1]; SQLCHAR job[JOB_LEN+1]; int job_ind; SQLRETURN rc = SQL_SUCCESS; 134 Stored Procedures Manual if (SQL_ERROR == SQLAllocStmt(dbc, printf("SQLAllocStmt error!!\n"); return SQL_ERROR; } /* 실행할 SQL 문을 준비 */ sprintf(sql, "EXEC proc1(?)"); if ( SQLPrepare(stmt,(SQLCHAR *)sql,SQL_NTS) == SQL_ERROR ) { printf("ERROR: prepare stmt\n"); } else { printf("SUCCESS: prepare stmt\n"); } /* 변수 sal에 100을 할당 */ sal = 100; /* SQL 문장에 매개변수(sal)를 연결시킴 */ if ( SQLBindParameter( stmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, NULL) == SQL_ERROR ) { printf("ERROR: Bind Parameter\n"); } else { printf("SUCCESS: 1 Bind Parameter\n"); } /* SQL 문장 실행, 프로시져 PROC1을 실행해서 'SELECT eno, ename, dno FROM emp'의 결과값은 P1에 'SELECT name,dept,job FROM staff WHERE salary > ?'(USING SAL)의 결과값은 P2에 가져온다 */ if (SQL_ERROR == SQLExecute(stmt)) 사용자 정의 타입 135 { printf("ERROR: Execute Procedure\n"); } /* 'SELECT eno, ename, dno FROM emp'의 결과값을 변수(eno, ename, dno)에 저장 */ if (SQL_ERROR == SQLBindCol(stmt, 1, SQL_C_SLONG, ong *) { printf("ERROR: Bind 1 Column\n"); } if (SQL_ERROR == SQLBindCol(stmt, 2, SQL_C_CHAR, ename, sizeof(ename), NULL)) { printf("ERROR: Bind 2 Column\n"); } if (SQL_ERROR == SQLBindCol(stmt, 3, SQL_C_SLONG, ong *) { printf("ERROR: Bind 3 Column\n"); } /* P1에 결과값이 있는 동안 결과값을 받아 화면에 출력 */ while (SQL_SUCCESS == rc) { rc = SQLFetch(stmt); if (SQL_SUCCESS == rc) { printf("Result Set 1 : %d,%s,%d\n" ,eno, ename, dno); } else { if (SQL_NO_DATA == rc) { break; } else { printf("ERROR: SQLFetch [%d]\n", rc); execute_err(dbc, stmt, sql); break; } } } 136 Stored Procedures Manual / * 다음 결과(P2)로 이동 */ rc = SQLMoreResults(stmt); if (SQL_ERROR == rc) { printf("ERROR: SQLMoreResults\n"); } else { /* 'SELECT name,dept,job FROM staff WHERE salary > ?'(USING SAL)의 결과값을 변수(name, dept, job)에 저장 */ if (SQL_ERROR == SQLBindCol(stmt, 1, SQL_C_CHAR, name, sizeof(name), NULL)) { printf("ERROR: Bind 1 Column\n"); } if (SQL_ERROR == SQLBindCol(stmt, 2, SQL_C_CHAR, dept, sizeof(dept), NULL)) { printf("ERROR: Bind 2 Column\n"); } if (SQL_ERROR == SQLBindCol(stmt, 3, SQL_C_CHAR, job, sizeof(job), (long *) { printf("ERROR: Bind 3 Column\n"); } /* P2에 결과값이 있는 동안 결과값을 받아 화면에 출력 */ while (SQL_SUCCESS == rc) { rc = SQLFetch(stmt); if (SQL_SUCCESS == rc) { if( job_ind == -1 ) printf("Result Set 2 : %s,%s,NULL\n" ,name, dept); else printf("Result Set 2 : %s,%s,%s\n" ,name, dept, job); } else { if (SQL_NO_DATA == rc) { 사용자 정의 타입 137 break; } else { printf("ERROR: SQLFetch [%d]\n", rc); execute_err(dbc, stmt, sql); break; } } } } if (SQL_ERROR == SQLFreeStmt( stmt, SQL_DROP )) { printf("sql free stmt error\n"); } } 타입 세트 139 7. 타입 세트 이 장에서는 타입 세트를 정의하고 사용하는 방법에 대해 설명한다. 140 Stored Procedures Manual 개요 타입 세트(Type Set)는 저장 프로시저에서 사용하는 사용자 정의 타입들을 한 곳에 모아서 관리하도록 해주는 데이터베이스 객체(Object)이다. 특징 사용자 정의 타입의 공유 사용자 정의 타입들을 한 곳에서 관리하므로 각각의 저장 프로시저 내에서 동일한 구조의 사용자 정의 타입을 중복해서 선언하지 않아도 된다. 사용자 정의 타입을 인자 또는 리턴값으로 사용 동일한 타입세트에 속해 있는 타입은 인자 또는 리턴값으로 프로시저 간 전달이 가능하다. 단, 클라이언트로는 전송할 수 없다. 데이터 타입들을 논리적 단위로 통합 관리 타입세트는 저장 프로시저와 저장 함수에서 사용하는 타입들을 데이터의 논리적 단위로 통합 관리하기 용이하게 해 준다. 결과 집합 전달 저장 프로시저 내부에서 실행된 SQL문의 결과 집합을 REF CURSOR 타입을 사용하여 클라이언트로 전달할 수 있다. 구조 아래 그림과 샘플 코드에서처럼, 타입 세트를 사용하면 여러 저장 프로시저에서 사용하는 사용자 정의 타입을 공유하고 관리하는 것이 가능하며, 데이터 이동이 용이하다. 타입 세트 141 PROCEDURE 1, 2, FUNCTION 3에서 사용하는 데이터 타입들의 집합 PROCDURE 1PROCDURE 2FUNCTION 3 TYPESET 1 TYPESET 1 사용자 정의 타입 emp_rec_type과 emp_arr_type 을 typeset_1안에 정의한다. CREATE TYPESET typeset_1 AS TYPE emp_rec_type IS RECORD ( name VARCHAR(20), job_id VARCHAR(10), salary NUMBER(8) ); TYPE emp_arr_type IS TABLE OF emp_rec_type INDEX BY INTEGER; END; / PROCEDURE 1 저장 프로시저 procedure_1에서 emp_arr_type을 OUT 인자로 하는 procedure_2를 호출한다. CREATE PROCEDURE procedure_1 AS V1 typeset_1.emp_arr_type; BEGIN procedure_2( V1 ); PRINTLN(V1[1].name); PRINTLN(V1[1].job_id); PRINTLN(V1[1].salary); END; / PROCEDURE 2 142 Stored Procedures Manual 저장 프로시저 procedure_2의 OUT 인자에 function_3의 반환값을 할당한다. CREATE PROCEDURE procedure_2 ( P1 OUT typeset_1.emp_arr_type ) AS V1 typeset_1.emp_rec_type; BEGIN V1 := function_3(); P1[1] := V1; END; / FUNCTION 3 typeset_1.emp_rec_type타입의 값을 반환한다. CREATE FUNCTION function_3 RETURN typeset_1.emp_rec_type AS V1 typeset_1.emp_rec_type; BEGIN V1.name := 'Smith'; V1.job_id := 1010; V1.salary := 200; RETURN V1; END; / 타입 세트 143 CREATE TYPESET 구문 create_typeset ::= CREATETYPESETtypeset_name ORREPLACE AS IS type_declarationEND user_name. 전제 조건 SYS 사용자 또는 CREATE PROCEDURE, CREATE ANY PROCEDURE 시스템 권한을 가진 사용자만 실행 가능하다. 설명 저장 프로시저에서 사용할 사용자 정의 타입을 포함하는 타입 세트를 정의한다. 타입 세트 내에서 정의한 타입은 프로시저의 INPUT / OUTPUT 인자로 사용할 때 용이하다. user_name 생성될 타입 세트의 소유자 이름을 명시한다. 생략하면 알티베이스는 현재 세션에 연결된 사용자의 스키마에 타입 세트를 생성한다 typeset_name 타입 세트의 이름이다. type_declaration 144 Stored Procedures Manual 6장 사용자 정의 타입에서 “사용자 정의 타입의 정의”절을 참고한다. 예제 예제1 my_typeset이란 이름의 타입 세트를 생성한다. CREATE TYPESET my_typeset AS TYPE emp_rec_type IS RECORD( name VARCHAR(20), id INTEGER ); TYPE emp_arr_type IS TABLE OF emp_rec_type INDEX BY INTEGER; END; / 예제2 my_typeset을 이용하는 프로시저 my_proc1을 생성한다. CREATE PROCEDURE my_proc1 AS V1 my_typeset.emp_rec_type; V2 my_typeset.emp_arr_type; BEGIN V1.name := 'jejeong'; V1.id := 10761; V2[1] := V1; V1.name := 'ehkim'; V1.id := 11385; V2[2] := V1; V1.name := 'mslee'; V1.id := 13693; V2[3] := V1; PRINTLN('NAME : '||V2[1].name|| ' ID : '||V2[1].id ); PRINTLN('NAME : '||V2[2].name|| ' ID : '||V2[2].id ); PRINTLN('NAME : '||V2[3].name|| ' ID : '||V2[3].id ); 타입 세트 145 END; / 결과 iSQL> exec my_proc1; NAME : jejeong ID : 10761 NAME : ehkim ID : 11385 NAME : mslee ID : 13693 Execute success. 146 Stored Procedures Manual DROP TYPESET 구문 drop_typeset ::= DROPTYPESETtypeset_name; user_name. 전제 조건 SYS 사용자이거나 객체의 생성자 또는 DROP ANY PROCEDURE 시스템 권한을 가진 사용자만 실행 가능하다. 설명 명시된 타입 세트를 제거한다. 제거된 타입 세트를 사용하던 저장 프로시저는 유효하지 않은(Invalid) 상태가 된다. user_name 제거될 타입 세트의 소유자 이름을 명시한다. 생략하면 알티베이스는 제거될 타입 세트가 현재 세션에 연결된 사용자의 스키마에 속한 것으로 간주한다. typeset_name 타입 세트의 이름이다. 예제 타입 세트 147 my_typeset이란 이름의 타입 세트를 삭제한다. DROP TYPESET my_typeset 동적 SQL 149 8. 동적 SQL 이 장에서는 저장 프로시저와 저장 함수에서 동적 SQL을 사용하는 방법을 설명한다. 150 Stored Procedures Manual 동적 SQL의 개요 동적 SQL(Dynamic SQL)은 실행 시간에 사용자가 원하는 질의를 만들어서 실행하는 것이다. 일반적인 저장 프로시저의 SQL 실행 방법은 정적인 (Static) 방법으로, 저장 프로시저가 컴파일 될 때 이미 SQL문의 실행계획이 생성된다. 저장 프로시저를 실행할 때 결정되는 SQL 구문은 동적 SQL을 사용해야 한다. 동적 SQL의 실행 아래 다이어그램은 정적 SQL과 동적 SQL이 포함된 저장 프로시저를 생성하고 실행할 때 알티베이스 내부에서 이를 수행하는 과정을 비교하고 있다. 동적 SQL 151 [그림 8-1] 정적 SQL과 동적 SQL의 실행 과정 비교 [그림 8- 1]의 왼쪽 저장 프로시저는 ‘DELETE FROM T1’ 문을 정적으로 처리한 것이고, 오른쪽 저장 프로시저는 동일한 DELETE 문을 EXECUTE IMMEDIATE를 사용하여 동적으로 처리한 것이다. 전자는 프로시저 생성(컴파일) 시에 이미 DELETE문에 대한 실행계획이 만들어지고, 실행 시에 DELETE문을 수행 (EXECUTE)한다. 하지만, 후자는 프로시저 생성(컴파일) 시에는 실행계획을 만들지 않고 프로시저를 실행할 때 DELETE 문에 대해 실행계획을 생성하고 수행한다. 수행 시에 실행계획을 생성하고 수행하기 때문에, 수행 시에 SQL 문장이 다른 문장으로 바뀌어도 된다. 특징 152 Stored Procedures Manual 동적 SQL의 장점은 저장 프로시저 실행 시에 SQL문을 사용자 마음대로 변경하여 실행시킬 수 있다는 것이다. 또한 SQL문의 종류에 관계없이 DBMS가 지원하는 SQL은 무엇이든 실행시킬 수 있다. 동적 SQL은 다음의 상황에서 유용하다. 쿼리할 테이블의 이름이 실행시에 결정 될 때 상황에 따라 질의의 힌트나 조건절의 조건 연산자를 바꾸어 실행할 필요가 있을 때 DDL과 DML구문이 빈번하게 일어나서 저장 프로시저 내의 SQL문을 그 때 그때 최적화할 필요가 있을 때 실행 비용이 최적화 비용보다 큰 SQL을 자주 실행할 필요가 있을 때 재활용성 가능성이 높은 저장 프로시저가 필요할 때 단, 동적 SQL문은 문장의 생성, 삭제 및 바인딩 비용이 매우 크므로 정적 SQL과 비교해서 낮은 성능을 보일 수 있다. 동적 SQL문의 사용은 응용프로그램 구조를 유연하게 하는 반면 성능을 저하시킬 수 있다. 동적 SQL 153 EXECUTE IMMEDIATE 동적으로 DDL, DCL, DML 및 단일 레코드를 결과로 반환하는 SELECT 질의를 실행하기 위해 사용한다. 구문 execute_imme_statement ::= dynamic_string ::= expr dynamic_stringEXECUTE IN IMMEDIATE record_name variable_name , INTO OUT IN variable_name , USING OUT variable_name 154 Stored Procedures Manual 설명 dynamic_string 실행할 질의문을 가지는 문자열이다. INTO INTO절은 SELECT ... INTO 구문과 마찬가지로 가져온 결과 집합을 저장할 변수들을 명시한다. USING USING절은 실행 시에 SQL구문에 바인드할 인자를 명시하는데 사용된다. 인자는 SQL문 내의 물음표 (“?”) 위치에 보이는 순서대로 바인드 된다. IN, OUT, IN/OUT 인자를 지정할 수 있다. 예제 다음은 DML문을 동적 SQL을 사용해서 실행하는 예제이다. CREATE PROCEDURE fire_emp(v_emp_id INTEGER) AS BEGIN EXECUTE IMMEDIATE 'DELETE FROM employees WHERE emp_id = ?' USING v_emp_id; END; / CREATE PROCEDURE insert_table ( table_name VARCHAR(100), dept_no NUMBER, dept_name VARCHAR(100), location VARCHAR(100)) AS stmt VARCHAR2(200); BEGIN stmt := 'INSERT INTO ' || table_name || ' values (?, ?, ?)'; EXECUTE IMMEDIATE stmt USING dept_no, dept_name, location; END; 동적 SQL 155 / EXECUTE IMMEDIATE dynamic_string 구문은 해당 질의문을 Direct-Execute 방식으로 실행한다. USING 뒤에 나오는 변수는 바인드할 인자이다. DDL 및 DCL의 경우도 DML과 마찬가지로 EXECUTE IMMEDIATE를 사용하여 실행할 수 있다. 제약사항 저장 프로시저 내에서 동적 SQL 형태로 사용 가능한 질의문은 다음과 같다. DML SELECT, INSERT, UPDATE, DELETE, MOVE, LOCK TABLE, ENQUEUE DDL CREATE, ALTER, DROP DCL ALTER SYSTEM, ALTER SESSION, COMMIT, ROLLBACK 동적 SQL사용을 지원하지 않는 구문은 다음과 같다. iSQL을 통해서만 실행할 수 있는 구문 - SELECT * FROM tab; - DESC table_name - SET TIMING - SET AUTOCOMMIT CONNECT DISCONNECT DEQUEUE 156 Stored Procedures Manual OPEN FOR 이 구문은 커서변수 (REF CURSOR)를 초기화하고 쿼리를 수행하여 결과 집합을 결정하는 데 사용된다. 결과 집합의 데이터는 FETCH 문을 사용해서 가져올 수도 있고, 저장 프로시저의 인자를 사용해서 클라이언트로 전달할 수도 있다. USING 절을 이용하여 인자를 바인딩할 수도 있다. 구문 open_for_statement ::= cursor_variable_nameOPEN IN USINGvariable_name , FORdynamic_string 설명 cursor_variable_name REF CURSOR 타입의 커서 변수의 이름이다. dynamic_string dynamic_string은 실행될 질의문이다. 여기에는 문자열 형태의 SELECT 구문 만이 올 수 있다. USING 동적 SQL 157 USING 절은 실행 시에 SQL구문에 바인드할 인자를 명시하는 데 사용된다. 인자는 SQL문 내의 물음표 (“?”) 위치에 보이는 순서대로 바인드 된다. 예제 다음은 여러 행을 가져오는 동적 SQL문의 실행 결과를 클라이언트로 전달하기 위해서, 프로시저 내부에서 REF CURSOR타입의 커서 변수를 사용하는 예제이다. 클라이언트로 전달된 커서변수를 통하여 결과집합을 FETCH하는 방법은 Precompiler User's Manual, ODBC Reference, API User's Manual 을 참고한다. CREATE OR REPLACE PROCEDURE fetch_employee AS TYPE MY_CUR IS REF CURSOR; emp_cv MY_CUR; emp_rec employees%ROWTYPE; stmt VARCHAR2(200); v_job VARCHAR2(10) := 'WEBMASTER'; BEGIN stmt := 'SELECT * FROM employees WHERE job_id = ?'; OPEN emp_cv FOR stmt USING v_job; LOOP FETCH emp_cv INTO emp_rec; EXIT WHEN emp_cv%NOTFOUND; PRINTLN('[Name]: ' || emp_rec.name || ' [Job Id]: ' || emp_rec.job_id); END LOOP; CLOSE emp_cv; END; / 예외 처리 159 9. 예외 처리 160 Stored Procedures Manual 개요 저장 프로시저 수행 중에 발생하는 예외 (Exception)는 예외 처리부에서 각 예외 별로 지정하여 처리할 수 있다. 종류 알티베이스 저장 프로시저에서 발생하는 예외에는 두 가지 종류가 있다. 시스템-정의 예외 (System-defined Exception) 사용자정의 예외 (User-defined Exception) 저장 프로시저가 지원하는 예외에는 와 사용자가 임의로 정의하여 사용할 수 있는 가 있다. 시스템-정의 예외 시스템-정의 예외는 알티베이스 내에 미리 정의해 둔 것으로, 저장 프로시저의 선언부에 따로 선언할 필요가 없다. 발생 가능한 몇몇 시스템-정의 예외는 다음과 같다. 예외 이름 발생 원인 CURSOR_ALREADY_OPEN 이미 열려 있는 커서를 닫지 않고 다시 열려고 하는 경우 발생한다. Cursor FOR LOOP의 경우 내부에서 묵시적으로 커서가 열리므로 루프 내에서 OPEN문을 사용하여 명시적으로 커서를 열 수 없다. DUP_VAL_ON_INDEX Unique 인덱스가 정의된 칼럼에 중복된 값을 입력하려 하는 경우 발생한다. INVALID_CURSOR 열려 있지 않은 커서를 사용해서 FETCH 또는 CLOSE하려는 경우 등과 같이, 현재 커서 상태에서 수행할 수 없는 작업을 수행하려 하는 경우에 발생한다. NO_DATA_FOUND SELECT 문에 의해 반환된 데이터가 한 건도 없을 때 발생한다. TOO_MANY_ROWS SELECT INTO문은 하나의 레코드만 반환해야 하는데 둘 이상의 예외 처리 161 레코드가 반환된 경우 발생한다. 사용자정의 예외 사용자정의 예외는 사용자가 명시적으로 선언한 것으로, RAISE문을 사용해서 의도적으로 발생시켜야 한다. DECLARE comm_missing EXCEPTION; -- DECLARE user defined EXCEPTION BEGIN ...... RAISE comm_missing; -- raising EXCEPTION ...... EXCEPTION WHEN comm_missing THEN ...... 사용자정의 예외를 시스템-정의 예외와 같은 이름으로 정의할 경우, 사용자정의 예외가 시스템-정의 예외보다 우선된다. 즉, 예외 처리부 내에서 사용자정의 예외로 간주될 것이다. 예외 선언 시스템-정의 예외의 경우 시스템 내부에 예외 이름이 정의되어 있으므로 명시적으로 선언할 필요가 없다. 반면에, 사용자정의 예외의 경우 선언부에 명시적으로 그 이름을 정의한 후 사용해야 한다. 예외 발생 시스템-정의 예외는 명시적으로 발생시킬 필요가 없다. 저장 프로시저 수행 도중 시스템-정의 예외가 발생하면 그 예외를 처리하는 Exception Handler가 존재하는지 확인한다. 존재할 경우 자동으로 그 쪽으로 분기해서 Exception Handler에 정의된 작업을 처리한다. 반면, 사용자정의 예외의 경우 저장 프로시저 내에서 명시적으로 예외를 발생시켜야 한다. 사용자정의 예외는 RAISE문을 사용해서 발생시킬 수 있다. 예외 처리부 162 Stored Procedures Manual 사용자정의 예외 및 시스템-정의 예외가 발생할 경우 처리할 작업들을 여기에 정의한다. 예외 처리 163 EXCEPTION 구문 exception_declaration ::= exception_nameEXCEPTION; 설명 사용자정의 예외를 정의한다 exception_name 한 블록 내에서 유일한 이름이어야 한다. 자신이 선언된 블록의 BEGIN와 END 문 범위내에서 유효하다. 예제 DECLARE error_1 EXCEPTION; error_2 EXCEPTION; error_3 EXCEPTION; 164 Stored Procedures Manual RAISE 구문 raise_statement ::= ;RAISE exception_name 설명 이 구문은 명시적으로 예외를 발생시키기 위해 사용된다. 예외가 발생하면 발생한 예외에 해당하는 exception handler의 루틴으로 제어가 넘어간다. exception_name 발생시키고자 하는 예외 이름을 지정한다. 이 이름은 블록 선언부에 선언되어 있는 예외 또는 시스템-정의 예외의 이름이어야 한다. 선언되지 않은 예외를 여기에 명시하면, 저장 프로시저 컴파일은 실패한다. 저장 프로시저 실행 시 예외가 발생했는데 해당하는 exception handler가 존재하지 않을 경우에는, 저장 프로시저의 수행을 중단하고 오류를 돌려 준다. 사용자정의 예외의 경우 외부 블록과 내부 블록에 같은 예외 이름을 정의할 수도 있다. 이 경우 모호함을 없애기 위해서 각 블록에 LABEL을 붙이고 RAISE문에서는 예외 이름 앞에 LABEL 명을 명시하여 사용하면 된다. 외부 블록에서 선언된 예외는 내부 블록에서 선언된 예외에 해당하는 exception handler내에서 발생시킬 수 있다. 예외 명을 지정하지 않아도 되는 경우는 exception handler안에서만 사용되는 경우로서 이때는 앞서 발생한 예외를 다시 한번 발생시킨다. 예외 처리 165 예제 예제 1 다음은 VALUE_ERROR 예외를 예외 처리부에서 처리하고, 같은 예외를 다시 발생시키는 예제이다. CREATE OR REPLACE PROCEDURE PROC1 AS BEGIN RAISE VALUE_ERROR; EXCEPTION WHEN VALUE_ERROR THEN PRINTLN('VALUE ERROR CATCHED. BUT RE-RAISE.'); RAISE; END; / iSQL> EXEC PROC1; VALUE ERROR CATCHED. BUT RE-RAISE. [ERR-3116F : Value error 0004 : RAISE VALUE_ERROR; ^ ^ ] 예제2 다음은 예제 1의 PROC1에서 발생한 예외를 처리하는 예제이다 CREATE OR REPLACE PROCEDURE PROC2 AS BEGIN PROC1; EXCEPTION WHEN OTHERS THEN PRINTLN('EXCEPTION FROM PROC1 CATCHED.'); PRINTLN('SQLCODE : '||SQLCODE); END; / iSQL> EXEC PROC2; VALUE ERROR CATCHED. BUT RE-RAISE. EXCEPTION FROM PROC1 CATCHED. SQLCODE : 201071 Execute success. 166 Stored Procedures Manual RAISE_APPLICATION_ERROR 사용자정의 에러코드 및 에러 메시지를 가지고 예외를 발생시킨다. 에러코드는 990000부터 991000까지의 범위로, 1001 개의 사용자 에러 코드가 지원된다. 구문 RAISE_APPLICATION_ERROR ( errcode INTEGER, errmsg VARCHAR(2047) ); 파라미터 이름 입출력 데이터 타입 설명 errcode IN INTEGER 사용자정의 에러 코드 (990000 에서 991000 까지 사용 가능) errmsg IN VARCHAR 사용자정의 에러 메시지 텍스트 설명 이 프로시저는 사용자가 정의한 에러 코드와 에러메시지를 가지는 예외를 발생시킨다. 예제 다음은 사용자 정의 에러를 발생시키는 예제이다. 단, iSQL에서 에러코드는 16진수 값으로 표시된다. CREATE OR REPLACE PROCEDURE PROC1 AS BEGIN RAISE_APPLICATION_ERROR( 990000, 'This is my error msg. ' ); END; / 예외 처리 167 iSQL> exec proc1; [ERR-F1B30 : This is my error msg. 0004 : RAISE_APPLICATION_ERROR( 990000, 0005 : 'This is my error msg.' ); 168 Stored Procedures Manual 사용자정의 예외 사용자가 직접 RAISE문을 사용해서 예외를 발생시키는 경우는 다음 2가지 경우이다. 사용자가 정의한 예외를 처리하는 경우 시스템이 정의한 예외을 처리하는 경우 사용자정의 예외의 에러코드 사용자가 정의한 예외를 처리하는 경우, SQLCODE의 값은 항상 201232로 고정되어 있다. CREATE OR REPLACE PROCEDURE PROC1 AS E1 EXCEPTION; BEGIN RAISE E1; EXCEPTION WHEN E1 THEN PRINTLN('SQLCODE: ' || SQLCODE); -- 에러 코드 출력 PRINTLN('SQLERRM: ' || SQLERRM); -- 에러 메시지 출력 END; / iSQL> EXEC PROC1; SQLCODE: 201232 SQLERRM: User-Defined Exception. Execute success. 만약 이 예외를 예외 처리부에서 사용자정의 예외로 처리하지 않는 경우, 발생하는 에러는 다음과 같다. 즉 사용자정의 예외를 위한 exception handler가 없다는 의미이다. CREATE OR REPLACE PROCEDURE PROC1 AS E1 EXCEPTION; BEGIN RAISE E1; END; / iSQL> EXEC PROC1; 예외 처리 169 [ERR-31157 : Unhandled exception : E1] 사용자정의 예외가 예외 처리부에서 처리될 때는 항상 다음의 에러 코드를 가진다. Exception Name Error Code(integer) Error Code(hexadecimal) Error Section 201232 31210 qpERR_ABORT_QSX_USER_ DEFINED_EXCEPTION 시스템 정의 예외의 에러코드 시스템-정의 예외가 발생하는 경우에는 다음과 같이 시스템에 지정된 에러코드가 출력된다. CREATE OR REPLACE PROCEDURE PROC1 AS BEGIN RAISE NO_DATA_FOUND; EXCEPTION WHEN NO_DATA_FOUND THEN PRINTLN('SQLCODE: ' || SQLCODE); -- 에러 코드 출력 PRINTLN('SQLERRM: ' || SQLERRM); -- 에러 메시지 출력 END; / iSQL> EXEC PROC1; SQLCODE: 201066 SQLERRM: No data found. 0004 : RAISE NO_DATA_FOUND; Execute success. 다음과 같이 시스템-정의 예외의 경우에는 별도의 예외처리를 예외 처리부에서 하지 않더라도 이미 정의된 에러코드가 출력되는 것을 볼 수 있다. CREATE OR REPLACE PROCEDURE PROC1 AS BEGIN RAISE NO_DATA_FOUND; END; / iSQL> EXEC PROC1; [ERR-3116A : No data found. 170 Stored Procedures Manual 0 0 0 4 : R AI SE N O _ D A T A_ F O U N D ; 참고로 많이 사용되는 시스템-정의 예외의 에러코드는 다음과 같다. 각 예외의 원인에 대해서는 앞서 설명한 “시스템-정의 예외” 절을 참고하기 바란다. 모든 에러 코드 리스트는 Error Message Reference를 참조한다. Exception Name Error Code (integer) Error Code (hexade cimal) Error Section "CURSOR_ALREAD Y_OPEN" 201062 31166 qpERR_ABORT_QSX_CURSOR_ALREAD Y_OPEN "DUP_VAL_ON_IND EX" 201063 31167 qpERR_ABORT_QSX_DUP_VAL_ON_IN DEX "INVALID_CURSOR" 201064 31168 qpERR_ABORT_QSX_INVALID_CURSOR "NO_DATA_FOUND" 201066 3116A qpERR_ABORT_QSX_NO_DATA_FOUND "TOO_MANY_ROWS 201070 3116E qpERR_ABORT_QSX_TOO_MANY_ROW S "INVALID_PATH" 201237 31215 qpERR_ABORT_QSX_FILE_INVALID_PA TH "INVALID_MODE" 201235 31213 qpERR_ABORT_QSX_INVALID_FILEOPE N_MODE "INVALID_FILEHAN DLE" 201238 31216 qpERR_ABORT_QSX_FILE_INVALID_FI LEHANDLE "INVALID_OPERATI ON" 201239 31217 qpERR_ABORT_QSX_FILE_INVALID_OP ERATION "READ_ERROR" 201242 3121A qpERR_ABORT_QSX_FILE_READ_ERRO R "WRITE_ERROR" 201243 3121B qpERR_ABORT_QSX_FILE_WRITE_ERR OR "ACCESS_DENIED" 201236 31214 qpERR_ABORT_QSX_DIRECTORY_ACCE SS_DENIED "DELETE_FAILED" 201240 31218 qpERR_ABORT_QSX_FILE_DELETE_FAI LED "RENAME_FAILED" 201241 31219 qpERR_ABORT_QSX_FILE_RENAME_FA ILED 예외 처리 171 SQLCODE와 SQLERRM SQLCODE, SQLERRM은 SQL문 수행 시 발생한 예외에 해당하는 에러코드와 메시지를 얻어와서 이에 대한 적절한 대응을 하기 위해 Exception Handler에서 사용된다. SQLCODE, SQLERRM에 에러가 세팅 되는 경우는 다음과 같다. 저장 프로시저 실행 도중 에러가 발생한 경우 사용자정의 예외가 발생한 경우 시스템-정의 예외가 발생한 경우 사용자가 RAISE_APPLICATION_ERROR로 자신이 정의한 에러를 발생시킨 경우 Exception Handler 내에서 다시 RAISE하는 경우 위와 같은 경우 기존의 SQLCODE와 SQLERRM는 새로 발생한 에러코드와 에러메시지로 변경된다. 또한, Exception Handler가 정상적으로 동작한 후에는 SQLCODE와 SQLERRM의 값은 예외 발생 이전의 에러코드와 에러메시지로 원복 되는데, 이는 LIFO (last in, first out) 스택의 원리로 동작하기 때문이다. 따라서, 한번 발생한 예외로 인해 SQLCODE와 SQLERRM에 셋팅된 값은 그 블록의 상위 블록으로 제어가 넘어가지 않는 한 스택에 계속 남아있게 된다. 다음의 예제를 보면, CREATE OR REPLACE PROCEDURE PROC1 AS BEGIN BEGIN RAISE NO_DATA_FOUND; EXCEPTION WHEN OTHERS THEN BEGIN PRINTLN('1SQLCODE : '||SQLCODE); PRINTLN('1SQLERRM : '||SQLERRM); RAISE VALUE_ERROR; EXCEPTION WHEN OTHERS THEN PRINTLN('2SQLCODE : '||SQLCODE); PRINTLN('2SQLERRM : '||SQLERRM); END; 172 Stored Procedures Manual PR I N T L N ( ' 3 SQ L C O D E : ' | | SQ L C O D E); PR I N T L N ( ' 3 SQ L ER R M : ' | | SQ L E R R M ) ; END; PRINTLN('4SQLCODE : '||SQLCODE); PRINTLN('4SQLERRM : '||SQLERRM); END; / 위 예제의 경우 다음과 같은 결과를 출력한다. 1SQLCODE : 201066 1SQLERRM : No data found. 0005 : RAISE NO_DATA_FOUND; 2SQLCODE : 201071 2SQLERRM : Value error 0011 : RAISE VALUE_ERROR; 3SQLCODE : 201066 3SQLERRM : No data found. 0005 : RAISE NO_DATA_FOUND; 4SQLCODE : 0 4SQLERRM : Successfully completed Execute success. 이를 도식화하면 SQLCODE, SQLERRM의 범위는 다음과 같음을 알 수 있다. 예외 처리 173 CREATE OR REPLACE PROCEDURE PROC1 AS BEGIN BEGIN RAISE NO_DATA_FOUND; EXCEPTION WHEN OTHERS THEN BEGIN PRINTLN('1SQLCODE : '||SQLCODE); PRINTLN('1SQLERRM : '||SQLERRM); RAISE VALUE_ERROR; EXCEPTION WHEN OTHERS THEN PRINTLN('2SQLCODE : '||SQLCODE); PRINTLN('2SQLERRM : '||SQLERRM); END; PRINTLN('3SQLCODE : '||SQLCODE); PRINTLN('3SQLERRM : '||SQLERRM); END; PRINTLN('4SQLCODE : '||SQLCODE); PRINTLN('4SQLERRM : '||SQLERRM); END; / 0, Successfully completed 201066, No Data found. 201066, No Data found. 201071, Value error. 0, Successfully completed RAISE Exception Handled Exception Handled RAISE 174 Stored Procedures Manual Exception Handler 구문 exception_handler ::= WHEN exception_name ORexception_name OTHERS THENstatement 기능 Exception Handler에는 예외가 발생했을 때의 처리 루틴을 기술한다. 예외가 발생했을 때, 알티베이스는 어느 exception handler에 제어를 넘길 것인지 결정한다. Exception Handler를 찾아내는 규칙은 다음과 같다. 현재 블록부터 시작하여 현재 블록을 포함하고 있는 바깥 블록들로 예외의 이름이 같은 Exception Handler를 찾는다. 도중에 어느 한 블록에서라도 OTHERS 핸들러를 만나게 되면 OTHERS 핸들러에서 예외처리를 하게 된다. 맨 바깥블록까지 Exception Handler가 발견되지 않는다면, 사용자에게 “Unhandled Exception” 에러를 출력하고 프로시저의 수행은 즉시 중지된다. 발생한 에러를 확인하기 위해 exception handler에서 SQLCODE와 SQLERRM을 사용할 수 있다. 즉, SQLCODE는 알티베이스 에러코드 번호를 반환하고, SQLERRM은 대응하는 에러 메시지를 반환하다. 예외 처리 175 SQLCODE와 SQLERRM은 SQL구문에서 직접 사용할 수는 없다. 대신에 그 값을 지역 변수에 대입하고 그 변수를 SQL 구문에서 사용하면 된다. exception name 처리 하고자 하는 시스템-정의 예외 또는 사용자정의 예외의 이름을 기술한다. 예외 발생시에 동일한 처리를 하고자 하는 예외들을 OR로 묶어서 하나의 루틴으로 처리할 수 있다. others 이전에 기술된 모든 Exception Handler에서 현재 발생한 예외를 처리하지 못할 경우 최종적으로 OTHERS 루틴에서 처리된다. 예제 예제1 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE TABLE t2(i1 INTEGER, i2 INTEGER, i3 INTEGER); INSERT INTO t1 VALUES(1,1,1); INSERT INTO t1 VALUES(2,2,2); CREATE OR REPLACE PROCEDURE proc1 AS BEGIN DECLARE CURSOR c1 IS SELECT * FROM t1; v1 INTEGER; v2 INTEGER; v3 INTEGER; BEGIN -- OPEN c1; FETCH c1 INTO v1, v2, v3; INSERT INTO t2 VALUES (v1, v2, v3); CLOSE c1; 176 Stored Procedures Manual EXCEPTION WHEN INVALID_CURSOR THEN INSERT INTO t2 VALUES (-999, -999, -999); END; END; / iSQL> EXEC proc1; Execute success. iSQL> SELECT * FROM t2; T2.I1 T2.I2 T2.I3 ---------------------------------------- -999 -999 -999 1 row selected. 예제2 CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER); CREATE OR REPLACE PROCEDURE proc1(p1 IN INTEGER) AS v1 INTEGER; err1 EXCEPTION; BEGIN IF p1 < 0 THEN RAISE err1; END IF; SELECT i1 INTO v1 FROM t1; EXCEPTION WHEN NO_DATA_FOUND OR TOO_MANY_ROWS THEN INSERT INTO t1 VALUES(1,1,1); WHEN OTHERS THEN INSERT INTO t1 VALUES(0,0,0); END; / iSQL> EXEC proc1(1); Execute success. iSQL> SELECT * FROM t1; 예외 처리 177 T1.I1 T1.I2 T1.I3 ---------------------------------------- 1 1 1 1 row selected. iSQL> EXEC proc1(-8); Execute success. iSQL> SELECT * FROM t1; T1.I1 T1.I2 T1.I3 ---------------------------------------- 1 1 1 0 0 0 2 rows selected. 예제3 CREATE TABLE t1(i1 INTEGER NOT NULL); CREATE OR REPLACE PROCEDURE proc1 AS code INTEGER; errm VARCHAR(200); BEGIN INSERT INTO t1 VALUES(NULL); EXCEPTION WHEN OTHERS THEN -- 변수 code에 SQLCODE 에러코드 값 대입 code := SQLCODE; -- 변수 errm에 SQLERRM 에러 메시지 저장 errm := SUBSTRING(SQLERRM, 1, 200); system_.println('SQLCODE : ' || code); system_.println('SQLERRM : ' || errm); END; / iSQL> EXEC proc1; SQLCODE : 822558860 SQLERRM : Unable to insert or update the NULL value to a NOT NULL column 0006 : INSERT INTO T1 VALUES(NULL); Execute success. 내장 함수와 저장 프로시저 179 10. 내장 함수와 저장 프로시저 알티베이스는 다양한 종류의 내장된 저장 프로시저와 함수를 제공한다. 저장 프로시저 내에서의 파일 제어 함수와 DataPort로 통칭되는 이전 (migration) 관련 저장 프로시저가 그것이다. 이 장은 이들 저장 프로시저와 함수를 소개하고 그 사용법에 대해 설명한다. 이 장은 아래의 토픽을 포함한다. 파일 제어 DataPort 그 외 함수들 180 Stored Procedures Manual 파일 제어 프로시저의 파일 제어 기능은 운영 체제의 텍스트 파일에 대한 읽기 및 쓰기를 가능하게 한다. 이 기능을 이용하여 사용자는 저장 프로시저 실행에 대한 별도의 메시지 등을 파일에 남길 수도 있으며, 파일로 결과를 출력 하거나 파일로부터 데이터를 읽어와 테이블에 삽입하는 등 다양한 작업을 수행할 수 있다. 이 절은 이러한 파일 제어 기능에 대해서 설명한다. 디렉터리 관리 저장 프로시저에서 파일들을 생성하고 제어하기 위해서는 이들 파일들이 저장될 디렉터리가 필요한데, 이는 데이터베이스 객체로서 DML문을 사용해서 생성하고 관리할 수 있다. 디렉터리 생성 저장 프로시저 파일 제어 기능에서 사용하는 파일들을 저장할 디렉터리들은 CREATE DIRECTORY문을 사용하여 데이터베이스 객체로 생성한다. CREATE DIRECTORY문을 수행하면 SYS_DIRECTORIES_ 메타 테이블에 디렉터리 정보가 등록되며, 실제 운영 체제의 파일 시스템에 디렉터리가 생성되지는 않는다. 따라서 사용자는 실제 파일 시스템에 디렉터리를 생성하는 작업을 먼저 수동으로 해야 한다. 사용자는 CREATE DIRECTORY문에 데이터베이스가 참조할 논리적인 디렉터리명과 실제 파일 시스템 상에서의 디렉터리 절대 경로를 명시해야 한다. 예를 들어 다음과 같이 /home/알티베이스/알티베이스_home/psm_msg 디렉터리 밑에 alti_dir1 디렉터리를 생성한다. $ mkdir /home/altibase/altibase_home/psm_msg/alti_dir1 다음으로, alti_dir1 디렉터리 내의 파일들을 제어할 수 있도록 대응하는 디렉터리 객체를 데이터베이스 내에 생성한다. iSQL> create directory alti_dir1 as '/home/altibase/altibase_home/psm_msg'; Create success. 디렉터리 변경 내장 함수와 저장 프로시저 181 CREATE OR REPLACE DIRECTORY문을 사용해 이미 생성한 디렉터리의 절대 경로를 다음과 같이 변경할 수 있다. iSQL> create or replace directory alti_dir1 as '/home/altibase/altibase_home/psm_result'; Create success. 위의 예제에서 alti_dir1 디렉터리가 이미 데이터베이스에 존재할 경우에는 사용자가 명시한 절대 경로 정보를 변경하며, alti_dir1 디렉터리가 존재하지 않을 경우에는 새로운 객체를 데이터베이스에 생성한다. 디렉터리 삭제 디렉터리 객체는 DROP DIRECTORY문을 사용해서 데이터베이스에서 삭제할 수 있다. DROP DIRECTORY문을 사용해 디렉터리를 삭제하는 경우 데이터베이스에서 관리하는 오브젝트만 삭제되며 실제 파일 시스템 상의 디렉터리가 제거되는 것은 아니다. 따라서 사용자는 파일 시스템 상에 존재하는 불필요한 디렉터리와 파일들은 운영 체제 명령어를 이용해 직접 제거해야 한다. 다음은 DROP DIRECTORY문을 사용해 데이터베이스에서 디렉터리를 삭제하는 예제이다. iSQL> DROP DIRECTORY alti_dir1; Drop success. 파일 제어 파일 타입 저장 프로시저 내에서 파일 제어를 위해서 알티베이스는 FILE_TYPE이라는 데이터 타입을 지원한다. FILE_TYPE은 내부적으로 파일 식별자 및 기타 정보를 가지고 있으나, 사용자가 직접 이 내부 데이터에 접근할 수는 없다. 저장 프로시저 내에서 FILE_TYPE 데이터 타입의 지역변수들은 파일 제어 관련 시스템 저장 프로시저 및 저장 함수들의 인자로 사용될 수 있다. [예제] FILE_TYPE으로 변수를 선언하는 예제는 다음과 같다. 182 Stored Procedures Manual C R EAT E O R R EP L AC E PR O C ED U R E W R I T E_ T 1 AS V1 FILE_TYPE; ID INTEGER; NAME VARCHAR(40); BEGIN …… END; / 파일 제어 프로시저와 함수 알티베이스는 저장 프로시저 및 저장 함수 내에서 파일 제어와 관련해서 다음과 같은 시스템 프로시저와 함수를 제공한다. 시스템 프로시저/함수명 설명 FCLOSE 파일을 닫는다. FCLOSE_ALL 현재 세션에 열려있는 모든 파일을 닫는다. FCOPY 파일을 복사한다. FFLUSH 파일에 데이터를 물리적으로 기록한다. FOPEN 읽기 또는 쓰기 목적으로 파일을 오픈한다. FREMOVE 파일을 삭제한다. FRENAME 파일명을 변경한다. GET_LINE 파일에서 한 라인을 읽는다. IS_OPEN 파일이 열려있는지 검사한다. NEW_LINE 개행 문자를 출력한다. PUT 문자열을 파일에 기록한다 PUT_LINE 문자열에 개행 문자를 붙여서 파일에 기록한다 (= PUT+NEW_LINE)한다. 위 표의 시스템 프로시저와 함수들은 최초 데이터베이스 생성시 시스템 내에서 자동 생성되는 저장 프로시저 및 저장 함수로 PUBLIC 시노님으로 정의되어 있어 임의의 사용자가 이들을 이용해 저장 프로시저 내에서 파일을 제어 할 수 있다. 이러한 시스템 프로시저 및 함수를 사용한 파일 제어 작업은 다음 그림과 같이 표현된다. 내장 함수와 저장 프로시저 183 시스템 상에 directory를 생성하고 권한 부여 Directory Object 생성 FILE_TYPE 변수 선언 FOPEN FCLOSE FFLUSHGET_LINEIS_OPEN NEW_LINEPUTPUT_LINE FCOPYFREMOVEFRENAME FCLOSE_ALL 파일 복사 , 삭제 , 이동 파일 읽기 , 쓰기 세션에 닫혀있지 않은 File Handle 을 모두 당음 주의사항 다음은 저장 프로시저 실행 시 오류를 발생시킬 수 있는 사항들이므로 주의해야 한다. 디렉터리 이름 파일제어 함수 사용시 디렉터리 파라미터는 CREATE DIRECTORY문으로 생성한 디렉터리 객체의 이름을 사용하되 반드시 대문자로 표기한다. 예를 들어, CREATE DIRECTORY alti_dir AS ‘…’; 위와 같이 디렉터리 객체를 생성하였다면 저장 프로시저 내에서는 다음과 같이 사용해야 한다. file = FOPEN( ‘ALTI_DIR’, ‘a.txt’, ‘r’ ); 디렉터리 생성 시 소문자로 디렉터리 객체의 이름을 명시하여도 데이터베이스 내 객체 이름은 모두 대문자로 저장되기 때문에 시스템 프로시저 및 함수의 파라미터로 디렉터리 이름을 입력할 때는 대문자를 사용해야 한다. 파일 오픈 모드 184 Stored Procedures Manual 파일 오픈 모드는 반드시 소문자( ‘r’, ‘w’, ‘a’ )여야 한다. 예를 들어 다음과 같이 사용해야 한다. file = FOPEN( ‘ALTI_DIR’, ‘a.txt’, ‘r’ ); 한 라인의 문자열 길이 파일 내 한 라인의 최대 문자열 길이는 32767 bytes를 넘을 수 없다. 만약 최대 길이를 초과할 경우 오류가 발생한다. 파일 데이터 타입 FILE_TYPE은 사용자가 임의로 변수 값을 대입하거나 정보를 읽을 수 없으며, 시스템 프로시저 및 함수의 파라미터로만 사용할 수 있다. 파일 제어 관련 시스템 프로시저 및 함수 파일 제어 관련 시스템 프로시저 및 함수들은 기본적인 시스템-정의 예외 외에 다른 예외들을 발생시킬 수 있다. 예를 들면 디스크 공간 부족, 열 수 있는 파일 핸들 부족, 또는 운영체제 상에서 오류가 발생할 경우 INVALID OPERATION 등의 예기치 않은 오류를 발생시킨다. 파일 제어 관련 시스템 프로시저 및 함수들은 인자를 잘못 넘겨 받은 경우 VALUE_ERROR EXCEPTION을 발생시킨다. FCLOSE 열려 있는 파일 핸들을 닫고 다시 초기화 하는 기능을 제공하는 저장 프로시저다. 구문 FCLOSE ( file IN OUT FILE_TYPE ); 파라미터 이름 입출력 데이터 타입 설명 file IN OUT FILE_TYPE 파일 핸들 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 내장 함수와 저장 프로시저 185 수행 시 오류 발생 없이 항상 성공한다. 이미 닫힌 파일 핸들에 대해 수행할 때도 오류 없이 성공한다. 예제 FOPEN후에는 FCLOSE를 호출하여 열린 파일 핸들을 다음과 같이 닫아 주어야 한다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 FILE_TYPE; V2 VARCHAR(1024); BEGIN V1 := FOPEN( 'ALTI_DIR', 'schema.sql', 'r' ); GET_LINE( V1, V2, 100 ); PRINTLN(V2); FCLOSE(V1); END; / FCLOSE_ALL 현재 세션에 열려있는 모든 파일 핸들을 닫는 기능을 제공하는 저장 프로시저다. 저장 프로시저 수행 중 예외가 발생했을 때에도 파일을 닫기 위해서, 주로 예외 처리 시에 사용한다. 구문 FCLOSE_ALL; 파라미터 파라미터가 없다. 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 수행 시 오류를 발생시키지 않으며 항상 성공한다. 예제 186 Stored Procedures Manual 다음은 예외 처리 시 열려 있는 모든 파일 핸들을 닫는 예제이다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 FILE_TYPE; V2 VARCHAR(1024); BEGIN V1 := FOPEN( 'ALTI_DIR', 'schema.sql', 'r' ); GET_LINE( V1, V2, 100 ); PRINTLN(V2); FCLOSE(V1); EXCEPTION WHEN READ_ERROR THEN PRINTLN('READ ERROR!!!'); FCLOSE_ALL; END; / FCOPY 파일을 라인 단위로 복사하는 기능을 제공하는 저장 프로시저이다. 결과 파일이 해당 디렉터리 내에 존재하지 않을 경우에는 새로운 파일을 생성하여 소스 파일 내용을 복사하고, 이미 결과 파일이 존재하는 경우에는 오류 없이 그대로 내용을 덮어 쓴다. 구문 FCOPY ( location IN VARCHAR, filename IN VARCHAR, dest_dir IN VARCHAR, dest_file IN VARCHAR, start_line IN INTEGER DEFAULT 1, end_line IN INTEGER DEFAULT NULL); 파라미터 이름 입출력 데이터 타입 설명 location IN VARCHAR 소스 파일이 위치하는 경로에 해당하는 디렉터리 객체의 이름 filename IN VARCHAR 소스 파일의 이름 내장 함수와 저장 프로시저 187 dest_dir IN VARCHAR 결과 파일이 위치하는 경로에 해당하는 디렉터리 객체의 이름 dest_file IN VARCHAR 결과 파일의 이름 start_line IN INTEGER 복사할 시작 라인 번호 기본값: 1 end_line IN INTEGER 복사할 마지막 라인 번호. NULL로 주게 되면 파일의 끝까지 복사한다. 기본값: NULL 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 FCOPY는 다음의 시스템-정의 예외들을 발생시킬 수 있다. INVALID_PATH ACCESS_DENIED INVALID_OPERATION READ_ERROR WRITE_ERROR 예외 처리에 대한 자세한 설명은 이 장의 “파일 제어 예외 처리” 절을 참조한다. 예제 다음은 a.txt의 파일의 모든 내용을 b.txt에 복사하는 에제이다. iSQL> EXEC FCOPY( 'ALTI_DIR', 'a.txt', 'ALTI_DIR', 'b.txt' ); Execute success. $ cat a.txt 1-ABCDEFG 2-ABCDEFG 3-ABCDEFG 4-ABCDEFG 5-ABCDEFG 6-ABCDEFG 7-ABCDEFG 8-ABCDEFG 9-ABCDEFG 188 Stored Procedures Manual 10-ABCDEFG $ cat b.txt 1-ABCDEFG 2-ABCDEFG 3-ABCDEFG 4-ABCDEFG 5-ABCDEFG 6-ABCDEFG 7-ABCDEFG 8-ABCDEFG 9-ABCDEFG 10-ABCDEFG 다음은 특정 라인만을 a.txt에서 b.txt로 복사하는 예제이다. iSQL> EXEC FCOPY( 'ALTI_DIR', 'a.txt', 'ALTI_DIR2', 'b.txt', 4, 9 ); Execute success. $ cat a.txt 1-ABCDEFG 2-ABCDEFG 3-ABCDEFG 4-ABCDEFG 5-ABCDEFG 6-ABCDEFG 7-ABCDEFG 8-ABCDEFG 9-ABCDEFG 10-ABCDEFG $ cat b.txt 4-ABCDEFG 5-ABCDEFG 6-ABCDEFG 7-ABCDEFG 8-ABCDEFG 9-ABCDEFG FFLUSH 파일에 물리적으로 기록하는 기능을 제공하는 저장 프로시저다. 내장 함수와 저장 프로시저 189 구문 FFLUSH ( file IN FILE_TYPE ); 파라미터 이름 입출력 데이터 타입 설명 file IN FILE_TYPE 파일 핸들 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 FFLUSH는 다음의 시스템-정의 예외들을 발생시킬 수 있다. INVALID_FILEHANDLE WRITE_ERROR 예외 처리에 대한 자세한 설명은 이 장의 “파일 제어 예외 처리” 절을 참조한다. 예제 다음은 T1 테이블의 I1 칼럼의 모든 데이터를 파일에 한번에 기록하는 예제로 PUT_LINE의 마지막 인자인 autoflush 에 FALSE를 넘겨 PUT_LINE 호출 때마다 flush 하지 않고 마지막에 한번 FFLUSH를 호출해 flush 하는 예제이다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 FILE_TYPE; R2 T1%ROWTYPE; CURSOR C1 IS SELECT I1 FROM T1; BEGIN V1 := FOPEN( 'ALTI_DIR', 'a.txt', 'w' ); FOR R2 IN C1 LOOP PUT_LINE( V1, R2.I1, FALSE ); END LOOP; FFLUSH(V1); FCLOSE(V1); EXCEPTION WHEN INVALID_PATH THEN PRINTLN('CANNOT OPEN FILE.'); 190 Stored Procedures Manual W H EN N O _ D AT A_ F O U N D T H EN PR I N T L N ( ' N O D A T A F O U N D . ' ) ; F C L O SE( V1 ) ; EN D ; / FOPEN 파일을 열고 파일 핸들을 반환하는 기능을 제공하는 저장 함수이다. 구문 FILE_TYPE variable := FOPEN ( location IN VARCHAR, filename IN VARCHAR, open_mode IN VARCHAR ); 파라미터 이름 입출력 데이터 타입 설명 location IN VARCHAR 파일이 위치하는 경로에 해당하는 디렉터리 객체의 이름 filename IN VARCHAR 파일의 이름 open_mode IN VARCHAR 입력 가능 옵션은 다음 세 가지이다. r: 읽기 w: 쓰기 a: 이어 쓰기 주의 사항: rw, wa와 같이 조합해서 사용 불가하며 반드시 소문자로 써야 한다. 결과값 성공적으로 수행할 경우 데이터 타입이 FILE_TYPE인 파일 핸들을 반환한다. 예외 FOPEN은 다음의 시스템-정의 예외들을 발생시킬 수 있다. INVALID_PATH 내장 함수와 저장 프로시저 191 ACCESS_DENIED INVALID_OPERATION INVALID_MODE 예외 처리에 대한 자세한 설명은 이 장의 “파일 제어 예외 처리” 절을 참조한다. 예제 파일을 읽거나 쓰기 위해서는 우선 FOPEN을 사용해 다음과 같이 파일을 열어야 한다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 FILE_TYPE; V2 VARCHAR(1024); BEGIN V1 := FOPEN( 'ALTI_DIR', 'schema.sql', 'r' ); GET_LINE( V1, V2, 100 ); PRINTLN(V2); FCLOSE(V1); END; / FREMOVE 해당 파일을 삭제하는 기능을 제공하는 저장 프로시저다. 구문 FREMOVE ( location IN VARCHAR, filename IN VARCHAR); 파라미터 이름 입출력 데이터 타입 설명 location IN VARCHAR 파일이 위치하는 경로에 해당하는 디렉터리 객체의 이름 filename IN VARCHAR 파일의 이름 결과값 192 Stored Procedures Manual 저장 프로시저이므로 반환하는 결과값은 없다. 예외 FREMOVE는 다음의 시스템-정의 예외들을 발생시킬 수 있다. INVALID_PATH ACCESS_DENIED DELETE_FAILED 예외 처리에 대한 자세한 설명은 이 장의 “파일 제어 예외 처리” 절을 참조한다. 예제 다음은 파일을 삭제하는 예제이다. --## 현재 디렉터리 내의 파일 리스트 $ ls a.sql a.txt b.txt schema.sql --## FREMOVE 실행 iSQL> EXEC FREMOVE('ALTI_DIR','b.txt'); Execute success. --# 저장 프로시저 수행 후 디렉터리내의 파일 리스트 $ ls a.sql a.txt schema.sql FRENAME UNIX mv 명령어와 동일한 기능을 가지며, 파일의 이름을 바꾸거나, 다른 위치로 옮기는 기능을 제공하는 저장 프로시저다. 구문 FRENAME ( location IN VARCHAR, filename IN VARCHAR, dest_dir IN VARCHAR, dest_file IN VARCHAR, overwrite IN BOOLEAN DEFAULT FALSE ); 내장 함수와 저장 프로시저 193 파라미터 이름 입출력 데이터 타입 설명 location IN VARCHAR 원본 파일이 위치하는 경로에 해당하는 디렉터리 filename IN VARCHAR 원본 파일의 이름 dest_dir IN VARCHAR 결과 파일이 위치하는 경로에 해당하는 디렉터리 dest_file IN VARCHAR 결과 파일의 이름 overwrite IN BOOLEAN 이미 파일이 존재하는 경우 덮어 쓸지 여부를 지정한다. TRUE: 기존 파일을 새로운 파일로 덮어 쓴다. FALSE: 덮어 쓰지 않는다. 기본값: FALSE 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 FRENAME에서 발생 가능한 시스템-정의 예외들은 다음과 같다. INVALID_PATH ACCESS_DENIED RENAME_FAILED 예외 처리에 대한 자세한 설명은 이 장의 “파일 제어 예외 처리” 절을 참조한다. 예제 다음은 a.txt 파일을 result.txt로 이름을 변경하는 예제이다. --## 현재 디렉터리내의 파일 리스트 $ ls a.sql a.txt schema.sql --## FRENAME 수행 iSQL> EXEC FRENAME('ALTI_DIR','a.txt','ALTI_DIR','result.txt',TRUE); Execute success. --# 저장 프로시저 수행 후 디렉터리내의 파일 리스트 194 Stored Procedures Manual $ l s a . s q l r e s u l t . t x t s c h e m a . s q l GET_LINE 해당 파일에서 한 줄씩 읽어오는 기능을 제공하는 저장 프로시저다. 구문 GET_LINE ( file IN FILE_TYPE, buffer OUT VARCHAR, len IN INTEGER DEFAULT NULL); 파라미터 이름 입출력 데이터 타입 설명 file IN FILE_TYPE 파일 핸들 buffer OUT VARCHAR 파일에서 읽은 한 라인을 저장할 버퍼 len IN INTEGER 파일의 한 라인에서 읽어 올 최대 bytes 수로 입력하지 않을 경우 1024 bytes 크기만큼 읽어온다. 기본값: NULL 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 GET_LINE에서 발생 가능한 시스템-정의 예외는 다음과 같다. NO_DATA_FOUND READ_ERROR INVALID_FILEHANDLE 예외 처리에 대한 자세한 설명은 이 장의 “파일 제어 예외 처리” 절을 참조한다. 예제 다음은 파일의 한 라인에서 100 bytes를 읽어 출력하는 예제이다. 내장 함수와 저장 프로시저 195 iSQL> CREATE OR REPLACE PROCEDURE PROC1 2 AS 3 V1 FILE_TYPE; 4 V2 VARCHAR(1024); 5 BEGIN 6 V1 := FOPEN( 'ALTI_DIR', 'schema.sql', 'r' ); 7 GET_LINE( V1, V2, 100 ); 8 PRINTLN(V2); 9 FCLOSE(V1); 10 END; 11 / Create success. iSQL> EXEC PROC1; create table t1 (i1 integer, i2 integer, i3 integer); Execute success. IS_OPEN 파일이 열려 있는지 여부를 검사하는 기능을 제공하는 저장 함수다. 구문 BOOLEAN variable := IS_OPEN ( file IN FILE_TYPE ); 파라미터 이름 입출력 데이터 타입 설명 file IN FILE_TYPE 파일 핸들 결과값 결과값은 BOOLEAN 데이터 타입으로 열려있으면 TRUE, 열려있지 않으면 FALSE를 반환한다. 예외 파일 핸들이 정상적으로 열려 있는 경우에 TRUE를 반환하며 그 외의 경우에는 모두 FALSE를 반환하므로 수행 시 오류가 발생하지 않는다. 예제 196 Stored Procedures Manual 다음은 파일 핸들이 열려 있는지 없는지 검사하는 예제이다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 FILE_TYPE; BEGIN IF IS_OPEN(V1) = FALSE THEN PRINTLN('V1 IS NOT OPENED.'); ELSE PRINTLN('V1 IS OPENED.'); END IF; V1 := FOPEN( 'ALTI_DIR', 'a.txt', 'w' ); PRINTLN('FOPEN FUNCTION CALLED.'); IF IS_OPEN(V1) = FALSE THEN PRINTLN('V1 IS NOT OPENED.'); ELSE PRINTLN('V1 IS OPENED.'); END IF; FCLOSE( V1 ); PRINTLN('FCLOSE FUNCTION CALLED.'); IF IS_OPEN(V1) = FALSE THEN PRINTLN('V1 IS NOT OPENED.'); ELSE PRINTLN('V1 IS OPENED.'); END IF; END; / NEW_LINE 파일에 해당 개수의 개행 문자를 기록하는 기능을 제공하는 저장 프로시저다. 구문 NEW_LINE ( file IN FILE_TYPE, lines IN INTEGER DEFAULT 1 ); 파라미터 이름 입출력 데이터 타입 설명 file IN FILE_TYPE 파일 핸들 내장 함수와 저장 프로시저 197 lines IN INTEGER 기록할 라인의 수 기본값: 1 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 NEW_LINE에서 발생 가능한 시스템-정의 예외는 다음과 같다. INVALID_FILEHANDLE WRITE_ERROR 예외 처리에 대한 자세한 설명은 이 장의 “파일 제어 예외 처리” 절을 참조한다. 예제 다음은 파일에 문자열을 기록하는 예제이다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 FILE_TYPE; BEGIN V1 := FOPEN( 'ALTI_DIR', 'a.txt', 'w' ); PUT_LINE( V1, 'REPORT', TRUE ); NEW_LINE( V1, 3 ); PUT_LINE( V1, '------', TRUE ); FCLOSE( V1 ); END; / --## 위의 저장 프로시저 수행 후 a.txt 파일 결과 $ cat a.txt REPORT ------ $ PUT 198 Stored Procedures Manual 파일에 문자열을 기록하는 기능을 제공하는 저장 프로시저다. 구문 PUT ( file IN FILE_TYPE, buffer IN VARCHAR); 파라미터 이름 입출력 데이터 타입 설명 file IN FILE_TYPE 파일 핸들 buffer IN VARCHAR 기록할 문자열을 저장하고 있는 버퍼 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 PUT은 다음의 시스템-정의 예외들을 발생시킬 수 있다. INVALID_FILEHANDLE WRITE_ERROR 예외 처리에 대한 자세한 설명은 이 장의 “파일 제어 예외 처리” 절을 참조한다. 예제 다음은 파일에 문자열을 기록하는 예제이다. CREATE OR REPLACE PROCEDURE PROC1 AS V1 FILE_TYPE; BEGIN V1 := FOPEN( 'ALTI_DIR', 'a.txt', 'w' ); PUT( V1, 'REPORT'); PUT( V1, '-->'); PUT_LINE( V1, 'SUCCESS', TRUE ); FCLOSE( V1 ); END; / --## 위의 저장 프로시저 수행 후 a.txt 파일 결과 내장 함수와 저장 프로시저 199 $ cat a.txt REPORT-->SUCCESS $ PUT_LINE 파일에 문자열을 포함한 한 라인을 기록하는 기능을 제공하는 저장 프로시저다. 구문 PUT_LINE ( file IN FILE_TYPE, buffer IN VARCHAR, autoflush IN BOOLEAN DEFAULT FALSE); 파라미터 이름 입출력 데이터 타입 설명 file IN FILE_TYPE 파일 핸들 Buffer IN VARCHAR 기록할 문자열을 저장하고 있는 버퍼 autoflush IN BOOLEAN 호출할 때마다 flush할지 여부 기본값: FALSE 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 PUT_LINE는 다음의 시스템-정의 예외들을 발생시킬 수 있다. INVALID_FILEHANDLE WRITE_ERROR 예외 처리에 대한 자세한 설명은 이 장의 “파일 제어 예외 처리” 절을 참조한다. 예제 다음은 파일에 문자열의 라인을 기록하는 예제이다. CREATE OR REPLACE PROCEDURE PROC1 200 Stored Procedures Manual AS V1 FILE_TYPE; BEGIN V1 := FOPEN('ALTI_DIR', 'a.txt', 'w'); PUT_LINE(V1, '1-ABCDEFG'); PUT_LINE(V1, '2-ABCDEFG'); PUT_LINE(V1, '3-ABCDEFG'); PUT_LINE(V1, '4-ABCDEFG'); PUT_LINE(V1, '5-ABCDEFG'); PUT_LINE(V1, '6-ABCDEFG'); PUT_LINE(V1, '7-ABCDEFG'); PUT_LINE(V1, '8-ABCDEFG'); PUT_LINE(V1, '9-ABCDEFG'); PUT_LINE(V1, '10-ABCDEFG'); FCLOSE(V1); END; / 위의 저장 프로시저를 수행한 후 파일 내용은 다음과 같다. $ cat a.txt 1-ABCDEFG 2-ABCDEFG 3-ABCDEFG 4-ABCDEFG 5-ABCDEFG 6-ABCDEFG 7-ABCDEFG 8-ABCDEFG 9-ABCDEFG 10-ABCDEFG 파일 제어 예외 처리 저장 프로시저 또는 함수 실행 중에 발생할 수 있는 파일 제어 관련 예외를 처리할 때 명심해야 할 몇 가지 주의 사항을 설명한다. 파일 제어 관련 내장 프로시저와 함수 실행 중 발생 가능한 예외는 다음 표와 같다. 이들 예외는 다른 시스템-정의 예외처럼 exception handler에서 처리할 수 있다. 예외 이름 설명 INVALID_PATH 해당 디렉터리 객체가 존재하지 않음 (즉 명시한 객체는 CREATE DIRECTORY문으로 내장 함수와 저장 프로시저 201 만들어진 디렉터리 객체가 아님) INVALID_MODE 파일 열기 모드의 값이 유효하지 않음 (r, w, a 중 하나를 지정해야 함) INVALID_FILEHANDLE 파일 핸들이 유효하지 않음 (파일이 열린 상태가 아님) INVALID_OPERATION 실제 디렉터리 및 파일이 파일 시스템 상에 존재하지 않거나 파일 시스템에 의해 접근이 거부됨 READ_ERROR 열기에 성공한 파일이 READ 시 존재하지 않거나, 파일 시스템에 의해 파일이 접근 거부됨 WRITE_ERROR 열기에 성공한 파일이 WRITE 시 존재하지 않거나, 파일 시스템에 의해 파일이 접근 거부되거나, 쓰기 모드로 연 파일이 아닌 경우 ACCESS_DENIED 디렉터리 객체에 대한 접근이 거부됨 (GRANT문으로 사용자에게 객체 접근 권한을 부여 해야 함) DELETE_FAILED 삭제할 파일이 존재하지 않거나, 파일 시스템에 의해 파일 접근이 거부된 경우 RENAME_FAILED overwrite옵션을 주지 않았는데 이미 바꿀 파일이 존재하거나, 기타 운영 체제 에러가 발생한 경우 예제 예제1 다음은 디렉터리와 파일 이름을 입력으로 받아 파일을 열어 파일의 내용을 조회하는 프로시저다. 이 때, 디렉터리 또는 파일 이름이 잘못 지정되거나 데이터가 존재하지 않는 빈 파일이 지정될 수도 있다. 그러므로 이 저장 프로시저는 INVALID_PATH 및 NO_DATA_FOUND 예외를 처리할 수 있는 exception handler를 포함하고 있다. --# CREATE VERIFY PROCEDURE CREATE OR REPLACE PROCEDURE PROC2( PATH VARCHAR(40), FILE VARCHAR(40) ) AS V1 FILE_TYPE; V2 VARCHAR(100); BEGIN V1 := FOPEN( PATH, FILE, 'r' ); LOOP GET_LINE( V1, V2, 100 ); 202 Stored Procedures Manual PR I N T ( V 2 ); EN D L O O P; EXC EPT I O N W H EN I N VAL I D _ PAT H T H EN PR I N T L N ( ' C AN N O T O PEN F I L E. ' ) ; W H EN N O _ D AT A_ F O U N D T H EN PR I N T L N ( ' N O D A T A F O U N D . ' ) ; F C L O SE( V1 ) ; EN D ; / 예제2 다음 예제는 테이블의 데이터를 파일로 쓰거나 파일에서 읽는 방법을 보여준다. 사용자를 생성하고 그 사용자에게 적절한 권한을 부여한다. CONNECT SYS/MANAGER; CREATE USER MHJEONG IDENTIFIED BY MHJEONG; GRANT CREATE ANY DIRECTORY TO MHJEONG; GRANT DROP ANY DIRECTORY TO MHJEONG; 테이블을 생성하고 데이터를 입력한 후, 디렉터리 객체를 생성한다. CONNECT MHJEONG/MHJEONG; CREATE TABLE T1( ID INTEGER, NAME VARCHAR(40) ); INSERT INTO T1 VALUES( 1, ‘JAKIM’ ); INSERT INTO T1 VALUES( 2, ‘PEH’ ); INSERT INTO T1 VALUES( 3, ‘KUMDORY’ ); INSERT INTO T1 VALUES( 4, ‘KHSHIM’ ); INSERT INTO T1 VALUES( 5, ‘LEEKMO’ ); INSERT INTO T1 VALUES( 6, ‘MHJEONG’ ); CREATE DIRECTORY MYDIR AS ‘/home1/mhjeong’; T1테이블의 모든 레코드를 읽어서, 그 데이터를 t1.txt파일에 쓰는 저장 프로시저를 생성한다. CREATE OR REPLACE PROCEDURE WRITE_T1 AS V1 FILE_TYPE; ID INTEGER; NAME VARCHAR(40); BEGIN DECLARE CURSOR T1_CUR IS SELECT * FROM T1; BEGIN 내장 함수와 저장 프로시저 203 OPEN T1_CUR; V1 := FOPEN( ‘MYDIR’, ‘t1.txt’, ‘w’ ); LOOP FETCH T1_CUR INTO ID, NAME; EXIT WHEN T1_CUR%NOTFOUND; PUT_LINE( V1, ‘ID : ‘||ID||’ NAME : ‘||NAME); END LOOP; CLOSE T1_CUR; FCLOSE(V1); END; END; / 파일 t1.txt의 내용을 읽어서 화면에 출력하는 저장 프로시저를 생성한다. CREATE OR REPLACE PROCEDURE READ_T1 AS BUFFER VARCHAR(200); V1 FILE_TYPE; BEGIN V1 := FOPEN( ‘MYDIR’, ‘t1.txt’, ‘r’ ); LOOP GET_LINE( V1, BUFFER, 200 ); PRINT( BUFFER ); END LOOP; FCLOSE( V1 ); EXCEPTION WHEN NO_DATA_FOUND THEN FCLOSE( V1 ); END; / 실행 결과 위에서 생성한 저장 프로시저를 실행하면, 다음의 결과가 출력된다. iSQL> exec write_t1; EXECUTE success. iSQL> exec read_t1; ID : 1 NAME : JAKIM ID : 2 NAME : PEH ID : 3 NAME : KUMDORY ID : 4 NAME : KHSHIM ID : 5 NAME : LEEKMO ID : 6 NAME : MHJEONG 204 Stored Procedures Manual EXEC U T E s u c c e s s . 파일 시스템의 해당 디렉터리에 다음의 파일이 있을 것이다. $ cd /home1/mhjeong $ cat t1.txt ID : 1 NAME : JAKIM ID : 2 NAME : PEH ID : 3 NAME : KUMDORY ID : 4 NAME : KHSHIM ID : 5 NAME : LEEKMO ID : 6 NAME : MHJEONG 내장 함수와 저장 프로시저 205 DataPort DataPort는 테이블의 데이터를 다른 알티베이스 데이터베이스로 옮길 때 사용할 수 있는 알티베이스 기능 중의 하나이다. 이 기능은 시스템-정의 저장 프로시저 형태로 제공된다. DataPort 사용 개요 데이터 이전 (migration) 을 위해서 DataPort를 사용하면 다음의 이점을 얻을 수 있다. 데이터 이전 시 더 나은 성능 실현 원본과 대상 데이터베이스가 다른 종류의 하드웨어 또는 운영체제 상에 존재하거나, 각 데이터베이스의 문자 셋이 다른 경우에도 이전 가능 알티베이스 데이터베이스에서 사용이 지원되는 모든 데이터 타입을 DataPort에서 지원 이전에 실행중에 보류된 import작업은 원래 작업에서 중복되거나 빠뜨리는 것 없이 재시작 가능 관련 메타 테이블 SYS_DATA_PORTS_: 이 메타 테이블은 진행중이거나 완료된 export와 import 작업에 대한 정보를 담고 있다. 특정 작업의 상태를 확인하려면 이 테이블을 조회하면 된다. 보류한 작업을 재시작할 때, RESUME_DP 프로시저 또한 이 테이블에서 확인한다. 더 자세한 정보는 General Reference를 참고한다. 관련 프로퍼티 다음의 DataPort관련 프로퍼티를 altibase.properties에 설정할 수 있다. DATAPORT_FILE_DIRECTORY DATAPORT_IMPORT_COMMIT_UNIT DATAPORT_IMPORT_STATEMENT_UNIT 더 자세한 정보는 General Reference를 참고한다. 206 Stored Procedures Manual 파일 입출력 관련 예외 DataPort 관련 시스템 정의 프로시저와 함수 수행 중에 예외가 발생할 수 있다. 예를 들어, 디스크 I/O 오류 같은 하드웨어 오류 또는 운영체제 에러가 발생하면, 프로시저는 INVALID_OPERATION 예외를 발생시킨다. 데이터 export 도중에 예외가 발생하면, 원본 데이터베이스에는 아무런 영향이 없다. 데이터가 export 되고 있던 파일은 0개의 행을 가진 DataPort파일처럼 인식된다. 데이터 import도중에 예외가 발생하면, 처리된 대부분의 행은 대상 데이터베이스의 테이블로 삽입될 것이다. 이 경우, 입력된 행의 개수는 DATAPORT_IMPORT_COMMIT_UNIT 와 DATAPORT_IMPORT_STATEMENT_UNIT 프로퍼티에 설정된 값에 의해 좌우된다. 이들 프로퍼티 값이 작을수록 예외 발생 상황에서 입력된 행의 개수는 증가하지만, 성능은 떨어진다. 반면, 이들 값이 클수록 성능은 높아지지만, 예외가 발생했을 때 입력에 성공하는 행의 개수는 줄어든다. DataPort 작업의 상태는 SYS_DATA_PORTS_메타 테이블을 조회해서 확인할 수 있다. 문자집합과 DataPort DataPort 저장 프로시저는 데이터에 어떤 변환도 하지 않고 그대로 export하고 import한다. 원본 데이터베이스의 문자집합이 대상 데이터베이스의 문자집합과 다르다면, 원하는 결과를 얻지 못하거나, import 작업 중 검증 과정에서 실패할 수도 있다. 이 경우 데이터를 import하기 전에 대상 데이터베이스의 문자 셋에 일치하도록 convdp 유틸리티를 사용해서 데이터를 변환해야 한다. 이 유틸리티에 대한 설명은 Utilities Manual을 참고한다. 파일 이름 규칙 이 절에서 설명하는 이름 규칙은 파일로부터 데이터를 import하는 IMPORT_FROM_FILE 저장 프로시저, 데이터를 파일에 export하는 EXPORT_TO_FILE, EXPORT_PARTITION_TO_FILE과 EXPORT_USER_TABLES저장 프로시저에 적용된다. 이 규칙은 변환 대상 파일을 찾고 출력 파일의 이름을 결정할 때 convdp유틸리티에 의해서도 사용된다. 내장 함수와 저장 프로시저 207 파일에서 데이터 import할 때 사용자가 파일 이름을 명시하면, 알티베이스는 다음의 순서로 데이터 파일을 찾는다. 1. 사용자가 명시한 이름 2. 사용자가 명시한 이름 + “.dpf” 3. 사용자가 명시한 이름 + “_0.dpf” 명시한 파일을 찾지 못하면 에러가 발생한다.알티베이스는 이중화를 위해 서버의 데이터베이스 캐릭터 셋과 내셔널 캐릭터 셋이 동일해야 한다. 위에서 기술한 세 이름을 모두 찾아 봐도 명시한 파일이 없으면, 에러가 발생한다. 사용자가 파일 이름을 명시하지 않으면 아래의 순서로 파일 이름을 찾는다. 여기서 tablename과 ownername은 각각 데이터를 import할 테이블의 이름과 그 소유자의 이름이다. 1. ownername_tablename 2. ownername_tablename + “.dpf” 3. ownername_tablename + “_0.dpf” 위에서 기술한 세 이름을 모두 찾아 봐도 명시한 파일이 없으면, 에러가 발생한다. 파일로 데이터를 export할 때 사용자가 파일 이름을 명시했다면, “_0.dpf” 를 그 이름 뒤에 덧붙인다. 파일 이름을 명시하지 않았다면, ownername_tablename_0.dpf의 형식을 가지는 이름으로 파일이 자동 생성된다. 여기서 ownername과 tablename은 각각 export될 데이터가 존재하는 테이블과 그 소유자이다. 두 경우 모두, 같은 이름의 파일이 존재하면 덮어쓴다. 또한 split 인자를 설정했는데 레코드의 개수가 그 설정된 값보다 크다면, 여러 개의 파일이 생성될 것이다. 이 경우, 파일 이름은 “_1.dpf”, “_2.dpf”, 등으로 끝날 것이다. 사용자가 명시한 파일이름 확장자 Import와 export 작업 모두의 경우, 사용자가 지정한 파일이름내의 확장자는 확장자로 처리되지 않는다. 즉, 사용자가 명시한 이름 내에 확장자 (“.dpf” 포함)가 이미 있더라도, 사용자가 명시한 파일이름의 끝에 “.dpf” 또는 “_0.dpf”가 덧붙여진다. 208 Stored Procedures Manual 제약 사항 DataPort 저장 프로시저는 존재하는 테이블에만 import할 수 있다. 즉, DataPort는 테이블을 생성하지는 않는다. 칼럼의 데이터 타입, precision, scale과 테이블에서의 칼럼들의 순서 같은 칼럼의 모든 속성이 원본 테이블과 대상테이블간에 서로 동일해야 한다. 암호화된 칼럼을 포함하는 테이블은 import 또는 export 할 수 없다. 암호화된 칼럼을 포함하는 테이블을 import 또는 export하기 전에, 암호화된 칼럼에서 암호화 제약을 제거하거나 CREATE TABLE AS SELECT (AS) 구문을 사용해서 암호화되지 않은 칼럼과 동일한 데이터를 가지는 새로운 테이블을 생성하고 이 테이블을 export한다. 대상 테이블에 트리거나 인덱스가 있거나, 어떤 칼럼이 FOREIGN KEY나 NOT NULL같은 제약조건을 갖고 있거나, 또는 그 테이블이 이중화 대상 테이블이면, 그 테이블로 데이터를 import하는 것이 불가능하다. 데이터를 import하기 전에, 이런 모든 제약조건과 객체를 제거해야 한다. DataPort 저장 프로시저와 함수 DataPort를 구성하는 저장 프로시저와 함수는 아래의 표에 목록화되어 있다. SYS사용자만이 이들 프로시저를 실행할 수 있다. 이름 설명 EXPORT_TO_FILE 한 테이블의 모든 데이터를 한 개 또는 여러 개의 파일에 export한다. EXPORT_PARTITION_TO_FILE 명시한 테이블의 한 파티션의 데이터를 한 개 또는 여러 개의 파일에 export한다. EXPORT_USER_TABLES 어떤 사용자에 속한 모든 테이블을 한 개 또는 여러 개의 파일에 export한다. IMPORT_FROM_FILE DataPort 파일에서 대상 테이블로 데이터를 import한다. CLEAR_DP SYS_DATA_PORTS_메타 테이블에서 완료된 작업에 관련된 모든 레코드를 제거한다. RESUME_DP 보류한 작업을 재시작한다. REMOVE_DP SYS_DATA_PORTS_메타 테이블에서 특정 작업을 제거한다. 내장 함수와 저장 프로시저 209 EXPORT_TO_FILE 이 프로시저는 데이터를 특정 테이블에서 다운로드 하여 명시한 디렉터리에 존재하는 DataPort 파일에 저장한다. 이 DatPort 파일은 나중에 IMPORT_FROM_FILE 프로시저를 사용해서 다른 데이터베이스에 import할 수 있다. 구문 EXPORT_TO_FILE ( owner IN VARCHAR, tablename IN VARCHAR, filename IN VARCHAR DEFAULT NULL, jobname IN VARCHAR DEFAULT NULL, split IN BIGINT DEFAULT NULL, directory IN VARCHAR DEFAULT NULL); 파라미터 이름 입출력 데이터 타입 설명 owner IN VARCHAR 원본 테이블의 소유자 이름 tablename IN VARCHAR 원본 테이블의 이름. 이는 owner 가 소유하고 있어야 한다. filename IN VARCHAR 데이터를 저장할 파일의 이름. 파일이름이 어떻게 결정되는지에 대한 자세한 설명은 “파일 이름 규칙” 절을 참고한다. jobname IN VARCHAR 작업의 이름. 명시하지 않으면, 다음 형식으로 생성된 이름이 작업에 부여된다: _ split IN BIGINT 한 파일에 저장할 행의 최대 개수. export되는 행의 개수가 이 최대값에 도달하면, 추가로 파일이 생성된다. 명시하지 않거나 NULL을 주면, 한 파일에 저장되는 행의 수는 제한이 없게 된다. 즉, 모든 데이터가 한 파일에 저장된다. directory IN VARCHAR filename으로 명시한 파일이 저장될 디렉터리의 이름. 기본 디렉터리는 DATAPORT_FILE_DIRECTORY 210 Stored Procedures Manual 프로퍼티에 설정한 디렉터리이다. 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 EXPORT_TO_FILE은 다음의 시스템-정의 예외들을 발생시킬 수 있다. RUNNING_JOB TOO_LONG_DATAPORT_DIRECTORY_PATH SUSPENDED_JOB 예외 처리에 대한 자세한 설명은 이 장의 “DataPort 예외” 절을 참조한다. 예제 다음의 예제는 SYS사용자가 소유하고 있는 TD테이블을 export하는 것을 보여준다. Filename, jobname, split과 directory인자를 명시하지 않았기 때문에, export된 데이터는 기본 디렉터리의 SYS_TD_0.dpf 파일에 저장될 것이다. iSQL> EXEC EXPORT_TO_FILE( 'SYS', 'TD' ); Export - SYS_TD 10 record(s). Execute success. EXPORT_PARTITION_TO_FILE 이 프로시저는 테이블의 특정 파티션의 데이터를 다운로드 하여 명시한 디렉터리에 존재하는 DataPort 파일에 저장한다. 이 DatPort 파일은 나중에 IMPORT_FROM_FILE 프로시저를 사용해서 다른 데이터베이스에 import할 수 있다. 구문 EXPORT_PARTITION_TO_FILE ( owner IN VARCHAR, tablename IN VARCHAR, patitionname IN VARCHAR, filename IN VARCHAR DEFAULT NULL, 내장 함수와 저장 프로시저 211 jobname IN VARCHAR DEFAULT NULL, split IN BIGINT DEFAULT NULL, directory IN VARCHAR DEFAULT NULL); 파라미터 이름 입출력 데이터 타입 설명 owner IN VARCHAR 원본 테이블의 소유자 이름 tablename IN VARCHAR 원본 테이블의 이름. 이는 owner 가 소유하고 있어야 한다. patitionname IN VARCHAR 파티션 이름. 이는 명시한 테이블내의 파티션이어야 한다. filename IN VARCHAR 데이터를 저장할 파일의 이름. 파일이름이 어떻게 결정되는지에 대한 자세한 설명은 “파일 이름 규칙” 절을 참고한다. jobname IN VARCHAR 작업의 이름. 명시하지 않으면, 다음 형식으로 생성된 이름이 작업에 부여된다: _ split IN BIGINT 한 파일에 저장할 행의 최대 개수. export되는 행의 개수가 이 최대값에 도달하면, 추가로 파일이 생성된다. 명시하지 않거나 NULL을 주면, 한 파일에 저장되는 행의 수는 제한이 없게 된다. 즉, 모든 데이터가 한 파일에 저장된다. directory IN VARCHAR filename으로 명시한 파일이 저장될 디렉터리의 이름. 기본 디렉터리는 DATAPORT_FILE_DIRECTORY 프로퍼티에 설정한 디렉터리이다. 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 EXPORT_PARTITION_TO_FILE은 다음의 시스템-정의 예외들을 발생시킬 수 있다. RUNNING_JOB 212 Stored Procedures Manual TOO_LONG_DATAPORT_DIRECTORY_PATH SUSPENDED_JOB 예외 처리에 대한 자세한 설명은 이 장의 “DataPort 예외” 절을 참조한다. 예제 다음의 예제는 SYS사용자가 소유하고 있는 TESTL테이블의 L1파티션을 export하는 것을 보여준다. Filename, jobname, split과 directory인자를 명시하지 않았기 때문에, export된 데이터는 기본 디렉터리의 SYS_TESTL_L1_0.dpf 파일에 저장될 것이다. iSQL> EXEC EXPORT_PARTITION_TO_FILE('SYS','TESTL','L1' ); Export - SYS_TESTL_L1 2 record(s). Execute success. EXPORT_USER_TABLES 이 프로시저는 명시한 사용자가 소유하고 있는 모든 테이블에서 데이터를 다운로드 하여 명시한 디렉터리에 존재하는 DataPort 파일에 저장한다. 구문 EXPORT_USER_TABLES ( owner IN VARCHAR, split IN BIGINT DEFAULT NULL, directory IN VARCHAR DEFAULT NULL); 파라미터 이름 입출력 데이터 타입 설명 owner IN VARCHAR 원본 테이블의 소유자 이름 split IN BIGINT 한 파일에 저장할 행의 최대 개수. export되는 행의 개수가 이 최대값에 도달하면, 추가로 파일이 생성된다. 명시하지 않거나 NULL을 주면, 한 파일에 저장되는 행의 수는 제한이 없게 된다. 즉, 모든 데이터가 한 파일에 저장된다. directory IN VARCHAR 결과 파일이 저장될 디렉터리의 이름. 내장 함수와 저장 프로시저 213 기본 디렉터리는 DATAPORT_FILE_DIRECTORY 프로퍼티에 설정한 디렉터리이다. 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 EXPORT_TO_FILE은 다음의 시스템-정의 예외를 발생시킬 수 있다. TOO_LONG_DATAPORT_DIRECTORY_PATH 예외 처리에 대한 자세한 설명은 이 장의 “DataPort 예외” 절을 참조한다. 예제 다음의 예제는 SYS사용자가 소유하고 있는 모든 테이블을 export하는 것을 보여준다. SYS사용자가 소유하고 있는 TD 테이블과 TESTL은 기본 디렉터리에 위치하는 다음의 파일들에 각각 저장될 것이다: SYS_TD_0.dpf 와 SYS_TESTL_0.dpf iSQL> EXEC EXPORT_USER_TABLES( 'SYS' ); USER_NAME: SYS TABLE_NAME: TD Export - SYS_TD 10 record(s). TABLE_NAME: TESTL Export - SYS_TESTL 3 record(s). Execute success. IMPORT_FROM_FILE 이 프로시저는 export 파일에서 대상 테이블로 데이터를 로드한다. 구문 IMPORT_FROM_FILE ( owner IN VARCHAR, tablename IN VARCHAR, filename IN VARCHAR DEFAULT NULL, 214 Stored Procedures Manual jobname IN VARCHAR DEFAULT NULL, firstrow IN BIGINT DEFAULT NULL, lastrow IN BIGINT DEFAULT NULL, directory IN VARCHAR DEFAULT NULL); 파라미터 이름 입출력 데이터 타입 설명 owner IN VARCHAR 대상 테이블의 소유자 이름 tablename IN VARCHAR 대상 테이블의 이름. 이는 owner 가 소유하고 있어야 한다. filename IN VARCHAR Import할 데이터가 저장되어 있는 파일의 이름. 파일이름이 어떻게 결정되는지에 대한 자세한 설명은 “파일 이름 규칙” 절을 참고한다. jobname IN VARCHAR 작업의 이름. 명시하지 않으면, 다음 형식으로 생성된 이름이 작업에 부여된다: _ firstrow IN BIGINT Import할 처음 행 (기본값은 파일의 첫 번째 행이다) lastrow IN BIGINT Import할 마지막 행 (기본값은 파일의 마지막 행이다) directory IN VARCHAR filename으로 명시한 파일이 위치하는 디렉터리의 이름. 기본 디렉터리는 DATAPORT_FILE_DIRECTORY 프로퍼티에 설정한 디렉터리이다. 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 IMPORT_FROM_FILE은 다음의 시스템-정의 예외들을 발생시킬 수 있다. RUNNING_JOB TOO_LONG_DATAPORT_DIRECTORY_PATH SUSPENDED_JOB CORRUPTED_BLOCK 내장 함수와 저장 프로시저 215 INVALID_COLUMN_SIZE CORRUPTED_HEADER DATA_PORT_INTERNAL_ERROR VERSION_NOT_SUPPORTED CANT_OPEN_FILE INCOMPATIBLE_DATA_TYPE TABLE_HAS_TRIGGERS TABLE_HAS_CONSTRAINTS TABLE_HAS_REPLICATION TABLE_HAS_INDEX TABLE_HAS_FOREIGN_KEY NOT_SAME_DATABASE_CHARSET NOT_SAME_NATIONAL_CHARSET 예외 처리에 대한 자세한 설명은 이 장의 “DataPort 예외” 절을 참조한다. 예제 다음의 예제는 기본 디렉터리에 위치하는 TDFILE, TDFILE.dpf 또는 TDFILE_0.dpf 중의 한 파일에서 SYS사용자가 소유하고 있는 TD테이블로 데이터를 import하는 것을 보여준다. iSQL> EXEC IMPORT_FROM_FILE( 'SYS', 'TD', 'TDFILE' ); SYS_TD 10 record(s) Import - SYS_TD 10 record(s). CLEAR_DP 이 프로시저는 SYS_DATA_PORTS_메타 테이블에서 완료된 import, export 작업에 관련된 레코드를 모두 삭제한다. 구문 CLEAR_DP ( ); 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 216 Stored Procedures Manual 예외를 발생시키지 않는다. RESUME_DP 현재 보류중인 import 작업을 다시 시작한다. 구문 RESUME_DP ( jobname IN VARCHAR); 파라미터 이름 입출력 데이터 타입 설명 jobname IN VARCHAR 재 시작할 작업의 이름 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 RESUME_DP은 다음의 시스템-정의 예외를 발생시킬 수 있다. INVALID_JOBNAME 예외 처리에 대한 자세한 설명은 이 장의 “DataPort 예외” 절을 참조한다. 예제 다음의 예제는 디스크 공간 부족 또는 파일 I/O 에러로 인해서 중지된 작업을 재 시작하는 것을 보여준다. iSQL> EXEC IMPORT_FROM_FILE( 'SYS', 'TD' ); TM 230 record(s) [ERR-11123: The tablespace does not have enough free space ( TBS Name:TBS1 ).] iSQL> ALTER TABLESPACE TBS1 ADD DATAFILE 'TBS2.TBS' SIZE 50M; ALTER success. iSQL> EXEC RESUME_TE( 'SYS_TD' ); TM 320 record(s) Import - TM 320 record(s). Execute success. 내장 함수와 저장 프로시저 217 REMOVE_DP SYS_DATA_PORTS_메타 테이블에서 특정 작업을 삭제한다. 보류중인 작업도 제거 가능하다. 구문 REMOVE_DP ( jobname IN VARCHAR); 파라미터 이름 입출력 데이터 타입 설명 jobname IN VARCHAR 삭제할 작업의 이름 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 REMOVE_DP은 다음의 시스템-정의 예외들을 발생시킬 수 있다. INVALID_JOBNAME RUNNING_JOB 예외 처리에 대한 자세한 설명은 이 장의 “DataPort 예외” 절을 참조한다. DataPort 예외 몇몇 시스템-정의 예외는 DataPort에만 한정된다. 즉, DataPort 저장 프로시저 실행 중에만 발생된다. 이들 예외의 목록은 다음 표와 같다. 이름 설명 CANT_OPEN_FILE 지정한 파일을 찾을 수 없다 CORRUPTED_BLOCK DataPort내의 N번째 Block이 손상되어 있다 CORRUPTED_HEADER DataPort 파일의 헤더가 손상되어 있다 DATA_PORT_INTERNAL_ERROR DataPort파일에 오류가 있다 INCOMPATIBLE_DATA_TYPE DataPort 파일과 대상 테이블이 218 Stored Procedures Manual 서로 일치하지 않는 칼럼을 포함하고 있다 INVALID_COLUMN_SIZE 삽입하려는 칼럼의 데이터 크기가 칼럼의 최대 크기를 초과했다 INVALID_JOBNAME 해당 이름을 가진 작업이 존재하지 않는다 NOT_SAME_DATABASE_CHARSET 데이터베이스의 문자집합과 DataPort의 문자집합이 다르다 NOT_SAME_NATIONAL_CHARSET 데이터베이스의 국가 문자집합과 DataPort의 국가 문자집합이 다르다 RUNNING_JOB 동일한 이름을 가진 작업이 이미 존재한다 SUSPENDED_JOB 동일한 이름을 가진 작업이 이미 보류된 상태로 존재한다 TABLE_HAS_CONSTRAINTS 테이블에 제약조건이 있어서 데이터를 import할 수 없다 TABLE_HAS_FOREIGN_KEY 테이블에 외래 키가 있어서 데이터를 import할 수 없다 TABLE_HAS_INDEX 테이블에 인덱스가 걸려 있어서 데이터를 import할 수 없다 TABLE_HAS_REPLICATION 테이블이 이중화 대상 테이블이어서 데이터를 import할 수 없다 TABLE_HAS_TRIGGERS 테이블에 트리거가 걸려 있어서 데이터를 import할 수 없다 TOO_LONG_DATAPORT_DIRECTORY_PATH 명시한 디렉토리 이름의 길이가 1024 bytes보다 크다 VERSION_NOT_SUPPORTED DataPort가 지원하지 않는 버전의 파일이다 예제 정상 수행의 경우 iSQL> CREATE TABLE TM ( I1 INTEGER, I2 CHAR(1000) ); CREATE success. iSQL> CREATE TABLE TD ( I1 INTEGER, I2 CHAR(1000) ); CREATE success. 내장 함수와 저장 프로시저 219 iSQL> INSERT INTO TM VALUES( '1', '1' ); 1 row inserted. iSQL> INSERT INTO TM VALUES( '2', '2' ); 1 row inserted. iSQL> INSERT INTO TM VALUES( '3', '3' ); 1 row inserted. iSQL> INSERT INTO TM VALUES( '4', '4' ); 1 row inserted. iSQL> INSERT INTO TM VALUES( '5', '5' ); 1 row inserted. iSQL> INSERT INTO TM VALUES( '6', '6' ); 1 row inserted. iSQL> INSERT INTO TM VALUES( '7', '7' ); 1 row inserted. iSQL> INSERT INTO TM VALUES( '8', '8' ); 1 row inserted. iSQL> INSERT INTO TM VALUES( '9', '9' ); 1 row inserted. iSQL> INSERT INTO TM VALUES( '10','10' ); 1 row inserted. iSQL> INSERT INTO TM SELECT * FROM TM; 10 rows inserted. iSQL> INSERT INTO TM SELECT * FROM TM; 20 rows inserted. iSQL> INSERT INTO TM SELECT * FROM TM; 40 rows inserted. iSQL> INSERT INTO TM SELECT * FROM TM; 80 rows inserted. iSQL> INSERT INTO TM SELECT * FROM TM; 160 rows inserted. --# Export_to_file( , , ); iSQL> EXEC EXPORT_TO_FILE( 'SYS', 'TM', 'TMFILE' ); Export - SYS_TM 320 record(s). Execute success. --# Copy the generated file, TMFILE_0.dpf, --# to the default directory, ${ALTIBASE_HOME}/dbs, on the target machine. --# Import_from_file( , , ); iSQL> EXEC IMPORT_FROM_FILE( 'SYS', 'TD', 'TMFILE' ); SYS_TD 50 record(s) SYS_TD 100 record(s) 220 Stored Procedures Manual SYS_ T D 1 5 0 r e c o rd (s ) SYS_ T D 2 0 0 r e c o rd (s ) SYS_ T D 2 5 0 r e c o rd (s ) SYS_ T D 3 0 0 r e c o rd (s ) I m p o rt - S YS_ T D 3 2 0 r e c o rd (s ). Ex e c u t e s u c c e s s . --# Clear_the meta table iSQL> EXEC CLEAR_DP(); REMOVE: SYS_TD REMOVE: SYS_TM CLEAR DATA_PORTS Execute success. 데이터 import 도중 예외 발생한 경우 iSQL> CREATE TABLE TM ( I1 INTEGER, I2 CHAR(32000) ); CREATE success. iSQL> CREATE TABLESPACE TBS1 DATAFILE 'TBS1.TBS' SIZE 10M; CREATE success. iSQL> CREATE TABLE TD ( I1 INTEGER, I2 CHAR(32000) ) TABLESPACE TBS1; CREATE success. iSQL> INSERT INTO TM VALUES( '1', '1' ); 1 row inserted. iSQL> INSERT INTO TM VALUES( '2', '2' ); 1 row inserted. iSQL> INSERT INTO TM VALUES( '3', '3' ); 1 row inserted. iSQL> INSERT INTO TM VALUES( '4', '4' ); 1 row inserted. iSQL> INSERT INTO TM VALUES( '5', '5' ); 1 row inserted. iSQL> INSERT INTO TM VALUES( '6', '6' ); 1 row inserted. iSQL> INSERT INTO TM VALUES( '7', '7' ); 1 row inserted. iSQL> INSERT INTO TM VALUES( '8', '8' ); 1 row inserted. iSQL> INSERT INTO TM VALUES( '9', '9' ); 1 row inserted. iSQL> INSERT INTO TM VALUES( '10','10' ); 1 row inserted. 내장 함수와 저장 프로시저 221 iSQL> INSERT INTO TM SELECT * FROM TM; 10 rows inserted. iSQL> INSERT INTO TM SELECT * FROM TM; 20 rows inserted. iSQL> INSERT INTO TM SELECT * FROM TM; 40 rows inserted. iSQL> INSERT INTO TM SELECT * FROM TM; 80 rows inserted. iSQL> INSERT INTO TM SELECT * FROM TM; 160 rows inserted. --# Export_to_file( , , , ); iSQL> EXEC EXPORT_TO_FILE( 'SYS', 'TM', 'TMFILE', 'SYS_TM_EXPORT' ); Export - SYS_TM_EXPORT 320 record(s). Execute success. --# In the following example, --# if there is insufficient space on the device, the operation is rolled back in units of 50 rows and --# after abnormal shutdown of a server, the operation is rolled back in units of 500(=50*10) rows. iSQL> ALTER SYSTEM SET DATAPORT_IMPORT_COMMIT_UNIT = 10; ALTER success. iSQL> ALTER SYSTEM SET DATAPORT_IMPORT_STATEMENT_UNIT = 50; ALTER success. --# Copy the generated file, TMFILE_0.dpf, --# to the default directory, ${ALTIBASE_HOME}/dbs, on the target machine. --# Import_from_file( , , , ); iSQL> EXEC IMPORT_FROM_FILE( 'SYS', 'TD', 'TMFILE', 'SYS_TM_IMPORT' ); SYS_TM_IMPORT 50 record(s) SYS_TM_IMPORT 100 record(s) SYS_TM_IMPORT 150 record(s) SYS_TM_IMPORT 200 record(s) [ERR-11123 : The tablespace does not have enough free space ( TBS Name :TBS1 ).] 222 Stored Procedures Manual i SQ L > A L T ER T AB L ES PAC E T BS 1 AD D D AT A F I L E ' T BS 2 . T BS' SI Z E 1 0 M ; AL T ER s u c c e s s . --# Resume_dp( ); iSQL> EXEC RESUME_DP( 'SYS_TM_IMPORT' ); SYS_TM_IMPORT 250 record(s) SYS_TM_IMPORT 300 record(s) Import - SYS_TM_IMPORT 320 record(s). Execute success. --# Clear_DP iSQL> EXEC CLEAR_DP(); REMOVE: SYS_TM_EXPORT REMOVE: SYS_TM_IMPORT CLEAR DATA_PORTS Execute success. 파티션을 export 하는 경우 iSQL> CREATE TABLE TM ( I1 INTEGER, I2 CHAR(1000) ); CREATE success. iSQL> CREATE TABLE TD ( I1 INTEGER, I2 CHAR(1000) ) PARTITION BY HASH ( I1 ) ( PARTITION H1, PARTITION H2, PARTITION H3 ) TABLESPACE SYS_TBS_DISK_DATA; CREATE success. iSQL> INSERT INTO TD VALUES( '1', '1' ); 1 row inserted. iSQL> INSERT INTO TD VALUES( '2', '2' ); 1 row inserted. iSQL> INSERT INTO TD VALUES( '3', '3' ); 1 row inserted. iSQL> INSERT INTO TD VALUES( '4', '4' ); 1 row inserted. iSQL> INSERT INTO TD VALUES( '5', '5' ); 1 row inserted. iSQL> INSERT INTO TD VALUES( '6', '6' ); 1 row inserted. iSQL> INSERT INTO TD VALUES( '7', '7' ); 1 row inserted. iSQL> INSERT INTO TD VALUES( '8', '8' ); 내장 함수와 저장 프로시저 223 1 row inserted. iSQL> INSERT INTO TD VALUES( '9', '9' ); 1 row inserted. iSQL> INSERT INTO TD VALUES( '10','10' ); 1 row inserted. --# Export_partition_to_file( , , , , ); iSQL> EXEC EXPORT_PARTITION_TO_FILE( 'SYS', 'TD', 'H1', 'SYS_TD_PARTITION_H1', 'EXPORT_PARTITION_H1' ); Export - EXPORT_PARTITION_H1 2 record(s). Execute success. --# Copy the generated file, TMFILE_0.dpf, --# to the default directory, ${ALTIBASE_HOME}/dbs, on the target machine. --# Import_from_file( , , , ); iSQL> EXEC IMPORT_FROM_FILE( 'SYS', 'TM', 'SYS_TD_PARTITION_H1' ); Import - SYS_TM 2 record(s). Execute success. 224 Stored Procedures Manual 그 외 함수들 REMOVE_XID XA 환경에서 “heuristically completed” 된 (데이터베이스 서버에 의해 임의로 롤백 되거나 커밋 된) 트랜잭션의 XID 정보를 강제로 삭제한다. 구문 REMOVE_XID (xidname IN VARCHAR); 파라미터 이름 입출력 데이터 타입 설명 xidname IN VARCHAR 삭제할 XID의 이름 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 REMOVE_XID는 다음의 시스템-정의 예외들을 발생시킬 수 있다. NOT_EXIST_XID InvalidXaState SLEEP 이 프로시저는 seconds 인자에 지정한 초만큼 세션을 쉬게 한다. 구문 SLEEP (seconds IN INTEGER); 파라미터 이름 입출력 데이터 타입 설명 seconds IN VARCHAR 쉴 시간 (초) 내장 함수와 저장 프로시저 225 결과값 저장 프로시저이므로 반환하는 결과값은 없다. 예외 이 프로시저는 예외를 발생시키지 않는다. 부록 A: 예제 227 A. 예제 228 Stored Procedures Manual 저장 프로시저 예제 예제1 리플리케이션 객체를 생성하는 스크립트를 출력하는 dumpReplScrip 저장 프로시저를 생성한다. 지역서버의 IP 주소가 192.168.1.12 이고 이중화 포트번호가 35524, 원격서버의 IP 주소가 192.168.1.60 이고 이중화 포트번호가 25524 인 두 서버간에 EMPLOYEE 테이블과 DEPARTMENT 테이블을 이중화 한다고 가정한다. 원격 서버에서: iSQL> CREATE REPLICATION rep1 WITH '192.168.1.12',35524 FROM SYS.EMPLOYEE TO SYS.EMPLOYEE, FROM SYS.DEPARTMENT TO SYS.DEPARTMENT; Create success. iSQL> ALTER REPLICATION rep1 START; Alter success. 지역 서버에서: iSQL> CREATE REPLICATION rep1 WITH '192.168.1.60',25524 FROM SYS.EMPLOYEE TO SYS.EMPLOYEE, FROM SYS.DEPARTMENT TO SYS.DEPARTMENT; Create success. iSQL> ALTER REPLICATION rep1 START; Alter success. iSQL> create or replace procedure dumpReplScript (p1 varchar(40)) as cursor c1 is select system_.sys_replications_.replication_name, system_.sys_replications_.host_ip, system_.sys_replications_.port_no, system_.SYS_REPLICATIONS_.ITEM_COUNT from system_.sys_replications_ where system_.sys_replications_.replication_name = UPPER(P1); r_name varchar(40); r_ip varchar(40); r_port varchar(20); r_item_cnt integer; 부록 A: 예제 229 r_local_user_name varchar(40); r_local_table_name varchar(40); r_remote_user_name varchar(40); r_remote_table_name varchar(40); cursor c2 is select system_.SYS_REPL_ITEMS_.LOCAL_USER_NAME, system_.SYS_REPL_ITEMS_.LOCAL_TABLE_NAME, system_.SYS_REPL_ITEMS_.REMOTE_USER_NAME, system_.SYS_REPL_ITEMS_.REMOTE_TABLE_NAME from system_.sys_repl_items_ where system_.SYS_REPL_ITEMS_.replication_name = r_name; begin open c1; SYSTEM_.PRINTLN('---------------------------------------'); SYSTEM_.PRINTLN(''); loop fetch C1 into r_name, r_ip, r_port, r_item_cnt; exit when C1%NOTFOUND; SYSTEM_.PRINT(' CREATE REPLICATION '); SYSTEM_.PRINT(r_name); SYSTEM_.PRINT(' WITH '''); SYSTEM_.PRINT(r_ip); SYSTEM_.PRINT(''','); SYSTEM_.PRINT(r_port); SYSTEM_.PRINTLN(' '); open c2; for i in 1 .. r_item_cnt loop fetch c2 into r_local_user_name, r_local_table_name, r_remote_user_name, r_remote_table_name; SYSTEM_.PRINT(' FROM '); SYSTEM_.PRINT(r_local_user_name); SYSTEM_.PRINT('.'); SYSTEM_.PRINT(r_local_table_name); SYSTEM_.PRINT(' TO '); SYSTEM_.PRINT(r_remote_user_name); SYSTEM_.PRINT('.'); SYSTEM_.PRINT(r_remote_table_name); if i r_item_cnt then SYSTEM_.PRINTLN(','); else SYSTEM_.PRINTLN(';'); end if; 230 Stored Procedures Manual e n d l o o p ; c l o s e c 2 ; e n d l o o p ; c l o s e c 1 ; SYST E M _ . PR I N T L N (' ' ) ; SYST E M _ . PR I N T L N (' ---- --------- -- --------- - --------- - ---- ' ) ; e n d ; / dumpReplScript 저장 프로시저를 실행한 출력 결과이다. iSQL> exec dumpReplScript('rep1'); ---------------------------------------------------------- CREATE REPLICATION REP1 WITH '192.168.1.60',25524 FROM SYS.DEPARTMENT TO SYS.DEPARTMENT, FROM SYS.EMPLOYEE TO SYS.EMPLOYEE; ---------------------------------------------------------- Execute success. 예제 2 리플리케이션의 이름과 정보를 출력하기 위한 저장 프로시저 showReplications를 생성한다. create or replace procedure showReplications as cursor c1 is select system_.sys_replications_.replication_name, system_.sys_replications_.host_ip, system_.sys_replications_.port_no, decode(system_.sys_replications_.is_started,1,'Running',0,'Not Running') from system_.sys_replications_; r_name varchar(40); r_ip varchar(40); r_port varchar(20); r_status varchar(20); r_local_user_name varchar(40); r_local_table_name varchar(40); r_remote_user_name varchar(40); r_remote_table_name varchar(40); cursor c2 is select system_.SYS_REPL_ITEMS_.LOCAL_USER_NAME, system_.SYS_REPL_ITEMS_.LOCAL_TABLE_NAME, system_.SYS_REPL_ITEMS_.REMOTE_USER_NAME system_.SYS_REPL_ITEMS_.REMOTE_TABLE_NAME from system_.sys_repl_items_ where system_.SYS_REPL_ITEMS_.replication_name 부록 A: 예제 231 = r_name; begin open c1; SYSTEM_.PRINTLN('-----------------------------------------'); SYSTEM_.PRINTLN(' Replications Infos'); SYSTEM_.PRINTLN('-----------------------------------------'); SYSTEM_.PRINTLN(' Name Ip Port Status'); SYSTEM_.PRINTLN('-----------------------------------------'); SYSTEM_.PRINTLN(''); loop fetch C1 into r_name, r_ip, r_port, r_status; exit when C1%NOTFOUND; SYSTEM_.PRINT(' '); SYSTEM_.PRINT(r_name); SYSTEM_.PRINT(' '); SYSTEM_.PRINT(r_ip); SYSTEM_.PRINT(' '); SYSTEM_.PRINT(r_port); SYSTEM_.PRINT(' '); SYSTEM_.PRINTLN(r_status); SYSTEM_.PRINTLN('+++++++++++++++++++++++++++++++++++++'); SYSTEM_.PRINTLN(' Local Table Name Remote Table Name'); SYSTEM_.PRINTLN('+++++++++++++++++++++++++++++++++++++'); open c2; loop fetch c2 into r_local_user_name, r_local_table_name, r_remote_user_name, r_remote_table_name; exit when C2%NOTFOUND; SYSTEM_.PRINT(' '); SYSTEM_.PRINT(r_local_user_name); SYSTEM_.PRINT('.'); SYSTEM_.PRINT(r_local_table_name); SYSTEM_.PRINT(' '); SYSTEM_.PRINT(r_remote_user_name); SYSTEM_.PRINT('.'); SYSTEM_.PRINTLN(r_remote_table_name); end loop; close c2; end loop; close c1; SYSTEM_.PRINTLN(''); SYSTEM_.PRINTLN('-----------------------------------------'); end; / 232 Stored Procedures Manual 다음은 showReplications 저장 프로시저를 이용한 출력 결과이다. iSQL> exec showReplications; ---------------------------------------------------------- Replication Info ---------------------------------------------------------- Name IP Port Status ---------------------------------------------------------- REP1 192.168.1.60 25524 Running ++++++++++++++++++++++++++++++++++++++++++++++++++++ Local Table Name Remote Table Name ++++++++++++++++++++++++++++++++++++++++++++++++++++ SYS.DEPARTMENT SYS.DEPARTMENT SYS.EMPLOYEE SYS.EMPLOYEE ---------------------------------------------------------- EXECUTE success. 예제 3 어떤 사용자의 테이블을 출력하기 위한 SHOWTABLES 저장 프로시저를 생성한다. create or replace procedure SHOWTABLES(p1 in varchar(40)) as cursor c1 is select SYSTEM_.SYS_TABLES_.TABLE_NAME from SYSTEM_.SYS_TABLES_ where SYSTEM_.SYS_TABLES_.USER_ID = (select SYSTEM_.SYS_USERS_.USER_ID from SYSTEM_.SYS_USERS_ where SYSTEM_.SYS_USERS_.USER_NAME = upper(p1) AND system_.SYS_TABLES_.TABLE_TYPE = 'T'); v1 CHAR(40); begin open c1; SYSTEM_.PRINTLN('-------------------'); SYSTEM_.PRINT(p1); SYSTEM_.PRINTLN(' Table'); SYSTEM_.PRINTLN('-------------------'); loop fetch C1 into v1; exit when C1%NOTFOUND; SYSTEM_.PRINT(' '); SYSTEM_.PRINTLN(v1); 부록 A: 예제 233 end loop; SYSTEM_.PRINTLN('-------------------'); close c1; end; / 다음은 showTables 저장 프로시저를 이용한 출력 결과이다. iSQL> exec showTables('SYS'); ------------------- SYS Table ------------------- CUSTOMER GOODS DUMMY ORDERS EMPLOYEE DEPARTMENT ------------------- Execute success. 예제 4 특정 프로시저의 내용을 출력하는 showProcBody 저장 프로시저를 생성한다. create or replace procedure showProcBody(p1 in varchar(40)) as cursor c1 is select system_.sys_proc_parse_.parse from system_.sys_proc_parse_ where system_.sys_proc_parse_.proc_oid = ( select SYSTEM_.sys_procedures_.proc_oid from system_.sys_procedures_ where SYSTEM_.sys_procedures_.proc_name = upper(p1)) order by system_.sys_proc_parse_.seq_no; v1 varchar(4000); begin open c1; SYSTEM_.PRINTLN('---------------------------------'); system_.print(p1); SYSTEM_.PRINTLN(' Procedure'); SYSTEM_.PRINTLN('---------------------------------'); SYSTEM_.PRINTLN(''); 234 Stored Procedures Manual l o o p f e t c h C 1 i n t o v 1 ; e x i t w h e n C 1 % N O T F O U N D ; SYS T E M _ . PR I N T L N (v 1 ); e n d l o o p ; close c1; SYSTEM_.PRINTLN(''); SYSTEM_.PRINTLN('---------------------------------'); end; / 다음은 저장 프로시저 텍스트 정보가 존재하는 메타 테이블 조회 결과이다. select system_.sys_proc_parse_.proc_oid, system_.sys_proc_parse_.parse from system_.sys_proc_parse_ where system_.sys_proc_parse_.proc_oid = ( select SYSTEM_.sys_procedures_.proc_oid from system_.sys_procedures_ where SYSTEM_.sys_procedures_.proc_name = upper('proc1')); PROC_OID ----------------------- PARSE ----------------------------------------------------------- 7695216 create or replace procedure PROC1 (P1 in NUMBER, P2 in VARCHAR(10), P3 in DATE) as begin if P1 > 7695216 0 then insert into T1 values (P1, P2, P3); end if; end 2 rows selected. 다음은 showProcBody 저장 프로시저를 실행한 출력 결과이다. iSQL> exec showProcBody('proc1'); --------------------------------- proc1 Procedure --------------------------------- create or replace procedure PROC1 부록 A: 예제 235 (P1 in NUMBER, P2 in VARCHAR(10), P3 in DATE) as begin if P1 > 0 then insert into T1 values (P1, P2, P3); end if; end --------------------------------- Execute success. 예제 5 커서 변수를 사용하는 저장 프로시저를 생성한다. ODBC 응용프로그램 내에서 이 프로시저를 실행하면 프로시저 내에서 커서변수를 열어서 ODBC 응용프로그램으로 커서를 전달하고, ODBC에서 커서변수를 통해 데이터를 읽는다. CREATE OR REPLACE TYPESET MY_TYPE AS TYPE MY_CUR IS REF CURSOR; END; / CREATE OR REPLACE PROCEDURE OPENCURSOR2 ( P1 OUT MY_TYPE.MY_CUR, P2 IN INTEGER ) AS BEGIN OPEN P1 FOR 'SELECT C1 FROM T1 WHERE C1 <= ?' USING P2; END; / iSQL> EXEC OPENCURSOR2(4); C1 -------------- 1 2 3 4 4 rows selected. /* ODBC 프로그램 */ 236 Stored Procedures Manual ... SQLINTEGER c1; SQLINTEGER param1; /* allocate Statement handle */ if (SQL_ERROR == SQLAllocStmt(dbc, printf("SQLAllocStmt error!!\n"); return SQL_ERROR; } sprintf(query,"EXEC OPENCURSOR2(?)"); if (SQLPrepare(stmt, (SQLCHAR *) query, SQL_NTS)== SQL_ERROR) { printf("ERROR: prepare stmt\n"); execute_err(dbc, stmt, query); return SQL_ERROR; } if (SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, NULL) == SQL_ERROR) { printf("ERROR: Bind Parameter 1\n"); execute_err(dbc, stmt, query); return SQL_ERROR; } param1 = 4; if (SQLExecute( stmt ) != SQL_SUCCESS) { execute_err(dbc, stmt, query); SQLFreeStmt(stmt, SQL_DROP); return SQL_ERROR; } if (SQL_ERROR == SQLBindCol(stmt, 1, SQL_C_SLONG, L)) { printf("ERROR: Bind 1 Column\n"); } while ( (rc = SQLFetch(stmt)) != SQL_NO_DATA) { 부록 A: 예제 237 if ( rc != SQL_SUCCESS ) { execute_err(dbc, stmt, query); break; } printf(" Result Set : [ %d ] \n", c1 ); } SQLFreeStmt(stmt, SQL_DROP); .... $ refcursor =========================================================== Result Set : [ 1 ] Result Set : [ 2 ] Result Set : [ 3 ] Result Set : [ 4 ] 238 Stored Procedures Manual 파일 제어 예제 다음은 파일에 테이블의 내용을 기록하고 그 파일로부터 데이터를 읽어오는 예제이다. 사용자 생성 및 사용자에게 적절한 권한을 부여한다. CONNECT SYS/MANAGER; CREATE USER JEJEONG IDENTIFIED BY JEJEONG; GRANT CREATE ANY DIRECTORY TO JEJEONG; GRANT DROP ANY DIRECTORY TO JEJEONG; 테이블과 디렉터리 객체를 생성한다. CONNECT JEJEONG/JEJEONG; CREATE TABLE T1( ID INTEGER, NAME VARCHAR(40) ); INSERT INTO T1 VALUES( 1, 'JEJEONG'); INSERT INTO T1 VALUES( 2, 'EJPARK' ); INSERT INTO T1 VALUES( 3, 'WSKIM' ); INSERT INTO T1 VALUES( 4, 'KKSHIM' ); INSERT INTO T1 VALUES( 5, 'CSKIM' ); INSERT INTO T1 VALUES( 6, 'KDHONG' ); CREATE DIRECTORY MYDIR AS '/home/JEJEONG'; 테이블 T1에 있는 모든 레코드를 읽어서 t1.txt 파일에 기록하는 저장 프로시저를 생성한다. CREATE OR REPLACE PROCEDURE WRITE_T1 AS V1 FILE_TYPE; ID INTEGER; NAME VARCHAR(40); BEGIN DECLARE CURSOR T1_CUR IS SELECT * FROM T1; BEGIN OPEN T1_CUR; V1 := FOPEN( 'MYDIR', 't1.txt', 'w' ); LOOP FETCH T1_CUR INTO ID, NAME; EXIT WHEN T1_CUR%NOTFOUND; PUT_LINE( V1, 'ID : '||ID||' NAME : '||NAME); END LOOP; CLOSE T1_CUR; FCLOSE(V1); 부록 A: 예제 239 END; END; / 파일 t1.txt에 있는 내용을 읽어 화면에 출력하는 저장 프로시저를 생성한다. CREATE OR REPLACE PROCEDURE READ_T1 AS BUFFER VARCHAR(200); V1 FILE_TYPE; BEGIN V1 := FOPEN('MYDIR', 't1.txt', 'r' ); LOOP GET_LINE( V1, BUFFER, 200 ); PRINT( BUFFER ); END LOOP; FCLOSE( V1 ); EXCEPTION WHEN NO_DATA_FOUND THEN FCLOSE( V1 ); END; / 결과 위의 저장 프로시저를 생성한 후 실행하면 다음과 같은 결과가 나온다. iSQL> exec write_t1; Execute success. iSQL> exec read_t1; ID : 1 NAME : JEJEONG ID : 2 NAME : EJPARK ID : 3 NAME : WSKIM ID : 4 NAME : KKSHIM ID : 5 NAME : CSKIM ID : 6 NAME : KDHONG Execute success. 파일 시스템 상의 실제 디렉터리에서 파일을 확인하면 결과는 다음과 같다. $ cd /home/JEJEONG $ cat t1.txt ID : 1 NAME : JEJEONG ID : 2 NAME : EJPARK 240 Stored Procedures Manual I D : 3 N AM E : W SK I M I D : 4 N AM E : KKSH I M I D : 5 N AM E : C SKI M I D : 6 N AM E : KD H O N G 찾아보기 241 찾아보기 A ALTER FUNCTION statement 29 ALTER PROCEDURE statement 20 ASSIGNMENT statement 49 Associative Array 118, 119 ASSOCIATIVE ARRAY TYPE 함수 123 B Block Body 34 C CASE statement 69 CLEAR_DP 215 CLOSE CURSOR 97 CLOSE CURSOR statement 107 CONTINUE statement 87 CREATE FUNCTION statement 25 CREATE PROCEDURE statement 12 CREATE TYPESET statement 143 CURSOR Attribute 111 CURSOR FOR LOOP 97 CURSOR FOR LOOP statement 108 D DataPort 205 파일 이름 규칙 206 DataPort 예외 217 DECLARE CURSOR 96 DECLARE CURSOR statement 98 Declare Exception 161 DECLARE EXCEPTION statement 163 Declare Section 33 DECLARE TYPE 120 declaring a local variable 35 DROP FUNCTION statement 30 DROP PROCEDURE statement 22 DROP TYPESET statement 146 E Exception 160 Exception Handler 34, 161, 174 EXECUTE IMMEDIATE statement 153 EXECUTE statement 23 EXIT statement 83 EXPORT_PATITION_TO_FILE 210 EXPORT_TO_FILE 209 EXPORT_USER_TABLES 212 F FCLOSE procedure 184 FCLOSE_ALL procedure 185 FCOPY 186 FETCH 96 FETCH statement 104 FFLUSH procedure 188 FOPEN function 190 FOR LOOP statement 77 FREMOVE procedure 191 FRENAME procedure 192 G GET_LINE procedure 194 GOTO 89 I IF statement 63 IMPORT_FROM_FILE 213 IN 13 INOUT 13 242 Stored Procedures Manual IS_OPEN function 195 L LABLE statement 52 LOOP statement 73 N NEW_LINE procedure 196 NULL statement 93 O OPEN CURSOR 96 OPEN CURSOR statement 101 OPEN FOR statement 156 OUT 13 P PRINT statement 55 PUT procedure 197 PUT_LINE procedure 199 R Raise Exception 161 RAISE statement 164 RAISE_APPLICATION_ERROR 166 RECORD 118 REF CURSOR 132 REMOVE_DP 217 REMOVE_XID 224
릴리즈노트
-
- ALTIBASE HDB (ver 5.5.1.0.0) Release Notes (English) ㅣ 2012-09-26
- create a thread object. # *Cause: Internal Bug # *Action: Please send a bug report to the vendor. 0x10146 ( 65862) Removed There are no double write files. Check your properties. # *Cause: DOUBLE_WRITE_DIRECTORY property is modified. # *Action...
-
미리보기
ALTIBASE HDB 5.5.1 Release Notes ALTIBASE HDB Release Notes 5.5.1.0.0 ALTIBASE HDB® HDB™ Hybrid RDBMS Table of Contents 1. Abstract 3 1.1 System Requirements 3 1.2 Release Platforms 3 2. Release Information 4 2.1 What is New in ALTIBASE HDB 5.5.1 4 2.2 Changes 5 2.3 Packages 14 2.4 Downloads 14 Location 14 Installation 14 ALTIBASE HDB Release Notes 5.5.1.0.0 ALTIBASE HDB® HDB™ Hybrid RDBMS 1. Abstract 1 .1 S y s t em R equi re m ents Minimum Hardware 512MB RAM (Recommended : 1GB) 1 CPU (Recommended : 2 CPUs) 4GB hard disk space (Recommended : 12GB) 1.2 Supported Operating Systems and Platforms OS CPU Version Bit (Server) Bit (Client) AIX PowerPC 5.3 tl1 or above 64bit 64bit, 32bit HP-UX PA-RISC 11.11 or above 64bit 64bit, 32bit IA64 11.23 or above SUN SPARC 2.8 or above 64bit 64bit, 32bit i86PC 2.10 or above Windows x86, x86-64 Windows 2003 64bit, 32bit 64bit, 32bit LINUX x86, x86-64 (GNU glibc 2.3.4 or lower) SSE2 (intel pentium4 above) AS40 64bit, 32bit 64bit, 32bit ES40 64bit, 32bit 64bit, 32bit x86, x86-64 (GNU glibc 2.4.0 or higher) SSE2 (intel pentium4 above) ES50 64bit, 32bit 64bit, 32bit Java Version: Compatible with JDK 1.4 or later versions for JDBC. ALTIBASE HDB Release Notes 5.5.1.0.0 ALTIBASE HDB® HDB™ Hybrid RDBMS 2. Release Information 2.1 What is New in ALTIBASE HDB 5.5.1 Eager Replication Enhancements - Introduced multi-threading for performance improvements for the sender/receiver processes. - Improved conflict detection and resolution for fail-back in 2-way replication mode. Eliminated the restriction on the GCC Client Library - In the previous version, the programming language of the Client Library was C++. It was changed to C for compatibility on the GCC development environment. IPv6 Support - IPv6 Network interface configuration is required to use this feature. - If the server supports dual stack then the ALTIBASE HDB client will support dual stack. If the server supports IPv6 only, then the ALTIBASE HDB client will support IPv6 only. Disk Spatial Index Support - ALTIBASE HDB provides Spatial Index for DRDB and builds using Bottom-Up method. Enhanced TMS (Treelist Managed Segment) - Enhanced TMS (Treelist Managed Segment) - one of Bitmap-based segment space management methods - proper to OLTP service. - Default segment space management method is changed from FSM (Freelist Managed Segment) to TMS (Treelist Managed Segment). Improved the Scalability on Logging - ALTIBASE HDB supports not only posix but also native of log file group mutex functions to improve the scalability of the Logging. As the result, the performance of Logging is two times faster than that of the previous Logging technique. DataPort support function for upgrading ALTIBASE HDB - There is no Network communication cost since DataPort functions directly downloads and uploads data. Furthermore, translating cost on data type does not exist since DataPort functions store data with the same type format that ALTIBASE HDB uses. Therefore, ALTIBASE HDB maximizes the performance of the data migration by using DataPort functions. Enhanced performance of Direct-Path INSERT function - Direct-Path INSERT function uses the APPEND feature and a separate buffer area. It improves the performance of disk data migration and uploading data to the disk table. Support Foreign Key NoValidate function - If stating the NOVALIDATE phrase when creating a foreign key in the child table, ALTIBASE HDB skips the foreign key validation check in the Parent table. As such, ALTIBASE HDB does not guarantee data consistency. The invalid tables can be viewed in the meta table called SYS_CONSTRAINTS_. Provides DBC(Database Connectivity) in C programming language - The CLI Library is complicated due to many parameters; and it makes many function calls. Therefore, ALTIBASE HDB provides an additional Library called C/DBC. It is easy to use and it is in the programming language C. Provides ALTIBASE HDB Administrative API ALTIBASE HDB Release Notes 5.5.1.0.0 ALTIBASE HDB® HDB™ Hybrid RDBMS - Applications can use the iLoader function and Check Server functions directly via ALTIBASE HDB Administrative APIs. Enhanced OLE DB Provider - Added BLOB data type and geometry data type. - Passed OGC OLE DB Provider test - Connection problem with Delphi is resolved. Adapter for Oracle - ALTIBASE Adapter for Oracle enables replication between an ALTIBASE HDB and an Oracle DB via ALA (Altibase Log Analysis). 2.2 Changes Listed below are added/updated/deleted functions explanations for DBA and developers. Database version updates Updated Database versions ALTIBASE HDB Version Binary database Version Communication Protocol Version Meta Version Replication Protocol Version 5.3.3 5.4.1 5.6.2 5.6.1 5.4.1 5.5.1 5.4.1 5.6.2 5.9.1 5.6.1 Compatibility Binary database version The binary database version indicates the compatibility of database image files and log files. When this value changes, the database must be migrated. Communication protocol version Client programs using ALTIBASE HDB 5.3.3 or above are compatible with the new version. They will connect to ALTIBASE HDB without requiring program rebuilds. For the applications using ALTIBASE HDB 5.3.3 or above, they have to re-link to the ALTIBASE HDB 5.5.1 Library to use IPC connections. ALTIBASE HDB Version CM Protocol Version Client Program Rebuild IPC Client Program Rebuild 5.3.3 5.6.2 XO 5.5.1 5.6.2 X X Meta version The Meta version will be upgraded to 5.9.1 automatically after upgrading ALTIBASE HDB to 5.5.1. Downgrade is not supported. ALTIBASE HDB Release Notes 5.5.1.0.0 ALTIBASE HDB® HDB™ Hybrid RDBMS Replication protocol version Replication between different replication protocol versions is not supported. ALTIBASE HDB Version Replication Protocol Version Compatibility 5.3.3 5.4.1 X 5.5.1 5.6.1 X Client compatibility Application development environment Development Environment Modification C language (ODBC, OLE DB) The spatial data structure has been modified. Therefore, Spatial applications must be modified for ALTIBASE HDB 5.5.1. JAVA ABConnectionPoolDataSource has been renamed to ABPoolingDataSource Changes on DBA utilities Utility Modification ( 5.3.3 to 5.5.1 ) Note iLoader FORM file format iLoader may use the quotation marks(“) on the table name and the column in the FORM file. The previous FORM file is compatible on ALTIBASE HDB 5.5.1. Data file format If there are no changes on the FORM file, iLoader can upload data with the existing data file. Command option iLoader ignores PARALLEL hint in the “INSERT INTO … SELECT” statement. iLoader ignores the -ioparallel option. -array and -commit options are available for the Direct-Path INSERT function. The following values are recommended. array size: 5,000 or higher commit unit: five or higher (set the proper value since a transaction can be aborted in the middle of the process.) Dumpdp (dump DataPage) Problem Tracking function enhancement The name of dumpdp(dumpDataPage) has been changed to dumpci(dump CheckpointImage) File specification argument of dumplf, dumpddf and dumpci utilities have been changed to -f. The help scripts of dumpddf, dumplf and dumpci utilities have been modified. The page logical dump function was added to the dumpddf utility ALTIBASE HDB Release Notes 5.5.1.0.0 ALTIBASE HDB® HDB™ Hybrid RDBMS Utility Modification ( 5.3.3 to 5.5.1 ) Note dumplf (Dump LogFile) Option explanation Before modification Usage : dumplf -f file [-t transactionID] [-s] [-l] After modification Usage : dumplf {-f log_file} [-t transaction_id] [-s] [-l] -f : specify log file name -t : specify transaction id -s : silence log value -l : display log types dumpla (Dump LogAnchor) Option explanation Before modification Usage: dumpla After modification Usage: dumpla loganchor_file dumplf, dumpci, dumpddf, dumpla Output format Naming on the information file of dumplf and dumpla utilities are now consistent. dumplf utility prints out the Log Record body of MMDB(Main Memory DB). The utilities print out more detailed information of the page in MMDB and DRDB. The output of utilities is in Hexa format, and it may be shown in logical structure format. Adapter for Oracle compatibility ALTIBASE Adapter for Oracle is supported with ALTIBASE HDB version 5.5.1.1.4 and above. ALTIBASE HDB version 5.5.1.1.4 is compatible only with oraAdapter version 5.5.1.1.4. Replication between different versions of ALTIBASE and oraAdapter is not supported. ALTIBASE Adapter for Oracle version 5.5.1.1.4 is supported for use with AIX versions 5.3 tl1 and above and Oracle versions 10g r2 and above, which are compatible with the Oracle OCI. Property New ALTIBASE HDB Properties Name Description New Properties DATAPORT_FILE_DIRECTORY The Dataport function uses the value of this property as a default directory to store exported/imported data. DATAPORT_IMPORT_COMMIT_UNIT The value of this property determines how many statements are committed at one Commit command when the DataPort function imports data. DATAPORT_IMPORT_STATEMENT_UNIT The value of this property determines how many rows are processed by one statement when DataPort function imports data. REPLICATION_COMMIT_WRITE_WAIT_ MODE If the value is 1, the Receiver Thread waits until a committed data is synchronized to disk. ALTIBASE HDB Release Notes 5.5.1.0.0 ALTIBASE HDB® HDB™ Hybrid RDBMS Name Description REPLICATION_SERVER_FAILBACK_MA X_TIME If ALTIBASE HDB is in Eager Replication mode, it synchronizes data with the peer server at the start up of service. If the synchronization takes more than the value of this property, ALTIBASE HDB stops the synchronization. Default: 2^32 - 1 seconds) This property may be used to perform a Full Sync manually or give up a Fail-Back. REPLICATION_EAGER_PARALLEL_FAC TOR If ALTIBASE HDB is in Eager Replication mode, many Sender Threads may work in parallel. The value of this property determines how many Sender Threads work in parallel. Default: the smaller value from the following - number of CPU cores - 512 LOG_ALLOC_MUTEX_TYPE This value determines the allocation mutex type. Default: 0(POSIX) 1: NATIVE1 2: NATIVE2 3: NATIVE3 Deleted ALTIBASE HDB properties ALTIBASE HDB properties Description Deleted Properties DIRECT_BUFFER_FLUSH_THREAD_SYN C_INTERVAL This property is now a hidden property since ALTIBASE HDB supports a logic that minimizes I/O bottleneck. DIRECT_PATH_INSERT_TEMP_QUEUE_S IZE The Queue was the bottleneck on the previous Direct-Path INSERT function, and has been removed. DIRECT_PATH_INSERT_TEMP_QUEUE_ WAIT_TIME The Queue was the bottleneck on the previous Direct-Path INSERT function, and has been removed. Modified Properties (5.3.3 => 5.5.1) ALTIBASE HDB properties Description Modified Properties DEFAULT_SEGMENT_MANAGEMENT_T YPE Default Value 0 => 1 INDEX_BUILD_THREAD_COUNT Default Value 2 => 4 IPC_PORT_NO Default Value 8802 => 20350 MULTIPLEXING_THREAD_COUNT Default Value 8 => 4 PARALLEL_LOAD_FACTOR Default Value 4 => 8 TRCLOG_DETAIL_PREDICATE Default Value 1 => 0 REPLICATION_MAX_LOGFILE, REPLICATION_RECOVERY_MAX_LOGFI LE These properties do not affect Eager Replication any longer. REPLICATION_SERVICE_WAIT_MAX_LI MIT This property does not affect Eager Replication any longer. ALTIBASE HDB Release Notes 5.5.1.0.0 ALTIBASE HDB® HDB™ Hybrid RDBMS Performance View New Performance View Name Description V$REPGAP_PARALLEL (Expansion to V$REPGAP) It extends V$REPGAP to show the Replication Gap information of parallel Replication Objects. Added Column PARALLEL_ID: Each Sender/Receiver Thread has a separate ID. CURRENT_TYPE: The type of Replication Sender/Receiver Thread Removed Column and START_FLAG: CURRENT_TYPE replaced it. V$REPRECEIVER_PARALLEL (Expansion to V$REPRECEIVER) It extends V$REPRECIEVER to show information of parallel Replication Receiver Threads. Added Column PARALLEL_ID: Each Sender/Receiver Thread has a separate ID. V$REPRECEIVER_TRANSTBL_PARALLE L (Expansion to V$REPRECEIVER_TRANSTBL) It extends V$REPRECIVER_TRANSTBL to show the information of Transaction Tables for the parallel Receiver Threads. Added Column PARALLEL_ID: Each Sender/Receiver Thread has a separate ID. V$REPSENDER_PARALLEL (Expansion to V$REPSENDER) It extends V$REPSENDER to show information of parallel Replication Sender Threads. Added Column PARALLEL_ID: Each Sender/Receiver Thread has a separate ID. CURRENT_TYPE: The type of Replication Sender/Receiver Thread Removed Column ACT_REPL_MODE: The value of this column does not change on ALTIBASE HDB 5.5.1. START_FLAG: CURRENT_TYPE replaced it. V$REPSENDER_TRANSTBL_PARALLEL (Expansion to V$REPSENDER_TRANSTBL) It extends V$REPSENDER_TRANSTBL to show information of Transaction Tables for parallel Sender Threads. Added Column PARALLEL_ID: Each Sender/Receiver Thread has a separate ID. CURRENT_TYPE: The type of Replication Sender/Receiver Thread Removed Column START_FLAG: CURRENT_TYPE replaced it. ALTIBASE HDB Release Notes 5.5.1.0.0 ALTIBASE HDB® HDB™ Hybrid RDBMS Name Description V$DIRECT_PATH_INSERT COMMIT_TX_COUNT : Total number of transactions successfully committed using the Direct-Path INSERT function. ABORT_TX_COUNT : Total number of transactions aborted using the Direct- Path INSERT function. INSERT_ROW_COUNT : Total number of rows inserted using Direct-Path INSERT function. ALLOC_BUFFER_PAGE_TRY_COUNT : Total number of counts that the Page allocation was requested by the Direct-Path INSERT function. ALLOC_BUFFER_PAGE_FAIL_COUNT : The total numbers of counts that the Page allocation failed using the Direct-Path INSERT function. Error messages (5.3.3 to 5.5.1) Removed Error Messages Message Status Description 0x0002E ( 46) Removed Failed to create a thread object. # *Cause: Internal Bug # *Action: Please send a bug report to the vendor. 0x10146 ( 65862) Removed There are no double write files. Check your properties. # *Cause: DOUBLE_WRITE_DIRECTORY property is modified. # *Action: check your properties. # Server Internal Message Modified Error Messages Message Status Description 0x1101F ( 69663) Before The Maximum size of the data file cannot be less than the current size of it. ( Current Size : <0%ld>, Request Max Size : <1%ld> ) # *Cause: The maximum size of the data file is less than the current size of the data file. # *Action: Please reset the INITSIZE or MAXSIZE of the data file correctly. After The MAXSIZE of data file is less than the current size of it. ( Request Max Size : <0%lu> pages, Current Size : <1%lu> pages ) # *Cause: The MAXSIZE of the data file is less than the current size of the data file. # *Action: Please reset the MAXSIZE of the data file correctly. 0x11020 ( 69664) Before The INITSIZE of a data file exceeds the maximum file size. ALTIBASE HDB Release Notes 5.5.1.0.0 ALTIBASE HDB® HDB™ Hybrid RDBMS Message Status Description After The INITSIZE of a data file exceeds the maximum file size. ( Request Init Size : <0%lu> pages, Maximum File Size : <1%lu> pages ) 0x11021 ( 69665) Before The CURRSIZE of a data file exceeds maximum file size. After The CURRSIZE of a data file exceeds maximum file size. ( Request Cur Size : <0%lu> pages, Maximum File Size : <1%lu> pages ) 0x11022 ( 69666) Before The MAXSIZE of data file exceeds maximum file size. ( Request Max Size : <0%ld>, Max Size : <1%ld>) After The MAXSIZE of data file exceeds maximum file size. ( Request Max Size : <0%lu> pages, Maximum File Size : <1%lu> pages ) 0x11030 ( 69680) Before The data file cannot be extended because the requested size is bigger than the maximum size(<0%d> pages). After The requested size of data file exceeds the maximum file size. ( Request Size : <0%lu> pages, Maximum File size : <1%lu> pages ) 0x11105 ( 69893) Before The data file cannot be extended because the requested size is bigger than the OS file limit size(<0%d> pages). After The requested size of data file exceeds the OS file limit size. ( Request Size : <0%lu> pages, OS File Limit Size : <1%lu> pages ) 0x11122 ( 69922) Before you can not shrink this file. # *Cause: you tried to shirink below HWM. # *Action: Don't do it. After The requested size of data file is less than the used size of data file. ( Request Size : <0%lu> pages, Used File Size : <1%lu> pages ) # *Cause: you tried to shrink below HWM. # *Action: Please retry with a greater size. 0x11124 ( 69924) Before you can't even make one extent. # *Action: You know what to do. After The INITSIZE of data file is less than the minimum file size. ( Request Init Size : <0%lu> pages, Minimum File Size : <1%lu> pages ) # *Action: Please retry with a greater size. 0x11128 ( 69928) Before the size is too small to make an extent. After The requested size of data file is less than the minimum file size. ( Request Size : <0%lu> pages, Minimum File Size : <1%lu> pages ) 0x61055 ( 397397) Before Memory allocation failure After Memory allocation failure [Function=<0%s>, Variable=<1%s>] 0x6200F ( 401423) Before [Sender] Stop sender thread <0%s> at [<1%ld>], Restart SN[<2%ld>] After [Sender] Stop sender thread <0%s>:<1%d> at [<2%ld>], Restart SN[<3%ld>] 0xB107C ( 725116) Before Memory allocation failure After Memory allocation failure [Function=<0%s>, Variable=<1%s>] ALTIBASE HDB Release Notes 5.5.1.0.0 ALTIBASE HDB® HDB™ Hybrid RDBMS Modified SQL syntax Name Description Option changed: ALTER SESSION SET REPLICATION= Either DEFAULT or NONE can be used for this SQL statement. Other values (LAZY, ACKED and EAGER) are not supported. Removed SQL: ALTER REPLICATION SET MODE = ALTER REPLICATION replication_name SET MODE {LAZY|ACKED|EAGER}; This SQL statement changes the replication mode, but it is not supported anymore. Replication mode cannot be changed in 5.5.1. CREATE TABLESPACE USER_BITMAP_TBS DATAFILE user_bitmap_tbs.dbf EXTENTSIZE 512K SEGMENT MANAGEMENT AUTO; Default Segment management method has changed from FMS (Freelist Managed Segment) to TMS (Treelist Managed Segment). The “SEGMENT MANAGEMENT AUTO” phrase can be omitted. The default value of EXTENTSIZE has changed from 256K to 512K for both FMS and TMS. CREATE TABLESPACE USER_FREELIST_TBS DATAFILE user_freelist_tbs.dbf EXTENTSIZE 512K SEGMENT MANAGEMENT MANUAL; FMS must be explicitly stated. Restrictions DataPort Function Description Same table schema must exist to import data. If one of following is different, the DataPort function cannot import data; column position, number, precision and scale. It is impossible to import/export on the encrypted tables. Encryption must be removed by ALTER statement in order to import/export data. Importing/Exporting can be performed on the new tables made by the CTAS (Create Table As Select) statement on the encrypted tables. Importing is not possible on tables that have a trigger, foreign key, index and a not null column. Furthermore, importing cannot be performed on the tables in the Replication Object. The DataPort function does not validate all the above restrictions before execution. Therefore, invalid data can be inserted if the restrictions listed above exist on the tables. Data must be imported after resolving the restrictions. Restrictions must be applied after DataPort function finishes the work. Eager Replication Functionality Description DDL on Replication On-line The Receiver Thread stops while ALTIBASE HDB performs DDL. In this case, Data Conflict may occur. Selection Replication It is a data filtering function, and is a proper function for ALA. Off-Line Replicator Data Conflict and replication gap does not occur in Eager Replication mode. Lazy/Acked Replication If the local server is in Eager Replication mode, the peer server must be in Eager Replication mode. Otherwise, Data Conflict may occur. ALTIBASE HDB Release Notes 5.5.1.0.0 ALTIBASE HDB® HDB™ Hybrid RDBMS Functionality Description Replication Give-up The value of REPLICATION_MAX_LOGFILE must not be set in Eager Replication mode since ALTIBASE HDB deletes logfiles if Replication Give-up occurs; Replication Give-up occurs when the existing numbers of logfiles are larger than the value of REPLICATION_MAX_LOGFILE. ALTIBASE HDB deletes all logfiles not required to resolve the Data Conflict. Max Rows The Max Rows restriction may fail a commit command, so it must not be used in Eager Replication mode. Data Conflict cannot be resolved if a commit command fails. Other Modifications on management of storage space ALTIBASE HDB Version Description ALTIBASE HDB 5.3.3 or below ALTIBASE HDB 5.3.3 or below does not support Non-Autocommit mode in the Direct-Path INSERT function. Therefore, the maximum storage space that the Direct-Path INSERT function uses is as follows. Number of Threads * Number of partitions * Number of Pages per Extent ALTIBASE HDB 5.3.5 or above The Direct-Path INSERT function allocates a new Page for every Cursor although an empty space may exist in the allocated Pages due to the characteristic of Buffer management. Furthermore, the Direct-Path INSERT function uses the APPEND method, and the APPEND method allocates a new Extent for each transaction start; although empty Pages exist before the HWM (High Water Mark). As a result, some storage space is wasted. The total space that the Direct-Path INSERT function uses for one transaction can be calculated as the following. (n: number of rows to insert, a: array size, c: commit unit) {(Page Size*[n/a])+(NEXTEXTENTS*(Number of Pages per Extent)*[nla/c])}}*Number of partitions Direct-Path INSERT function on different ALTIBASE HDB versions Column Name Description ALTIBASE HDB 5.3.3 or below The Non-Autocommit mode is not supported, and the value of commit unit is fixed to 1. Furthermore, the performance of Direct-Path INSERT function is slower than that of the ATOMIC INSERT. Therefore, it is not recommended to use this function. ALTIBASE HDB 5.3.5 The Non-Autocommit mode is supported. In the iLoader, the performance of Direct-Path INSERT function is generally faster than that of ATOMIC INSERT. (In some environments and schemas, the performance is slower.) Therefore, it is recommended to use this function. ALTIBASE HDB 5.5.1 The Non-Autocommit mode is supported, and the push method has been improved. The performance of the Direct-Path INSERT function is much faster than that of ATOMIC INSERT. Therefore, it is recommended to use this function. ALTIBASE HDB Release Notes 5.5.1.0.0 ALTIBASE HDB® HDB™ Hybrid RDBMS 2.5 Packages OS Version CPU Archive Name AIX 5.3 powerPC altibase-server-5.5.1.0-AIX-POWERPC-64bit-release.run altibase-client-5.5.1.0-AIX-POWERPC-32bit-release. run altibase-oraAdapter-5.5.1.1.4-AIX-POWERPC-32bit-release. run HP-UX 11.11 PA-RISC altibase-server-5.5.1.0-hpux-PA-RISC-64bit-release. run altibase-client-5.5.1.0-hpux-PA-RISC-32bit-release. run 11.23 IA64 altibase-server-5.5.1.0-hpux-IA64-64bit-release. run altibase-client -5.5.1.0-hpux-IA64-32bit-release. run Solaris 2.8 SPARC altibase-server-5.5.1.0-SOLARIS-SPARC-64bit-release. run altibase-client-5.5.1.0-SOLARIS-SPARC-32bit-release. run 2.10 X86 altibase-server-5.5.1.0-SOLARIS-X86-64bit-release. run altibase-client-5.5.1.0-SOLARIS-X86-32bit-release. run Linux/glibc ES40-x64 X86 altibase-server-5.5.1.0-LINUX-X86-64bit-release. run altibase-server-5.5.1.0-LINUX-X86-32bit-release. run altibase-client-5.5.1.0-LINUX-X86-32bit-release. run Windows 2003 X86 altibase-server-5.5.1.0-WIN_NT-X86-64bit-release.exe altibase-server-5.5.1.0-WIN_NT-X86-32bit-release. exe altibase-client -5.5.1.0-WIN_NT-X86-32bit-release. exe 2.6 Downloads Location Packages http://www.altibase.com/ Manual http://www.altibase.com/ Installation Refer to Installation User's Manual.
-
- ALTIBASE HDB (ver 6.1.1.0.0) Release Notes (English) ㅣ 2012-09-26
- created and managed their own memory pools. Also reduced the frequency of system calls by using a memory pool. - Overcame the problem whereby the VSZ (Virtual Memory Size) could not be reduced once it had been increased, and additionally overcame...
-
미리보기
ALTIBASE HDB 6.1.1 Release Note ALTIBASE HDB 6.1.1.0.0 Release Notes ALTIBASE HDB® HDB™ Hybrid RDBMS Table of Contents 1. Abstract 3 1.1 System Requirements 3 1.2 Release Platforms 3 2. Release Information 4 2.1 What's New in ALTIBASE HDB 6.1.1 4 2.2 Changes 5 2.3 Packages 18 2.4 Downloads 19 Location 19 Installation 19 ALTIBASE HDB 6.1.1.0.0 Release Notes ALTIBASE HDB® HDB™ Hybrid RDBMS 1. Abstract 1. 1 S y stem R eq u i r emen ts Minimum Hardware 512MB RAM (Recommended: 1GB) 1 CPU (Recommended: 2 CPUs) 4GB hard disk space (Recommended: 12GB) 1.2 Supported Operating Systems and Platforms OS CPU Version Bit (Server) Bit (Client) AIX PowerPC 5.3 tl1 and above 64-bit 64-bit, 32-bit HP-UX PA-RISC 11.11 and above 64-bit 64-bit, 32-bit IA64 11.23 and above SUN SPARC 2.8 and above 64-bit 64-bit, 32-bit i86PC 2.10 and above Windows x86, x86-64 Windows 2003 64-bit, 32-bit 64-bit, 32-bit LINUX x86, x86-64 (GNU glibc 2.3.4 or lower) SSE2 (intel pentium4 above) AS40 64-bit, 32-bit 64-bit, 32-bit ES40 64-bit, 32-bit 64-bit, 32-bit x86, x86-64 (GNU glibc 2.4.0 or higher) SSE2 (intel pentium4 above) ES50 64-bit, 32-bit 64-bit, 32-bit Java Version: JDBC compatible with JDK versions 1.4 or later. ALTIBASE HDB 6.1.1.0.0 Release Notes ALTIBASE HDB® HDB™ Hybrid RDBMS 2. Release Information 2. 1 W h at 's N e w i n A L T I B A SE H D B 6 . 1.1 Improved Scalability and Performance when Allocating and Freeing Statement Slots - Enhanced OLTP service scalability in environments such as WAS, in which statement slots are frequently allocated and freed. - Solved a problem whereby it took a long time to allocate and free statement slots when a statement-related performance view was being queried. As a result, performance is much faster than previously. Improved Spatial Validation Performance and Stability - Improved the performance with which Geometry data are loaded using iLoader. - Improved the performance with which Geometry data operations are performed. Support for LOB type in Volatile Tablespaces - The use of the LOB type in volatile tablespaces is now supported. MRDB Scalability Enhancement - Enhanced the performance of simultaneous multi-threaded DML execution by improving MRDB scalability. Increased Concurrency of Memory Index Access - Improved scalability by implementing a technique whereby the scalability of latches is improved when key values are inserted into or deleted from memory-resident indexes. Improved Performance in Solaris - Reduced internal contention within the ALTIBASE HDB engine in environments in which large numbers of transactions take place, thereby eliminating a major bottleneck that degraded performance. As a result, performance (TPS) is faster and CPU and disk utilization are both improved on the Solaris platform. Improved Performance of LAZY Mode Replication through Code Optimization - Improved replication performance by improving the efficiency of memory usage. As a result, the replication gap has been virtually eliminated under almost all circumstances. Implemented the TLSF (Two-Level Segregated Fit) Algorithm - Introduced a memory management algorithm, thereby eliminating memory allocation conflicts that arose when individual threads created and managed their own memory pools. Also reduced the frequency of system calls by using a memory pool. - Overcame the problem whereby the VSZ (Virtual Memory Size) could not be reduced once it had been increased, and additionally overcame the problem whereby the VSZ increased without limitation. - Enabled variable length allocation. Implemented an Atomic Method for Collecting Statistical Information about the Memory Manager - A bottleneck was occurring in the portion of the memory manager that allocates and deallocates global locks for saving statistical information. To relieve this bottleneck, an atomic operation, rather than a mutex, is now used for arithmetic operations. Performance is now faster than before in environments in which 16 or more clients are connected. Elimination of oraAdapter Gap - Improved UPDATE performance by implementing an UPDATE cache. - Improved the performance of INSERT and DELETE operations by implementing a Group Commit mechanism. ALTIBASE HDB 6.1.1.0.0 Release Notes ALTIBASE HDB® HDB™ Hybrid RDBMS Improved Performance when Binding Arrays - Reduced the number of OCI calls by implementing array binding when oraAdapter is used to commit data changes to an Oracle database, thereby reducing networking costs and improving performance. Changed the Method by which Index Statistics are Collected - Eliminated a major disk index-related bottleneck by changing the automatic index statistics collection method. Implemented Statistics Collection Methods - Implemented procedures (GATHER_TABLE_STATS, GATHER_INDEX_STATS) that DBAs can use to collect statistics. Added Support for Oracle-Style Outer Joins - To date, ALTIBASE HDB has supported the ANSI Standard joins, namely INNER JOIN, LEFT OUTER JOIN, RIGHT OUTER JOIN, and FULL OUTER JOIN. With version 6.1.1, ALTIBASE HDB additionally introduces support for the Oracle-style outer join operator, making it even easier to port your Oracle database and applications to ALTIBASE HDB Added Support for Pivot Queries - ALTIBASE HDB 6.1.1 adds explicit PIVOT functionality to the SELECT statement, making it easier to rearrange aggregate data for presentation in columns rather than in rows. Added Support for Ranking Functions - In addition to the existing analytic functions, ranking functions have now been added to the list of available reporting functions. The newly added ranking functions comprise the RANK, DENSE_RANK, and ROW_NUMBER functions. OpenGIS Implementation Specification for Geographic information - Simple feature access - Part 2: SQL option 1.2.1 (2012-02-16) Limited the default memory database size to 2GB for the evaluation version. Added “Full Index Scan” functionality. Implemented functionality for adding data files while DML operations are underway. Added functionality for disabling Checksum and DW in the default properties. Added functionality for checking I/O performance using the V$FLUSHER and V$BUFFPOOL_STAT performance views and the SM TRC file. 2.2 Changes Added, updated, and removed features are explained below for the benefit of DBAs and developers. Database Component Versions Updated Database Component Versions ALTIBASE HDB Version Database Binary Version Communication Protocol Version Meta Version Replication Protocol Version 5.5.1 5.4.1 5.6.2 5.9.1 5.6.1 6.1.1 6.1.1 5.6.2 5.9.1 6.1.1 ALTIBASE HDB 6.1.1.0.0 Release Notes ALTIBASE HDB® HDB™ Hybrid RDBMS Compatibility Passwords In ALTIBASE HDB 6.1.1, the maximum length of the password has been changed to 8 or 11 bytes, depending on the operating system. In Solaris x86 2.8 and above and Windows, the maximum password length is 11 bytes. In other operating systems, the maximum password length is 8 bytes. Therefore, it will be necessary to change any password-related scripts that contain passwords longer than 8 or 11 bytes. Database Binary Version The database binary version indicates the compatibility of database image files and log files. Existing databases must be migrated because the format of log files has changed. Communication protocol version Client applications that were built to work with ALTIBASE HDB 5.5.1 are compatible with the new version. They will connect to ALTIBASE HDB 6.1.1 without needing to be rebuilt. Applications that were built for use with ALTIBASE HDB 5.5.1 and use IPC connections will have to be re-linked to the ALTIBASE HDB 6.1.1 library. Meta Version The Meta version has not changed from ALTIBASE HDB 5.5.1 to 6.1.1. Replication Protocol Version The replication protocol version has changed from ALTIBASE HDB 5.5.1 to 6.1.1. Replication between different protocol versions is not supported. ALTIBASE HDB Version Replication Protocol Version Compatibility 5.5.1 5.6.1 X 6.1.1 6.1.1 X Client Compatibility Application development environment Development Environment Modification C language (ODBC, OLE DB) The structure of spatial data has been modified. Therefore, applications that were written for use with spatial data must be modified for use with ALTIBASE HDB 6.1.1. Adapter for Oracle Compatibility ALTIBASE HDB Adapter for Oracle is supported with ALTIBASE HDB version 6.1.1.0.0 and above. ALTIBASE HDB version 6.1.1.0.0 is compatible only with oraAdapter version 6.1.1.0.0. Replication between other versions of ALTIBASE HDB and oraAdapter is not supported. ALTIBASE HDB Adapter for Oracle version 6.1.1.0.0 is supported for use with LINUX/AIX versions 5.3 tl1 and above and Oracle versions 10g r2 and above, which are compatible with the Oracle OCI. ALTIBASE HDB 6.1.1.0.0 Release Notes ALTIBASE HDB® HDB™ Hybrid RDBMS Property New ALTIBASE HDB Properties Name Description New Properties SMALL_TABLE_THRESHOLD This property is used to determine whether to leave pages read by MPR (multi-page read) in the buffer when a full scan is performed on a table. Pages read by MPR are allowed to remain in the buffer only when the table contains fewer pages than the number specified using this property. Default Value: 128 REPLICATION_SENDER_START_AFTE R_GIVING_UP This property determines how replication proceeds after replication has been suspended when the number of accumulated log files preceding the Restart Redo Point exceeds the value of the REPLICATION_MAX_LOGFILE property. Default Value: 1 DDL_TIMEOUT If the execution time of a DDL statement exceeds the number of seconds specified here, execution of that statement is canceled. Default Value: 0 If DDL_TIMEOUT is set to the default value (0), ALTIBASE HDB will wait indefinitely for DDL operations to finish. New ALTIBASE HDB Adapter for Oracle Properties Name Description New Properties ORACLE_ASYNCRONOUS_COMMIT This property determines whether or not to wait until commit logs have been written to disk before sending a commit message to the client. 0: Do not use Asynchronous Commit This setting ensures that a commit message is returned to the client only after a commit log has been written to a persistent online redo log. 1: Use Asynchronous Commit (Default) A commit message is returned to the client regardless of whether the commit log has been completely written to disk. ORACLE_GROUP_COMMIT This property allows redo information for multiple transactions to be written to disk in a single I/O operation. 0: Do not use Group Commit 1: Use Group Commit (Default) ALTIBASE_USER Properties for Constraints: This is used to specify the name of a user account with which to connect to ALTIBASE HDB. ALTIBASE HDB 6.1.1.0.0 Release Notes ALTIBASE HDB® HDB™ Hybrid RDBMS Name Description ALTIBASE_PASSWORD Properties for Constraints: This is used to specify the password for the user account through which the connection to ALTIBASE HDB will be established. ALTIBASE_IP Properties for Constraints: This is the IP address of the server on which ALTIBASE HDB is installed. Default Value: 127.0.0.1 ALTIBASE_PORT Properties for Constraints: This is the number of the port at which ALTIBASE HDB listens. Range: 1024 - 65535 Default Value: 20300 ORACLE_ARRAY_DML_MAX_SIZE This property specifies the maximum number of DML statements that can be grouped in an array. Range: 1 - 32767 Default Value: 10 - This improves performance when the ORACLE_GROUP_COMMIT property is enabled. - At present, this property only affects INSERT and DELETE statements. ORACLE_UPDATE_STATEMENT_CACH E_SIZE This property determines the number of UPDATE statements that can be cached. Range: 0 ~ 4294967295 Default Value: 20 If the property is zero, oraAdapter does not cache UPDATE statements. Deprecated ALTIBASE HDB properties ALTIBASE HDB properties Description Deprecated Properties TRCLOG_SET_LOCK_TIME Deprecated FULL_SCAN_USE_BUFFER_POOL Deprecated LOG_ALLOC_MUTEX_TYPE Deprecated Modified Properties (5.5.1 => 6.1.1) ALTIBASE HDB properties Description Modified Properties MEM_MAX_DB_SIZE MEM_MAX_DB_SIZE is capped at 2GB when no license information has been provided (evaluation version). VOLATILE_MAX_DB_SIZE Range [2097152, 4294967296] => 32bit : [2097152, 232+1] => 64bit : [2097152, 264] Default value [4294967296] => [232+1] ALTIBASE HDB 6.1.1.0.0 Release Notes ALTIBASE HDB® HDB™ Hybrid RDBMS ALTIBASE HDB properties Description DRDB_FD_MAX_COUNT_PER_DATAFIL E Range [1, 32] => [1, 1024] BUFFER_AREA_SIZE Range [8192, 4294967295] => [8192, 18446744073709551615] BUFFER_AREA_CHUNK_SIZE Range [8192, 4294967295] => [8192, 18446744073709551615] SYS_UNDO_FILE_INIT_SIZE Default value [268435456] => [104857600] MAX_CLIENT Range [0, 4294967295] => [1, 65535] MAX_STATEMENTS_PER_SESSION Range [1, 4294967295] => [1, 65536] Performance View New Performance View Name Description V$USAGE Displays the amount of space used by tables and indexes on the server. In order to use this view, it is first necessary to use one of the ‘GATHER_..._STATS’ procedures to collect statistical information. V$REPSENDER_STATISTICS This view shows statistical information about the time that it takes for replication Senders to perform various tasks. When the TIMED_STATISTICS property is set to 1, cumulative statistics are maintained in this view. V$REPRECEIVER_STATISTICS This view shows statistical information about the time that it takes for replication Receivers to perform various tasks. When the TIMED_STATISTICS property is set to 1, cumulative statistics are maintained in this view. Modified Performance View Name Description V$BUFFPOOL_STAT Added Columns DB_MULTI_READ_PERF: This is the average number of bytes that are read from disk per second (in kB/sec) when multiple data pages are read from a disk data file. DB_SINGLE_READ_PERF: This is the average number of bytes that are read from disk per second (in kB/sec) when one data page is read from a disk data file. V$SESSION Added Columns FAILOVER_SOURCE: This contains information about the kind of Fail- Over and the connection for which Fail-Over was conducted. DDL_TIME_LIMIT This is the timeout value for DDL statements for the current session. ALTIBASE HDB 6.1.1.0.0 Release Notes ALTIBASE HDB® HDB™ Hybrid RDBMS Name Description V$SESSIONMGR Added Column DDL_TIMEOUT_COUNT This is the number of times that DDL statements have timed out since Altibase was started. V$FLUSHER Added Columns TOTAL_FLUSH_PAGES: The cumulative number of pages that have been flushed. TOTAL_LOG_SYNC_USEC: The cumulative amount of time taken to write buffer-resident redo logs to disk. TOTAL_DW_USEC: The cumulative amount of time taken to write the contents of double-write buffers to disk. TOTAL_WRITE_USEC: The cumulative amount of time taken to write data pages to data files. TOTAL_SYNC_USEC: The cumulative amount of time taken to forcibly flush data pages to disk. TOTAL_FLUSH_TEMP_PAGES: The cumulative number of temporary pages that have been flushed. TOTAL_TEMP_WRITE_USEC: The cumulative amount of time taken to write temporary pages to temporary files. TOTAL_CALC_CHECKSUM_USEC: The cumulative amount of time to taken to perform checksum calculations. DB_WRITE_PERF: The average number of bytes that are written per second when writing data pages to data files. TEMP_WRITE_PERF: The average number of bytes that are written per second when writing temporary pages to temporary files. Error messages (5.5.1 to 6.1.1) New Error Messages Message Status Description 0x51047 ( 331847) New Failed to create an instance of the memory allocator [<0%s>] # *Cause: Instantiating a memory allocator object has failed. # *Action: Please send a bug report to the vendor. 0x51048 ( 331848) New Failed to set memory allocator attributes [<0%s>] # *Cause: Setting attributes of a memory allocator object has failed. # *Action: Please send a bug report to the vendor ALTIBASE HDB 6.1.1.0.0 Release Notes ALTIBASE HDB® HDB™ Hybrid RDBMS Message Status Description 0x410D1 ( 266449) New Password too long # *Cause: The input password exceeded the maximum allowable length. # *Action: Please enter a shorter password. 0x31342 ( 201538) New Non-aggregate functions used in pivot operation # *Cause: Something other than an aggregate function was specified in the PIVOT clause. # *Action: Please ensure that only aggregate functions are specified in the PIVOT clause. 0x31343 ( 201539) New Duplicate column names # *Cause: The pivot operation resulted in multiple columns having the same name. # *Action: Please specify aliases in the PIVOT clause to prevent the generation of duplicate column names. 0x31344 ( 201540) New Mismatched number of PIVOT FOR elements and PIVOT IN subelements # *Cause: The number of elements in the PIVOT FOR clause is not the same as the number of subelements in each element in the PIVOT IN clause. # *Action: Please ensure that the number of PIVOT FOR elements is the same as the number of PIVOT IN subelements. 0x3133D ( 201533) New The retry option is not supported when replication is running in eager mode. # *Cause: The retry option is not supported in eager mode. # *Action: Please either remove the retry option or change the replication mode. 0x31367 ( 201575) New An ORDER BY expression is required in the window function. <0%s> # *Cause: A ranking function requires an ORDER BY expression in the OVER clause. # *Action: Please specify an ORDER BY expression. 0x610FB ( 397563) New Unexpected replication handshake ACK [Result:<0%u>] # *Cause: An internal server error has occurred. # *Action: Please send a bug report to the vendor. 0x610FC ( 397564) New Disconnection during handshaking [<0%s>] # *Cause: A network error has occurred. # *Action: Please check the network status and ensure that all systems are connected to the network. ALTIBASE HDB 6.1.1.0.0 Release Notes ALTIBASE HDB® HDB™ Hybrid RDBMS Message Status Description 0x11018 ( 69656) New The version of data file for backup is not compatible with the version of storage manager. Backup DB => [ Version ID = <0%s>, Bit = <1%d>, Endian = <2%s> LogSize = <3%lu> Transaction Table Size = <4%d> ] Server=>[ Version ID = <5%s>, Bit = <6%d>, Endian = <7%s> LogSize = <8%lu> Transaction Table Size = <9%d> ] # *Cause: The database has no backward compatibility. # *Action: Please import or export the database or use one of the previous versions of storage manager that is compatible with the data file. 0x110AF ( 69807) New The maximum file size of the OS is less than the database file size that was specified in the CREATE DATABASE statement ( <0%lu> MB ). # *Cause: The maximum file size of the OS is less than that specified in the property. # *Action: Please increase the maximum file size of the OS. 0x110B0 ( 69808) New Failed to invoke the getrlimit() system function # *Cause: The system failed to map the log file. # *Action: Please send a bug report to the vendor. 0x110B3 ( 69811) New Different Page List Count. The value of the PAGE_LIST_GROUP_COUNT property is different from the one used when createdb was executed. ( current PAGE_LIST_GROUP_COUNT:<0%d>, createdb's PAGE_LIST_GROUP_COUNT <1%d> ) # *Cause: The value of the PAGE_LIST_GROUP_COUNT property is different from the one used when createdb was executed. # *Action: Please set the PAGE_LIST_GROUP_COUNT value to exactly the same value specified when the database was created. 0x110B4 ( 69812) New Expand Chunk Page Count. The value of the EXPAND_CHUNK_PAGE_COUNT property is different from the one used when createdb was executed. ( current EXPAND_CHUNK_PAGE_COUNT:<0%d>, createdb EXPAND_CHUNK_PAGE_COUNT <1%d> ) # *Cause: The value of the EXPAND_CHUNK_PAGE_COUNT property is different from the one used when createdb was executed. # *Action: Please set the EXPAND_CHUNK_PAGE_COUNT to exactly the same value specified when the database was created. ALTIBASE HDB 6.1.1.0.0 Release Notes ALTIBASE HDB® HDB™ Hybrid RDBMS Message Status Description 0x110B5 ( 69813) New Too many pages in per list page count. ( Please change property values to meet the condition EXPAND_CHUNK_PAGE_COUNT(<0%lu>) is greater than or equal to 2 * PER_LIST_DIST_PAGE_COUNT(<1%lu>) * PAGE_LIST_GROUP_COUNT(<2%lu>). ) # *Cause: An expand chunk must have enough pages to be distributed to all database free page list at least twice. The user has specified property values that does not satisfy this condition, EXPAND_CHUNK_PAGE_COUNT <= 2 * PER_LIST_DIST_PAGE_COUNT * PAGE_LIST_GROUP_COUNT. # *Action: Please change EXPAND_CHUNK_PAGE_COUNT or PER_LIST_DIST_PAGE_COUNT to meet the condition EXPAND_CHUNK_PAGE_COUNT <= 2 * PER_LIST_DIST_PAGE_COUNT * PAGE_LIST_GROUP_COUNT. 0x110B6 ( 69814) New Log file group count of loganchor is not equal to Log file group count in altibase.properties. # *Cause: The log file group count in altibase.properties was changed after the db was created. # *Action: Please make sure that the log file group count of altibase.properties is correct. 0x110B7 ( 69815) New There is no need to use resetlog. # *Cause: There is no need to use resetlog. # *Action: Please do not use resetlog when not necessary. 0x110B8 ( 69816) New LFG Count(<0%d>) and Page List Count(<1%d>) are invalid. # *Cause: LFG Count and Page List Count are invalid. # *Action: Please check the property file to make sure that the LFG Count and Page List Count values are correct. 0x11108 ( 69896) New The size of the logfile is not aligned to DIRECT_IO_PAGE_SIZE. # *Cause: The size of logfile is not an exact multiple of DIRECT_IO_PAGE_SIZE. # *Action: Please make sure that the size of the logfile is an exact multiple of DIRECT_IO_PAGE_SIZE. 0x11112 ( 69906) New The OS does not support mmap. # *Cause: The OS does not support mmap. # *Action: Please check LOG_BUFFER_TYPE. ALTIBASE HDB 6.1.1.0.0 Release Notes ALTIBASE HDB® HDB™ Hybrid RDBMS Message Status Description 0x11165 ( 69989) New The storage manager experienced an internal server error. # *Cause: Internal server error. # *Action: Please send a bug report to the vendor. 0x11166 ( 69990) New TRANSACTION_TABLE_SIZE ['<0%d>'] is not powers of two. # *Cause: TRANSACTION_TABLE_SIZE is not 2^n. # *Action: Please check altibase.properties and set TRANSACTION_TABLE_SIZE to 2^n. 0x11167 ( 69991) New The size of the DB file(<0%s>) exceeds the size specified in the MEM_MAX_DB_SIZE property. # *Cause: The MEM_MAX_DB_SIZE property is set to a value that is less than the current size of the DB file. # *Action: Please check the altibase.properties file and increase the value of the MEM_MAX_DB_SIZE property 0x11168 ( 69992) New The logfile is too big. ( <0%d> > <1%d> ) # *Cause: The size of the logfile exceeds the DIRECT I/O limitation. # *Action: Please make sure that the logfile is smaller than the DIRECT I/O limitation, or set the value of the LOG_IO_TYPE property to 0. 0x11169 ( 69993) New Unable to restart a remote table cursor # *Cause: An attempt was made to restart a remote table cursor. # *Action: Please do not attempt to restart a remote table cursor. # Server Internal Message 0xA1049 ( 659529) New Invalid Polygon # *Cause: An attempt was made to perform an operation on an invalid Polygon. # *Action: Please check the structure of the Polygon and try again. 0xA104A ( 659530) New Unverified Polygon # *Cause: An attempt was made to perform an operation on a Polygon that has not been verified. # *Action: Please insert the Polygon again, or perform a validity check on the Polygon. 0x91123 ( 594211) New No port number was specified. # *Cause: The -port option value was omitted. # *Action: Please specify a port number for the port option and try again. ALTIBASE HDB 6.1.1.0.0 Release Notes ALTIBASE HDB® HDB™ Hybrid RDBMS Removed Error Messages Message Status Description 0x3133E ( 201534) Removed The length of the password exceeds the maximum limit. <0%s> # *Cause: # - The length of the password exceeds the maximum limit. # *Action: # - Please use a password that is less than or equal to 40 characters long. 0x3133F ( 201535) Removed The password is too short. # *Cause: # - The password is too short. # *Action: # - Please use a longer password. 0x10018 ( 65560) Removed The version of data file for backup is not compatible with the version of storage manager. Backup DB => [ Version ID = <0%s>, Bit = <1%d>, Endian = <2%s> LogSize = <3%lu> Transaction Table Size = <4%d> ] Server=>[ Version ID = <5%s>, Bit = <6%d>, Endian = <7%s> LogSize = <8%lu> Transaction Table Size = <9%d> ] # *Cause: The database has no backward compatibility. # *Action: Please import or export the database or use one of the previous versions of storage manager that is compatible with the data file. 0x100AF ( 65711) Removed The maximum file size of the OS is less than the database file size that was specified in the CREATE DATABASE statement ( <0%lu> MB ). # *Cause: The maximum file size of the OS is less than that specified in the property. # *Action: Please increase the maximum file size of the OS. 0x100B0 ( 65712) Removed Failed to invoke the getrlimit() system function # *Cause: The system failed to map the log file. # *Action: Please send a bug report to the vendor. 0x100B3 ( 65715) Removed Different Page List Count. The value of the PAGE_LIST_GROUP_COUNT property is different from the one used when createdb was executed. ( current PAGE_LIST_GROUP_COUNT:<0%d>, createdb's PAGE_LIST_GROUP_COUNT <1%d> ) # *Cause: The value of the PAGE_LIST_GROUP_COUNT property is different from the one used when createdb was executed. # *Action: Please set the PAGE_LIST_GROUP_COUNT value to exactly the same value specified when the database was created. ALTIBASE HDB 6.1.1.0.0 Release Notes ALTIBASE HDB® HDB™ Hybrid RDBMS Message Status Description 0x100B4 ( 65716) Removed Different Expand Chunk Page Count. The value of the EXPAND_CHUNK_PAGE_COUNT property is different from the one used when createdb was executed. ( current EXPAND_CHUNK_PAGE_COUNT:<0%d>, createdb EXPAND_CHUNK_PAGE_COUNT <1%d> ) # *Cause: The value of the EXPAND_CHUNK_PAGE_COUNT property is different from the one used when createdb was executed. # *Action: Please set the EXPAND_CHUNK_PAGE_COUNT to exactly the same value specified when the database was created. 0x100B5 ( 65717) Removed Too many pages in per list page count. ( Please change property values to meet the condition EXPAND_CHUNK_PAGE_COUNT(<0%lu>) is greater than or equal to 2 * PER_LIST_DIST_PAGE_COUNT(<1%lu>) * PAGE_LIST_GROUP_COUNT(<2%lu>). ) # *Cause: An expand chunk must have enough pages to be distributed to all database free page list at least twice. The user has specified property values that does not satisfy this condition, EXPAND_CHUNK_PAGE_COUNT <= 2 * PER_LIST_DIST_PAGE_COUNT * PAGE_LIST_GROUP_COUNT. # *Action: Please change EXPAND_CHUNK_PAGE_COUNT or PER_LIST_DIST_PAGE_COUNT to meet the condition EXPAND_CHUNK_PAGE_COUNT <= 2 * PER_LIST_DIST_PAGE_COUNT * PAGE_LIST_GROUP_COUNT. 0x100B6 ( 65718) Removed Log file group count of loganchor is not equal to Log file group count in altibase.properties. # *Cause: The log file group count in altibase.properties was changed after the db was created. # *Action: Please make sure that the log file group count of altibase.properties is correct. 0x100B7 ( 65719) Removed There is no need to use resetlog. # *Cause: There is no need to use resetlog. # *Action: Please do not use resetlog when not necessary. 0x100B8 ( 65720) Removed LFG Count(<0%d>) and Page List Count(<1%d>) are invalid. # *Cause: LFG Count and Page List Count are invalid. # *Action: Please check the property file to make sure that the LFG Count and Page List Count values are correct. ALTIBASE HDB 6.1.1.0.0 Release Notes ALTIBASE HDB® HDB™ Hybrid RDBMS Message Status Description 0x10108 ( 65800) Removed The size of the logfile is not aligned to DIRECT_IO_PAGE_SIZE. # *Cause: The size of logfile is not an exact multiple of DIRECT_IO_PAGE_SIZE. # *Action: Please make sure that the size of the logfile is an exact multiple of DIRECT_IO_PAGE_SIZE. 0x10112 ( 65810) Removed The OS does not support mmap. # *Cause: The OS does not support mmap. # *Action: Please check LOG_BUFFER_TYPE. 0x10166 ( 65894) Removed TRANSACTION_TABLE_SIZE ['<0%d>'] is not a power of two. # *Cause: The value of the TRANSACTION_TABLE_SIZE property is not a power of two(=2^n). # *Action: Please check the altibase.properties file and set the value of TRANSACTION_TABLE_SIZE to a power of two (2^n). # Server Internal Message Modified Error Messages Message Status Description 0x51017 ( 331799) Before VARCHAR declarations are not permitted in #include files. # *Cause: A varchar declaration was used in an #include file. # *Action: Please remove the varchar declaration from the #include file. Consider using EXEC SQL INCLUDE instead. After VARCHAR declarations are not permitted in #include files. # *Cause: A varchar declaration was used in an #include file. # *Action: Please remove the varchar declaration from the #include file. Consider using EXEC SQL INCLUDE instead. # embedded sql error (301-399L) ### 0x410CB ( 266443) Before 0x417D3 ( 268243) This replication mode is not supported at the session level. # *Cause: The replication modes (EAGER, ACKED, LAZY) do not pertain to sessions. # *Action: No action required. ALTIBASE HDB 6.1.1.0.0 Release Notes ALTIBASE HDB® HDB™ Hybrid RDBMS Message Status Description After 0x410CB ( 266443) This replication mode is not supported at the session level. # *Cause: The replication modes (EAGER, ACKED, LAZY) do not pertain to sessions. # *Action: No action required. 0xA1045 ( 659525) Before A multipolygon has intersecting polygons <0%d>, <1%d> # *Cause: Object integrity violation. # *Action: Please make sure to use a valid object After A multipolygon has intersecting polygons : (polygon:<0%d>, ring:<1%d>), (polygon:<2%d>, ring:<3%d>) # *Cause: Object integrity violation. # *Action: Please make sure to use a valid object 2.3 Packages OS Version CPU Archive Name AIX 5.3 powerPC altibase-server-6.1.1.0-AIX-POWERPC-64bit-release.run altibase-client-6.1.1.0-AIX-POWERPC-64bit-release.run altibase-client-6.1.1.0-AIX-POWERPC-32bit-release.run altibase-oraAdapter-6.1.1.0.0-AIX-POWERPC-64bit- release.run HP-UX 11.11 PA-RISC altibase-server-6.1.1.0-hpux-PA-RISC-64bit-release.run altibase-client-6.1.1.0-hpux-PA-RISC-64bit-release.run altibase-client-6.1.1.0-hpux-PA-RISC-32bit-release.run 11.23 IA64 altibase-server-6.1.1.0-hpux-IA64-64bit-release.run altibase-client -6.1.1.0-hpux-IA64-64bit-release.run altibase-client -6.1.1.0-hpux-IA64-32bit-release.run Solaris 2.8 SPARC altibase-server-6.1.1.0-SOLARIS-SPARC-64bit-release.run altibase-client-6.1.1.0-SOLARIS-SPARC-64bit-release.run altibase-client-6.1.1.0-SOLARIS-SPARC-32bit-release.run 2.10 X86 altibase-server-6.1.1.0-SOLARIS-X86-64bit-release.run altibase-client-6.1.1.0-SOLARIS-X86-64bit-release.run altibase-client-6.1.1.0-SOLARIS-X86-32bit-release.run Linux/glibc ES40-x64 X86 altibase-server-6.1.1.0-LINUX-X86-64bit-release.run altibase-client-6.1.1.0-LINUX-X86-64bit-release.run altibase-server-6.1.1.0-LINUX-X86-32bit-release.run altibase-client-6.1.1.0-LINUX-X86-32bit-release.run altibase_oraAdapter_6.1.1.0.0_LINUX-X86-64bit.run altibase_oraAdapter_6.1.1.0.0_LINUX-X86-32bit.run Windows 2003 X86 altibase-server-6.1.1.0-WIN_NT-X86-64bit-release.exe altibase-client -6.1.1.0-WIN_NT-X86-64bit-release.exe altibase-server-6.1.1.0-WIN_NT-X86-32bit-release.exe altibase-client -6.1.1.0-WIN_NT-X86-32bit-release.exe ALTIBASE HDB 6.1.1.0.0 Release Notes ALTIBASE HDB® HDB™ Hybrid RDBMS 2.4 Downloads Location Packages http://www.altibase.com/ Manual http://www.altibase.com/ Installation Please refer to the Installation Guide.
패치노트
-
- ALTIBASE HDB v.6.3.1.8.3 Patch Notes (Korean) ㅣ 2016-11-24
- ALTIBASE HDB 6.3.1.8.3 Patch Notes ALTIBASE HDB Patch Notes 6.3.1.8.3 2 / 4 목차 BUG-44022 CREATE AS SELECT로 TABLE 생성할 때에 OUTER JOIN과 LOB를 같이 사용하면 에러가 발생 ...................................................
-
미리보기
ALTIBASE HDB 6.3.1.8.3 Patch Notes ALTIBASE HDB Patch Notes 6.3.1.8.3 2 / 4 목차 BUG-44022 CREATE AS SELECT로 TABLE 생성할 때에 OUTER JOIN과 LOB를 같이 사용하면 에러가 발생 .......................................................................... 3 ALTIBASE HDB Patch Notes 6.3.1.8.3 2 / 4 ALTIBASE HDB Patch Notes 6.3.1.8.3 3 / 4 BUG-44022 CREATE AS SELECT로 TABLE 생성할 때에 OUTER JOIN과 LOB를 같이 사용하면 에러가 발생 Module qp-ddl-dcl-execute Category Functional Error 재현빈도 Always Reproducing Conditions 재현절차 # SOURCE DROP TABLE T1; CREATE TABLE T1 ( I1 INTEGER, I2 INTEGER, I3 CLOB ) TABLESPACE SYS_TBS_DISK_DATA; INSERT INTO T1 VALUES( 1, 0, 1 ); INSERT INTO T1 VALUES( 2, 1, null ); # MVIEW DROP MATERIALIZED VIEW V1; CREATE MATERIALIZED VIEW TEST TABLESPACE SYS_TBS_DISK_DATA BUILD IMMEDIATE REFRESH FORCE ON DEMAND AS SELECT B.I3 FROM ( SELECT I1, I3 FROM T1 ) A LEFT OUTER JOIN T1 B ON A.I1 = B.I2; # TABLE DROP TABLE T2; CREATE TABLE T2 TABLESPACE SYS_TBS_DISK_DATA AS SELECT B.I3 ALTIBASE HDB Patch Notes 6.3.1.8.3 4 / 4 FROM ( SELECT I1, I3 FROM T1 ) A LEFT OUTER JOIN T1 B ON A.I1 = B.I2; 수행결과 iSQL> CREATE MATERIALIZED VIEW TEST 2 TABLESPACE SYS_TBS_DISK_DATA 3 BUILD IMMEDIATE 4 REFRESH FORCE ON DEMAND 5 AS 6 SELECT B.I3 7 FROM ( SELECT I1, I3 FROM T1 ) A 8 LEFT OUTER JOIN T1 B ON A.I1 = B.I2; [ERR-41082 : Internal server error (mmcStatement::execute code=0x1214b143 You can't use this LobLocator.)] 예상결과 iSQL> CREATE MATERIALIZED VIEW TEST 2 TABLESPACE SYS_TBS_DISK_DATA 3 BUILD IMMEDIATE 4 REFRESH FORCE ON DEMAND 5 AS 6 SELECT B.I3 7 FROM ( SELECT I1, I3 FROM T1 ) A 8 LEFT OUTER JOIN T1 B ON A.I1 = B.I2; Create success. 증상 기존 코드에서는 outer join 시 lob locator 가 null 일 경우의 예외처리가 없었습니다. lob locator 가 null 일 경우 Lob 정보를 구성하지 않도록 예외처리를 추가하여 null point access 가 발생하지 않도록 수정했습니다. 변경사항 Performance View N/A Property N/A Compile Option N/A Error Code N/A Workaround N/A
-
- ALTIBASE HDB v.6.3.1.6.3 Patch Notes (Korean) ㅣ 2016-03-18
- ALTIBASE HDB 6.3.1.6.3 Patch Notes ALTIBASE HDB Patch Notes 6.3.1.6.3 2 / 14 목차 BUG-39354 alter database create checkpoint image 구문을 사용하여 media recovery를 수행할 경우 비정상 종료할 수 있다. ......................
-
미리보기
ALTIBASE HDB 6.3.1.6.3 Patch Notes ALTIBASE HDB Patch Notes 6.3.1.6.3 2 / 14 목차 BUG-39354 alter database create checkpoint image 구문을 사용하여 media recovery를 수행할 경우 비정상 종료할 수 있다. ...................... 3 BUG-42815 Multi table Insert 시에 첫 번째 입력 값으로 subquery가 사용되면 비정상 종료될 수 있다. .......................................... 4 BUG-42816 PAGE_LIST_GROUP_COUNT 프로퍼티의 설정 값이 18인 경우 서버가 비정상 종료될 수 있다. ............................................. 5 BUG-42817 이중화 수행중인 partitioned table에 DML과 truncate를 동시에 사용하면, deadlock이 발생할 수 있다. ................................... 6 BUG-42820 Disk tablespace를 EXTENTSIZE 512M로 서버가 생성하면 비정상 종료될 수 있다. .................................................................. 7 BUG-42826 ARCHIVE_FULL_ACTION 프로퍼티가 오동작할 수 있다. ............. 8 BUG-42848 Recursive with 절을 잘못 사용할 경우 서버가 비정상 종료될 수 있다. ................................................................................ 9 BUG-42856 AUTO_COMMIT = 0인 경우 Job Scheduler가 동작 중이면, select 쿼리 수행시 서버가 비정상 종료될 수 있다. .......................... 10 BUG-42877 LOB 컬럼의 테이블스페이스를 지정한 후 테이블의 스키마를 변경하면, LOB column 정보가 부정확해진다. ........................ 11 BUG-42891 JOIN UPDATE, DELETE의 Target이 Right이면, 서버가 비정상 종료될 수 있다. ................................................................ 14 ALTIBASE HDB Patch Notes 6.3.1.6.3 2 / 14 ALTIBASE HDB Patch Notes 6.3.1.6.3 3 / 14 BUG-39354 alter database create checkpoint image 구문을 사용하여 media recovery를 수행할 경우 비정상 종료할 수 있다. Module sm_recovery Category Functional Error 재현빈도 Always Reproducing Conditions 재현절차 해당 없음 수행결과 해당 없음 예상결과 해당 없음 증상 alter database create checkpoint image 구문을 사용하여 media recovery를 수행할 경우 비정상 종료할 수 있다. memory checkpoint image 파일이 유실되었어도, loganchor와 log가 있다면 media recovery를 하여 복원할 수 있도록 한다. 변경사항 Performance View None Property None Compile Option None Error Code None Workaround 해당 없음 ALTIBASE HDB Patch Notes 6.3.1.6.3 4 / 14 BUG-42815 Multi table Insert 시에 첫 번째 입력 값으로 subquery가 사용되면 비정상 종료될 수 있다. Module qx Category Fatal 재현빈도 Always Reproducing Conditions 재현절차 해당 없음 수행결과 해당 없음 예상결과 해당 없음 증상 Multi table Insert 시에 첫 번째 입력 값으로 subquery가 사용되면 비정상 종료될 수 있다. 변경사항 Performance View None Property None Compile Option None Error Code None Workaround 해당 없음 ALTIBASE HDB Patch Notes 6.3.1.6.3 5 / 14 BUG-42816 PAGE_LIST_GROUP_COUNT 프로퍼티의 설정 값이 18인 경우 서버가 비정상 종료될 수 있다. Module sm Category Fatal 재현빈도 Always Reproducing Conditions 재현절차 해당 없음 수행결과 해당 없음 예상결과 해당 없음 증상 PAGE_LIST_GROUP_COUNT 프로퍼티의 설정 값이 18이면, 서버가 비정상 종료될 수 있다. PER_LIST_DIST_PAGE_COUNT 초기값 오류를 수정하여 비정상종료하지 않도록 한다. 변경사항 Performance View None Property None Compile Option None Error Code None Workaround PER_LIST_DIST_PAGE_COUNT 의 값을 64로 설정한다. ALTIBASE HDB Patch Notes 6.3.1.6.3 6 / 14 BUG-42817 이중화 수행중인 partitioned table에 DML과 truncate를 동시에 사용하면, deadlock이 발생할 수 있다. Module rp-control Category Functional Error 재현빈도 Frequence Reproducing Conditions 재현절차 해당 없음 수행결과 해당 없음 예상결과 해당 없음 증상 이중화를 수행중인 partitioned table에 DML과 truncate를 동시에 사용하면, deadlock(ERR-11041)이 발생할 수 있다. [ERR-11041 : A deadlock situation has been detected.] 변경사항 Performance View None Property None Compile Option None Error Code None Workaround 해당 없음 ALTIBASE HDB Patch Notes 6.3.1.6.3 7 / 14 BUG-42820 Disk tablespace를 EXTENTSIZE 512M로 서버가 생성하면 비정상 종료될 수 있다. Module sm-disk-page Category Fatal 재현빈도 Always Reproducing Conditions 재현절차 해당 없음 수행결과 해당 없음 예상결과 해당 없음 증상 Disk tablespace를 EXTENTSIZE 512M로 생성하면 서버가 비정상 종료될 수 있다. Page slot을 할당할 때 page header info의 page slot에 index값을 매기는 logic을 수정하여 동작하도록 한다. 변경사항 Performance View None Property None Compile Option None Error Code None Workaround EXTENTSIZE를 256으로 한다. ALTIBASE HDB Patch Notes 6.3.1.6.3 8 / 14 BUG-42826 ARCHIVE_FULL_ACTION 프로퍼티가 오동작할 수 있다. Module sm_recovery Category Functional Error 재현빈도 Always Reproducing Conditions 재현절차 해당 없음 수행결과 해당 없음 예상결과 해당 없음 증상 ARCHIVE_FULL_ACTION 프로퍼티가 오동작할 수 있다. ARCHIVE_FULL_ACTION의 값이 0이면, logfile이 삭제되도록 수정한다. 변경사항 Performance View None Property None Compile Option None Error Code None Workaround 해당 없음 ALTIBASE HDB Patch Notes 6.3.1.6.3 9 / 14 BUG-42848 Recursive with 절을 잘못 사용할 경우 서버가 비정상 종료될 수 있다. Module qx Category Fatal 재현빈도 Always Reproducing Conditions 재현절차 해당 없음 수행결과 해당 없음 예상결과 해당 없음 증상 Recursive with 절을 잘못 사용하면, 서버가 비정상 종료될 수 있다. 예외처리를 하여 정상 동작할 수 있도록 한다. 변경사항 Performance View None Property None Compile Option None Error Code None Workaround 해당 없음 ALTIBASE HDB Patch Notes 6.3.1.6.3 10 / 14 BUG-42856 AUTO_COMMIT = 0인 경우 Job Scheduler가 동작 중이면, select 쿼리 수행시 서버가 비정상 종료될 수 있다. Module qx Category Fatal 재현빈도 Rare Reproducing Conditions 재현절차 해당 없음 수행결과 해당 없음 예상결과 해당 없음 증상 AUTO_COMMIT = 0인 경우 Job Scheduler 동작 중이면, select 쿼리를 수행할 때 서버가 비정상 종료될 수 있다. Job Session에서 smiTrans를 할당하지 않아서 생기는 문제이며, AUTO_COMMIT이 0이어도 smiTrans를 할당하도록 수정한다. 변경사항 Performance View None Property None Compile Option None Error Code None Workaround 해당 없음 ALTIBASE HDB Patch Notes 6.3.1.6.3 11 / 14 BUG-42877 LOB 컬럼의 테이블스페이스를 지정한 후 테이블의 스키마를 변경하면, LOB column 정보가 부정확해진다. Module qp-ddl-dcl-execute Category Functional Error 재현빈도 Always Reproducing Conditions 재현절차 DROP TABLESPACE TBS_DISK_LOB_TEST INCLUDING CONTENTS AND DATAFILES; CREATE TABLESPACE TBS_DISK_LOB_TEST DATAFILE 'LOB_TEST_00.DBF' SIZE 10M AUTOEXTEND OFF; CREATE TABLE LOB_TEST ( C1 INTEGER NOT NULL, C2 CLOB NULL ) TABLESPACE SYS_TBS_DISK_DATA LOB(C2) STORE AS ( TABLESPACE TBS_DISK_LOB_TEST ); SELECT TABLE_NAME, A.TBS_ID AS TABLE_TBS, B.TBS_ID AS LOB_TBS ,B.IS_DEFAULT_TBS FROM SYSTEM_.SYS_TABLES_ A , SYSTEM_.SYS_LOBS_ B WHERE A.TABLE_ID=B.TABLE_ID; ALTER TABLE LOB_TEST ADD COLUMN (C3 VARCHAR(20)); SELECT TABLE_NAME, A.TBS_ID AS TABLE_TBS, B.TBS_ID AS LOB_TBS ,B.IS_DEFAULT_TBS FROM SYSTEM_.SYS_TABLES_ A , SYSTEM_.SYS_LOBS_ B WHERE A.TABLE_ID=B.TABLE_ID; 수행결과 CREATE TABLE 직후 iSQL> SELECT TABLE_NAME, A.TBS_ID AS TABLE_TBS, B.TBS_ID AS LOB_TBS ,B.IS_DEFAULT_TBS 2 FROM SYSTEM_.SYS_TABLES_ A 3 , SYSTEM_.SYS_LOBS_ B ALTIBASE HDB Patch Notes 6.3.1.6.3 12 / 14 4 WHERE A.TABLE_ID=B.TABLE_ID; TABLE_NAME TABLE_TBS LOB_TBS IS_DEFAULT_TBS ----------------------------------------------------------------------------- LOB_TEST 2 9 T 1 row selected. ALTER TABLE 직후 iSQL> SELECT TABLE_NAME, A.TBS_ID AS TABLE_TBS, B.TBS_ID AS LOB_TBS ,B.IS_DEFAULT_TBS 2 FROM SYSTEM_.SYS_TABLES_ A 3 , SYSTEM_.SYS_LOBS_ B 4 WHERE A.TABLE_ID=B.TABLE_ID; TABLE_NAME TABLE_TBS LOB_TBS IS_DEFAULT_TBS ----------------------------------------------------------------------------- LOB_TEST 2 9 F 1 row selected. 예상결과 CREATE TABLE 직후 iSQL> SELECT TABLE_NAME, A.TBS_ID AS TABLE_TBS, B.TBS_ID AS LOB_TBS ,B.IS_DEFAULT_TBS 2 FROM SYSTEM_.SYS_TABLES_ A 3 , SYSTEM_.SYS_LOBS_ B 4 WHERE A.TABLE_ID=B.TABLE_ID; TABLE_NAME TABLE_TBS LOB_TBS IS_DEFAULT_TBS ----------------------------------------------------------------------------- LOB_TEST 2 9 T 1 row selected. ALTER TABLE 직후 iSQL> SELECT TABLE_NAME, A.TBS_ID AS TABLE_TBS, B.TBS_ID AS LOB_TBS ,B.IS_DEFAULT_TBS 2 FROM SYSTEM_.SYS_TABLES_ A 3 , SYSTEM_.SYS_LOBS_ B 4 WHERE A.TABLE_ID=B.TABLE_ID; TABLE_NAME TABLE_TBS LOB_TBS IS_DEFAULT_TBS ----------------------------------------------------------------------------- LOB_TEST 2 9 T 1 row selected. 증상 LOB컬럼의 테이블스페이스를 지정한 후에 테이블의 스키마를 ALTIBASE HDB Patch Notes 6.3.1.6.3 13 / 14 변경하면, LOB column 정보가 부정확해진다. 테이블을 생성할 때 lob 컬럼의 정보는 메타 캐쉬에 반영되지 않는다. 따라서 SYS_LOBS_의 IS_DEFAULT_TBS 값을 column flag에 추가하도록 수정한다. 변경사항 Performance View None Property None Compile Option None Error Code None Workaround 해당 없음 ALTIBASE HDB Patch Notes 6.3.1.6.3 14 / 14 BUG-42891 JOIN UPDATE, DELETE의 Target이 Right이면, 서버가 비정상 종료될 수 있다. Module qx Category Fatal 재현빈도 Always Reproducing Conditions 재현절차 해당 없음 수행결과 해당 없음 예상결과 해당 없음 증상 JOIN UPDATE, DELETE의 Target이 Right이면, 서버가 비정상 종료될 수 있다. 변경사항 Performance View None Property None Compile Option None Error Code None Workaround JOIN UPDATE, DELETE의 Target을 Left에 둔다.