데이터 이관 작업 시 lob 데이터를 고려하지 않고 export 나 load를 수행하게 된다.

많은 테이블에 대해서 이관 작업을 급히 수행해야 되는 경우, 이관 대상의 테이블 컬럼 정보를 확인하지 않고 수행을 하다 보면 lob 데이터가 있는 테이블은 load 시 “문자 짤림" 로그가 남게 된다.

 

데이터 코드 셋이 잘못되어서 발생하는 경우도 있지만, lob 컬럼이 속한 테이블 경우 추출/적재 시 lob 컬럼에 대한 옵션을 주지 않으면 발생하기도 한다. 또한 load 작업 시 여러 이유에서 실패를 하거나 중단되는 경우 load 작업을 위한 임시 공간이 full 발생하여 load 작업이 실패하게 되는 경우도 발생한다.

 

데이터 이관 작업 시, lob 데이터 및 대용량 데이터 이관을 고려하여 다음과 같이 export 및 import/load 구문을 사용하는 것이 바람직하지 않을까 싶다.

 

1. export 구문

  lob 컬럼이 존재하는 테이블을 LOBS TO 옵션을 주어 별도의 경로에 데이터를 저장하도록 함

  (녹색 글자: 경로 및 파일명을 의미함, {}: shell 변수들을 의미함)

export to export/${TABSCHEMA}.${TABNAME}.ixf of ixf LOBS TO lobs/${TABNAME}  modified by lobsinfile messages export/${TABSCHEMA}.${TABNAME}.exp select * from ${TABSCHEMA}.${TABNAME} with ur

 

2. load 구문

  LOB 데이터의 경로를 LOBS FROM 옵션을 통하여 지정하고, load 작업 시 필요한 임시 공간은 tempfiles path 옵션으로 지정함

load from export/${TABSCHEMA}.${TABNAME}.ixf of ixf LOBS FROM lobs/${TABNAME}  modified by lobsinfile messages ${TABSCHEMA}.${TABNAME}.msg tempfiles path tmp replace into ${TABSCHEMA}.${TABNAME}

고객사 기술지원을 하다보면, 데이터 추출/적재 작업은 개발 프로그램을 통하여 진행하고자 하는 요건이 발생한다.

개발자들이 이관 작업을 수행하는 경우는 개발 language를 통해서 구현하고 싶어하고, DBA가 수행하는 경우는 shell 방식으로 처리하는 경우가 보편적이지 않을까 싶다.

 

admin_cmd를 이용하여 다양한 관리 작업을 개발 레벨에서 관리를 할 수 있다. 자세한 정보는 정보센터를 통해서 확인이 가능하다.

문서: http://pic.dhe.ibm.com/infocenter/db2luw/v10r5/index.jsp?topic=%2Fcom.ibm.db2.luw.sql.rtn.doc%2Fdoc%2Fr0012547.html

 

0. 테스트 환경

- DB2: V10.5

- 오라클 호환성: 적용하지 않음

- 인스턴스명: db2inst

- DB 명: sample

- 테이블: employee

 

1. 데이터 추출/적재(export/load)

- procedure code

  문법 체크 및 debug를 위해서 dbms_output.put_line() 를 사용하였고, export/load의 실행 로그 확인을 위해 커서를 선언하여 출력 변수로 추출하여 반환하도록 구현하였다.

db2 +p -td@ << EOF
connect to sample @
create or replace procedure sp_load ( IN I_VAR1 char(3), OUT O_EXP_MSG varchar(256), OUT O_LOA_MSG varchar(256), OUT O_MSG varchar(256))
specific sp_load
dynamic result sets 2
begin
     DECLARE EXP_ROWS_EXPORTED  BIGINT ;
     DECLARE EXP_RETRIEVAL      VARCHAR(512);
     DECLARE EXP_REMOVAL        VARCHAR(512) ;
    
     DECLARE LOA_ROWS_READ      BIGINT ;
     DECLARE LOA_ROWS_SKIPPED   BIGINT ;
     DECLARE LOA_ROWS_LOADED    BIGINT ;
     DECLARE LOA_ROWS_REJECTED  BIGINT ;
     DECLARE LOA_ROWs_DELETED   BIGINT ;
     DECLARE LOA_ROWS_COMMITED  BIGINT ;
     DECLARE LOA_ROWS_PARTITIONED BIGINT ;
     DECLARE LOA_NUM_AGENTINFO_ENTRIES BIGINT;
     DECLARE LOA_MSG_RETRIEVAL  VARCHAR(512) ;
     DECLARE LOA_MSG_REMOVAL    VARCHAR(512) ;
    
     DECLARE P_STMT             VARCHAR(2048) ;
     DECLARE P_EXPORT_PATH      VARCHAR(512) ;
     DECLARE P_EXPORT_FILE      VARCHAR(32)  ;
    
     DECLARE EXP_RESULT             RESULT_SET_LOCATOR VARYING;
     DECLARE LOA_RESULT             RESULT_SET_LOCATOR VARYING;
    
     declare EXIT handler for SQLEXCEPTION
     begin
           get DIAGNOSTICS EXCEPTION 1 O_MSG = MESSAGE_TEXT;
     end ;
    
     SET P_EXPORT_PATH = '/db2user/db2inst/script/' ;
     SET P_EXPORT_FILE = 'emp.ixf' ;
     SET P_STMT = 'select * from emp where workdept = ''' || I_VAR1 || '''' ;
    
     call dbms_output.put_line('export to ''' || P_EXPORT_PATH || P_EXPORT_FILE || ''' of ixf messages on server ' || P_STMT) ;
    
     call sysproc.admin_cmd('export to ''' || P_EXPORT_PATH || P_EXPORT_FILE || ''' of ixf messages on server ' || P_STMT) ;
    
     associate result set locators(EXP_RESULT) with procedure sysproc.admin_cmd ;
     allocate EXP_CUR cursor for result set EXP_RESULT ;
     fetch from EXP_CUR into EXP_ROWS_EXPORTED, EXP_RETRIEVAL, EXP_REMOVAL ;
    
     call dbms_output.put_line('ROWS_EXPORTED : ' || TO_CHAR(EXP_ROWS_EXPORTED) || ',  RETRIVAL : ' || EXP_RETRIEVAL) ;
     SET O_EXP_MSG = EXP_RETRIEVAL ;
     
     
     call dbms_output.put_line('load from ''' || P_EXPORT_PATH || P_EXPORT_FILE || ''' of ixf messages on server insert into t_emp nonrecoverable indexing mode autoselect') ;
    
     call sysproc.admin_cmd('load from ''' || P_EXPORT_PATH || P_EXPORT_FILE || ''' of ixf messages on server insert into t_emp nonrecoverable indexing mode autoselect') ;

     associate result set locators(LOA_RESULT) with procedure sysproc.admin_cmd ;
     allocate LOA_CUR cursor for result set LOA_RESULT ;
     fetch from LOA_CUR into LOA_ROWS_READ, LOA_ROWS_SKIPPED, LOA_ROWS_LOADED, LOA_ROWS_REJECTED, LOA_ROWS_DELETED, LOA_ROWS_COMMITED, LOA_ROWS_PARTITIONED, LOA_NUM_AGENTINFO_ENTRIES, LOA_MSG_RETRIEVAL, LOA_MSG_REMOVAL ;
    
     call dbms_output.put_line('ROWS_READ(' ||TO_CHAR(LOA_ROWS_READ) || ') ROWS_SKIPPED(' || TO_CHAR(LOA_ROWS_SKIPPED) || ') ROWS_LOADED(' || TO_CHAR(LOA_ROWS_LOADED) || ') ROWS_REJECTED(' || TO_CHAR(LOA_ROWS_REJECTED) || ') ROWS_DELETED('|| TO_CHAR(LOA_ROWS_DELETED) || ') ROWS_COMMITED(' || TO_CHAR(LOA_ROWS_COMMITED) || ') MSG_RETRIEVAL(' || LOA_MSG_RETRIEVAL) ;
     SET O_LOA_MSG = LOA_MSG_RETRIEVAL ;
     --select sqlcode, msg into O_LOA_SQLCODE,O_LOA_MSG from table (sysproc.admin_get_msgs(LOA_MSG_RETRIEVAL)) as msg ;
     --call sysproc.admin_remove_msgs(LOA_MSG_REMOVAL) ;
    
end
@
terminate @
EOF

 

실행 테스트

   Database Connection Information

Database server        = DB2/AIX64 10.5.2
SQL authorization ID   = DB2INST
Local database alias   = SAMPLE

[db2inst@dbcni1:/db2user/db2inst/script] $ db2 set serveroutput on
DB20000I  The SET SERVEROUTPUT command completed successfully.

[db2inst@dbcni1:/db2user/db2inst/script] $ db2 "call sp_load ('A00', ?,  ?, ?)"

  Value of output parameters
  --------------------------
  Parameter Name  : O_EXP_MSG
  Parameter Value : SELECT SQLCODE, MSG FROM TABLE(SYSPROC.ADMIN_GET_MSGS('15911_503_DB2INST')) AS MSG

  Parameter Name  : O_LOA_MSG
  Parameter Value : SELECT SQLCODE, MSG FROM TABLE(SYSPROC.ADMIN_GET_MSGS('17722_11123_DB2INST')) AS MSG

  Parameter Name  : O_MSG
  Parameter Value : -

  Return Status = 0

export to '/db2user/db2inst/script/emp.ixf' of ixf messages on server select * from emp where workdept = 'A00'
ROWS_EXPORTED : 5,  RETRIVAL : SELECT SQLCODE, MSG FROM TABLE(SYSPROC.ADMIN_GET_MSGS('2786_6890_DB2INST')) AS MSG
load from '/db2user/db2inst/script/emp.ixf' of ixf messages on server insert into t_emp nonrecoverable indexing mode autoselect
ROWS_READ(5) ROWS_SKIPPED(0) ROWS_LOADED(5) ROWS_REJECTED(0) ROWS_DELETED(0) ROWS_COMMITED(5) MSG_RETRIEVAL(SELECT SQLCODE, MSG FROM TABLE(SYSPROC.ADMIN_GET_MSGS('28553_15499_DB2INST')) AS MSG

 

 

2. 데이터 적재(load cursor 방식)

export하지 않고 cursor에 담아 load 처리하는 방법

 

db2 +p -td@ << EOF
connect to sample @
create or replace procedure sp_cur ( IN I_VAR1 char(3), OUT O_LOA_MSG varchar(256), OUT O_MSG varchar(256))
specific sp_cur
dynamic result sets 1
begin
     DECLARE LOA_ROWS_READ      BIGINT ;
     DECLARE LOA_ROWS_SKIPPED   BIGINT ;
     DECLARE LOA_ROWS_LOADED    BIGINT ;
     DECLARE LOA_ROWS_REJECTED  BIGINT ;
     DECLARE LOA_ROWs_DELETED   BIGINT ;
     DECLARE LOA_ROWS_COMMITED  BIGINT ;
     DECLARE LOA_ROWS_PARTITIONED BIGINT ;
     DECLARE LOA_NUM_AGENTINFO_ENTRIES BIGINT;
     DECLARE LOA_MSG_RETRIEVAL  VARCHAR(512) ;
     DECLARE LOA_MSG_REMOVAL    VARCHAR(512) ;
    
     DECLARE P_STMT             VARCHAR(2048) ;
     DECLARE LOA_RESULT             RESULT_SET_LOCATOR VARYING;
    
     declare EXIT handler for SQLEXCEPTION
     begin
           get DIAGNOSTICS EXCEPTION 1 O_MSG = MESSAGE_TEXT;
     end ;
    
     SET P_STMT = 'select * from emp where workdept = ''' || I_VAR1 || '''' ;
    
    
     call dbms_output.put_line('load from ( database sample ' || P_STMT || ' ) of cursor messages on server insert into t_emp nonrecoverable indexing mode autoselect') ;
    
     call sysproc.admin_cmd('load from ( database sample ' || P_STMT || ' ) of cursor messages on server insert into t_emp nonrecoverable indexing mode autoselect') ;

     associate result set locators(LOA_RESULT) with procedure sysproc.admin_cmd ;
     allocate LOA_CUR cursor for result set LOA_RESULT ;
     fetch from LOA_CUR into LOA_ROWS_READ, LOA_ROWS_SKIPPED, LOA_ROWS_LOADED, LOA_ROWS_REJECTED, LOA_ROWS_DELETED, LOA_ROWS_COMMITED, LOA_ROWS_PARTITIONED, LOA_NUM_AGENTINFO_ENTRIES, LOA_MSG_RETRIEVAL, LOA_MSG_REMOVAL ;
    
     call dbms_output.put_line('ROWS_READ(' ||TO_CHAR(LOA_ROWS_READ) || ') ROWS_SKIPPED(' || TO_CHAR(LOA_ROWS_SKIPPED) || ') ROWS_LOADED(' || TO_CHAR(LOA_ROWS_LOADED) || ') ROWS_REJECTED(' || TO_CHAR(LOA_ROWS_REJECTED) || ') ROWS_DELETED('|| TO_CHAR(LOA_ROWS_DELETED) || ') ROWS_COMMITED(' || TO_CHAR(LOA_ROWS_COMMITED) || ') MSG_RETRIEVAL(' || LOA_MSG_RETRIEVAL) ;
     SET O_LOA_MSG = LOA_MSG_RETRIEVAL ;
end
@
terminate @
EOF

 

실행 테스트

$ db2 connect to sample user db2inst using
$ db2 set serveroutput on
$ db2 "call sp_cur('A00',?,?)"

  Value of output parameters
  --------------------------
  Parameter Name  : O_LOA_MSG
  Parameter Value : SELECT SQLCODE, MSG FROM TABLE(SYSPROC.ADMIN_GET_MSGS('22620_26387_DB2INST')) AS MSG

  Parameter Name  : O_MSG
  Parameter Value : -

  Return Status = 0

load from ( database sample select * from emp where workdept = 'A00' ) of cursor messages on server insert into t_emp indexing mode autoselect
ROWS_READ(5) ROWS_SKIPPED(0) ROWS_LOADED(5) ROWS_REJECTED(0) ROWS_DELETED(0) ROWS_COMMITED(5) MSG_RETRIEVAL(SELECT SQLCODE, MSG FROM TABLE(SYSPROC.ADMIN_GET_MSGS('22620_26387_DB2INST')) AS MSG

 

참고로 lob 컬럼이 있는 경우는 export 및 import/load 시 추가 옵션이 필요하며, 대용량 데이터의 load 작업 경우 temp 경로를 설정하여 주도록 한다.

오라클 호환성 기능은 DB2_COMPATIBILITY_VECTOR 레지스트리 변수에 값을 적용함으로서 오라클 문법/표현등을 사용하게 된다.

그러나 일부 기능은 데이터베이스를 생성하기 전에만 적용되는 것들이 있다.

Developerworks에 나온 문서(https://www.ibm.com/developerworks/mydeveloperworks/blogs/SQLTips4DB2LUW/entry/about_the_db2_compatibility_vector_and_what_not_to_say_at_the_dinner_table282?lang=en) 의 내용 및 기억을 더듬어보면

1. NUMBER

2. VARCHAR2

3. DATE

4. Oracle Data Dictionary

정도가 아닌가 싶다.

 

이 중 VARCHAR2를 세부적으로 들여다보면 특별한 기능을 포함하고 있다.

오라클이 공백과 NULL 값 구분을 하지 않기 때문에, DB2로 Data Migration을 하게 되면 이것은 문제가 된다. (DB2는 공백과 NULL을 구분하기에)

 

따라서 SQL 내에서 공백의 데이터를 처리하다보면 Oracle과는 다른 결과를 갖게 된다.

이 문제를 해결하기 위해서 VARCHAR2 (0x20) 값을 적용할 필요가 생긴다.

 

1. DB2에서 공백 값 테스트

$ db2 "values (COALESCE('','NULL'))"

1
----


$ db2 "values (COALESCE(' ','NULL'))"

1
----


$ db2 "values (COALESCE(NULL,'NULL'))"

1
----
NULL



$> db2 "values (bigint(''))"

1
--------------------


SQL0420N  Invalid character found in a character string argument of the function "BIGINT".  SQLSTATE=22018

 

2. 오라클의 공백 값 테스트

SQL> select nvl('','NULL') as C1 from dual;

C1
----
NULL


SQL> select nvl(' ','NULL') as C2 from dual;

C
-


SQL> select nvl(NULL,'NULL') as C3 from dual;

C3
----
NULL



SQL> select to_number('') as C4 from dual;

        C4
----------

 

4번째 SQL을 통하여 DB2에서는 “공백 값"이 숫자로 캐스팅되지 못하고 SQL0420N 오류가 발생했다.

 

오라클 호환성 (db2set DB2_COMPATIBILITY_VECTOR=20) 을 적용해도 “공백"값은 숫자형으로 형 변환되지 못한다.

 

이 경우 오라클처럼 “공백" 값을  숫자로 형변환 되도록 하기 위해서는 db2set DB2_COMPATIBILITY_VECTOR=20 를 적용하고 DB 재생성을 해야 된다.

$> db2set -all
[i] DB2_COMPATIBILITY_VECTOR=20

$> db2 drop db sample
$> db2stop
$> db2start
$> db2sampl
$> db2 connect to sample
$> db2 "values (bigint(''))"

1
--------------------
                   -

 

위 문제는 사전 테스트를 통하여 “공백"처리 문제가 발생함을 확인하는 경우 VARCHAR2 호환성을 적용되어야 할 것이다.

그렇지 않다면 SQL 수정이 되어야 할 것이다.

 

다른 관점에서는 “해당 DB가 오라클 호환성을 적용하고 생성된 DB인지” 확인하는 방법이 될 수도 있다.

예를 들어 DB2 구성 작업에 많은 수의 인스턴스 및 DB 생성 작업 시,  “오라클 호환성"을 적용하고 DB가 만들어져야 되는 상황이라면

오라클 호환성이 제대로 적용되어 생성된 것인지 확인하고 싶을 수 있다.

- 많은 반복 작업으로 작업 검증 절차를 빠뜨리거나

- 인스턴스 구성 후 자동으로 인스턴스가 실행 중인 상황에서, 호환성 적용하고 (인스턴스 재시작 없이) DB를 생성된 것은 아닌지

- 이미 구축된 DB2 환경에 (히스토리 없이 지원을 하게 되어) 오라클 호환성 관련된 문제가 발생한 경우 호환성 적용 시점이 언제인지 확인할 때

요즘의 개발 추세는 java 기반으로 가고 있지만, 과거 시스템에는 Embedded SQL로 개발되어진 시스템들이 많다.

특히 OS390, AS400 등의 host 시스템에서는 여러 류의 ESQL 프로그램들이 사용되는 것 같다.

 

ESQL 이 DB와 연동하기 위해서는 테이블에 대한 정의서(DDL)와 개발 언어로 변환된 변수 선언부가 필요해진다.

수많은 테이블에 대해서 테이블의 데이터 유형을 개발 언어에 맞게 변환을 수작업으로 한다면 (단순한 일에) 많은 시간이 소요될 것이다.

 

그래서 DBMS마다 테이블에 대한 유형을 개발 언어에 맞게 변환해주는 유틸리티를 제공하는 것 같다.

DB2 for LUW에서는 db2dclgn 이라는 유틸리티를 제공한다.

db2dclgn를 수행하여 테이블의 데이터 유형들이 개발언어에 맞는 유형들로 변환된 결과물을 얻을 수 있다.

(내가 정보를 덜 찾아본 것일 수 있겠지만) 호스트 시스템에서 제공하는 유틸리티보다는 효용성이 떨어지는 것 같다.

테이블에 대한 정의서는 출력을 해 주지 않기 때문이다.

 

범용 DB2 (DB2 for LUW)에서는 db2dclgn과 db2look을 같이 사용해야 개발자들에게 필요한 “선언부 생성물”이 만들어 질 듯 하다.

 

(명령어 예)
db2dclgn -d 디비명 -t 테이블명 -ㅣ COBOL  -o 출력파일.cbl -u 접속계정 -p 비밀번호

 

(테스트)
db2dclgn -d sample -t employee -b lob -l cobol -n PFIX_ -c -s employee

vi employee.cbl

01 EMPLOYEE.
  05 PFIX-EMPNO PIC X(6).
  05 PFIX-FIRSTNME.
    49 LEN PIC S9(4) COMP-5.
    49 DAT PIC X(12).
  05 PFIX-MIDINIT PIC X(1).
  05 PFIX-LASTNAME.
    49 LEN PIC S9(4) COMP-5.
    49 DAT PIC X(15).
  05 PFIX-WORKDEPT PIC X(3).
  05 PFIX-PHONENO PIC X(4).
  05 PFIX-HIREDATE PIC X(10).
  05 PFIX-JOB PIC X(8).
  05 PFIX-EDLEVEL PIC S9(4) COMP-5.
  05 PFIX-SEX PIC X(1).
  05 PFIX-BIRTHDATE PIC X(10).
  05 PFIX-SALARY PIC S9(7)V9(2) COMP-3.
  05 PFIX-BONUS PIC S9(7)V9(2) COMP-3.
  05 PFIX-COMM PIC S9(7)V9(2) COMP-3.

 

고객사 지원 시 단순하게 사용했었는데, –n (접두사), –c(접미사) 를 사용하면 개발자들에게 그나마 편할 듯 하다.

접두사는 “테이블의 스키마 명" 지정하고 접미사는 –c 옵션을 주면 컬럼명을 사용하게 된다.

-s 옵션은 “구조체 명"으로 출력파일의 첫 줄을 정의하는 옵션이다.

 

스키마 이름이 다른 동명 테이블의 dcl를 추출하는 경우, 접두사(-n) 와 구조체 명(-s) 를 통하여 구분을 지을 수 있고,

-c 옵션으로 컬럼명을 “구조체의 필드명(변수명)"으로 사용하여 테이블 컬럼 정보를 참조할 수 있을 것 같다.

 

개발지원 시, 더 테스트를 했으면 개발자 분들이 조금은 더 편하게 작업을 할 수 있도록 도움을 드렸을텐데, 지금에 와서 시간을 두고 옵션들을 테스트해 보니 어떻게 db2dclgn을 사용해야 할 지가 보인다. (정보센터에서 다양한 예제가 있었으면 도움이 되었을 텐데, 그렇지 못함을 탓해 본다.)

이전에 db2 jdbc driver 버전에 대해 기술한 적이 있다.

JAVA 6 (JDK 1.6) 버전 관점에서 설명을 하고 버전 확인하는 법들을 설명했는데, Driver 버전이 4인 경우와 아닌 경우에 따른JAVA 연동 시의 문제점을 최근에 알게 되었다.

 

문제점

- JDBC 4.0 (db2jcc4.jar) 으로 DB2와 연동하는 경우 SELECT문의 “컬럼 별명"이 반영되지 않는(무시되는) 현상

 

원인

- jdbc 4.0 에서는 ResultSetMetaData.getColumnName 과 ResultSetMetaData.getColumnLabel 이 명확히 구분되어

  ResultSetMetaData.getColumnLabel 를 사용하지 않는 이상 “컬럼 명"으로 결과 값들이 반환됨

 

정리

JDBC & SQLJ getColumnName getColumnLabel
버전 4 이전 별명 반환 별명 반환
버전 4 이상 컬럼 명 반환 별명 반환

 

DB2 V9.5 정보센터를 보면 Application Developer 변경 사항에 이에 대한 언급이 설명되어 있다.

http://publib.boulder.ibm.com/infocenter/db2luw/v9r5/index.jsp?topic=%2Fcom.ibm.db2.luw.wn.doc%2Fdoc%2Fi0052606.html

 

DB2 V9.7 에서도 이에 대해 예제를 들어 설명해 놓았다. (오타가 있어 감안해서 봐야 될 것 같다)

http://pic.dhe.ibm.com/infocenter/db2luw/v9r7/index.jsp?topic=%2Fcom.ibm.db2.luw.apdv.java.doc%2Fsrc%2Ftpc%2Fimjcc_c0052593.html

 

두 버전의 호환 사용방안

(방법1) useJDBC4ColumnNameAndLabelSemantics Connection  설정

(방법2) DataSource property to DB2BaseDataSource.NO (2) 설정

고객사의 connect by 관련 지원하다 C 소스 코드를 컴파일 해보는 작업을 잠시 해 보게 되었다.

윈도우 환경에서 사용되어 C 컴파일러가 필요하여 지인을 통해 무료 컴파일러인 MinGW 다운받아 설치를 했다.

그러나 bldrtn 을 이용해서 컴파일은 되지 않았다. (cl.exe 오류)

왜 오류가 날까? bldrtn을 열어보니 기본 컴파일러로 Visual Studio C++ 를 사용하도록 BLDCOMP 변수에 박여 있었다.

즉 Visual Studio를 설치해야 bldrtn을 구동할 수 있게 셋팅되어 있던 것…

MinGW 및 기타 gcc 들을 설치해 봐야 “어떤 값"을 넣어 “기본 컴파일러" 설정해야 하는지 알 수 없기에 Trial version으로 VS를 설치하였다.

 

물론 VC++이 설치되어 있다고 bldrtn이 실행되지 않는다.

환경변수 설정이 필요하다. (내 컴퓨터 > 속성 > 고급 시스템 설정 > 고급 탭 > 환경 변수)

 

1) INCLUDE 변수에 경로 추가  (stdio.h 에러)

    C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include

2) CLASSPATH, LIB 변수에 경로 추가

    C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\lib    (msvcrt.lib 에러)

    C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib   (kernel132.lib 에러)

    SDK가 설치 되지 않은 경우 kernel132.lib 에러가 발생하므로 Visual Studio 업데이트 파일 실행 후 kernel132.lib 라이브러리 확인 필요

 

이후 bldrtn 이 정상적으로 수행되었다.

 

C:\Program Files\IBM\SQLLIB\samples\c>bldrtn connect_by
Microsoft (R) C/C++ Optimizing Compiler Version 17.00.51106.1 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

connect_by.c
Microsoft (R) Incremental Linker Version 11.00.51106.1
Copyright (C) Microsoft Corporation.  All rights reserved.

connect_by.def(2) : warning LNK4017: DESCRIPTION statement not supported for the
target platform; ignored
   Creating library connect_by.lib and object connect_by.exp

        1개 파일이 복사되었습니다.

 

오라클의 connect by 구문은 DB2 V9.7 이상부터 지원되고 있다.

(V9.5에서도 오라클 호환성 변수가 적용되어 지원은 된다.)

 

표준 recursive SQL 인 Common table 을 이용해서는 “위계/계층"의 순서를 살릴 수가 없다.

(common 테이블 결과는 위계/계층별로 정렬되어 보여 줌)

성능 이슈가 제기되어 DB2 의 connect by의 대안으로 ibm developer works에 소개된 c로 만든 connect by였다.

(참고: http://www.ibm.com/developerworks/data/library/techarticle/dm-0510rielau/ )

 

connect by 대상 테이블에 다른 테이블이 조인되어 사용되는 경우 siblings by 처리 시 미묘하게 값 정렬이 틀려진다.

성능은 좋았지만 적용할 수가 없었다.

 

현재까지 DB2에서 제공되는 connect by 는  self join 방식에서는 성능 문제가 없지만, 다른 테이블과 join 되는 경우 성능이 많이 느려지는 느낌을 받았고, self join 이여도 siblings by 를 추가하는 경우 느려지는 느낌을 받았다.

 

다른 테이블과의 join이 발생하는 경우,  connect by 처리를 inline view(subquery)로 처리하여 되도록 self join 만 하도록 해야 성능 저하를 막을 수 있을 것으로 보인다.

DB V10.5 케플러가 출시된 올해, V9.7 코브라는 이제 오래된 제품처럼 여겨진다.

얘기하고자 하는 내용은 V9.7 때 있었기 때문에 지금은 달라졌을 수도 있다고 생각한다.

 

샘플 데이터

db2 +p –tv << EOF
create table t1 (c1 int);
insert into t1 values (1),(2);
select * from t1;

EOF

 

테스트 SQL

select c1, case when c1=1 then '1234'

            else '12' end c1_string,

            length( case when c1=1 then '1234' else '12' end) c1_length

from t1

 

(Case 1) 오라클 호환성 없는 환경

C1       C1_STRING        C1_LENGTH
---       -------------       ---------------
1          1234                4
2          12                  
2

 

(Case 2) 오라클 호환성 켜지 않고 생성된 DB에 ORA값을 적용한 경우

C1 C1_STRING C1_LENGTH
--- ------------- ---------------
1    1234          4
2    12             4

 

위 2개의 사례로 보면 “오라클 호환성”을 적용함으로서 데이터 길이가 틀리게 인식됨을 확인할 수 있다.

 

IBM Lab에서는 다음과 같이 정리해서 답변을 주었다.

case1) ORA mode + DB2_COMPATIBILITY_VECTOR=

case2) ORA mode + DB2_COMPATIBILITY_VECTOR=ORA

case3) Non ORA mode + DB2_COMPATIBILITY_VECTOR=

C1 C1_STRING C1_LENGTH
--- ------------- ---------------
1   1234          4
2   12            
2

 

case4) Non ORA mode + DB2_COMPATIBILITY_VECTOR=ORA

C1 C1_STRING C1_LENGTH
--- ------------- ---------------
1    1234          4
2    12            
4

 

ORA mode 는 “호환성 변수를 적용하고 DB 생성”함을 의미한다.

 

결론은 “호환성 모드가 아닌 DB에 ORA 값을 적용한 경우, 문자열이 CHAR로 변환”이 된다는 점이다.

즉  출력되어야 문자열 중 “가장 긴 길이를 기준”으로 문자열 길이가 정해져서, 짧은 문자열이 출력되어도 패딩처리되어 문자열 길이는 동일하게 처리되는 것이다. (varchar로 인식이 되었다면 이런 문제는 발생하지 않았을 것이다.)

 

Trace 분석 결과 (0x20은 ASCII에서 공백문자를 의미함)

10509 data DB2 UDB runtime interpreter sqlri_trace_bno_zvals fnc (3.3.112.1463.0.0)

pid 3436680 tid 2829 cpid 2711578 node 0 sec 4 nsec 200418531 probe 0

bytes 17

Data1 (PD_TYPE_DEFAULT,12) Hexdump:

6161 6161 6161 2020 2020 2020 aaaaaa

 

개인적으로 오라클 호환성 변수 사용을 권장하지는 않지만, 사용을 해야 되는 경우 F 정도까지는 무난하지 않나 싶다.

오라클 호환성 옵션 사용 시 검토할 기술문서를 소개하고 마무리 해 본다.

 

https://www.ibm.com/developerworks/mydeveloperworks/blogs/SQLTips4DB2LUW/entry/about_the_db2_compatibility_vector_and_what_not_to_say_at_the_dinner_table282?lang=en

국가에서 정책적으로 보안 기능을 요구하면서 많은 고객사들이 데이터 보안 프로젝트를 진행하고 있다.

지원하고 있는 한 고객사에서도 보안 기능을 적용하기 위하여 보안 컨설팅을 받은 결과 DB2의 보안 취약점 중 하나로 DB2 서버와 클라이언트 간의 통신 시 사용자 정보의 암호화 되지 않는 점이 지적사항으로 도출되었다.

 

DB2 인스턴스 구성 시 authentication의 기본 값은 SERVER 로 설정된다. 이것은 인증처리가 DB2 서버 쪽에서 처리됨을 의미한다.

정보센터에서 authentication의 값에 대해 찾아 보면 SERVER_ENCRYPT 값은 통신 시 사용자 ID와 비밀번호가 암호화되어 되어진다고 기술되어 있다. 이외에도 GSSPLUGIN 등을 사용한 암호화 방식도 있지만, 개인적으로 DB2내에서 통신 패킷의 사용자 정보 암호화는 SERVER_ENCRYPT 값으로 해도 충분하지 않나 싶다.

 

SERVER_ENCRYPT로 설정한 후 DB2 client를 통해 접속 테스트를 해 본바, 별다른 특이 사항이 발생하지는 않았다.

차후 DB2 설치 작업 시, 보안 측면에서 SERVER_ENCRYPT 값으로 authentication 구성변수를 설정하는 것을 고려해 볼 필요가 있겠다.

Informix에서는 CLR (continuous log restore) 를 통하여 원격지 서버에 복제 서버 구성이 가능한 것으로 알고 있다.

DB2에서는 (내가 모르고 있는 것인지 모르지만) CLR에 해당하는 기능은 없어 보인다.

HADR 설정을 통하여 DB 동기화는 가능하지만, Flash Copy 및 디스크 복제 기반의 이중화 환경에서는 실시간으로 Disk 복제를 하지 않는 이상 불가능하다.

 

한번은 고객사쪽에서 아카이브 로그만 복제 서버로 옮겨서 복구를 계속 할 수 있는지를 문의해 왔다.

즉 rollforward 작업을 한번이 아닌, 여러 번 수행 가능한지 확인이 필요한 문제였다.

아카이브 로그를 원격지 복제서버에 수동으로 옮겨주어야 하는 불편함은 있지만, 이 부분은 제외를 하고 다음과 같이 테스트를 해 보았다.

 

1. 테스트 시나리오

순서

TEST(운영 서버)

TEST1 (복제서버)

1

TEST DB 생성 및 archive log 적용

2

T1 테이블 생성

3

온라인 DB full 백업

4

TEST 온라인 백업을 TEST1 이름으로 Restore

5

T1 에 값 입력(1) 후 로그 스위치

6

아카이브 로그 복사 후 rollforward

7

T1 에 값 입력(2) 후 로그 스위치

8

아카이브 로그 복사 후 rollforward

9

Rollforward 완료 및 데이터 조회

10

데이터 조회

 

2. 테스트

 

TEST(운영 서버)

TEST1 (복제서버)

1

db2 create db test on /database

db2 update db cfg for test using logarchmeth1 disk:/database/archivelog

db2 backup db test to /dev/null

2

db2 connect to test

db2 "create table t1 (c1 int, c2 timestamp)"

3

db2 backup db test online to /database/backup

4

db2 "restore db test from /database/backup into test1"

db2 "rollforward database test1 query status"

Rollforward Status

Input database alias = test

Number of members have returned status = 1

Member ID = 0

Rollforward status = DB pending

Next log file to be read = S0000001.LOG

Log files processed = -

Last committed transaction = 2013-03-20-06.18.12.000000 UTC

5

db2 "insert into t1 values (1,current timestamp)"

ls /database/archivelog/fed10/TEST/NODE0000/

LOGSTREAM0000/C0000000

S0000000.LOG S0000001.LOG S0000002.LOG S0000003.LOG

 ls /database/archivelog/fed10/TEST/NODE0000/

LOGSTREAM0000/C0000000

S0000000.LOG S0000001.LOG S0000002.LOG S0000003.LOG S0000004.LOG S0000005.LOG

 

6

cp /database/archivelog/fed10/TEST/NODE0000/

LOGSTREAM0000/C0000000/* .

db2 "rollforward db test1 to end of logs"

Rollforward Status

Input database alias = test1

Number of members have returned status = 1

Member ID = 0

Rollforward status = DB working

Next log file to be read = S0000006.LOG

Log files processed = S0000001.LOG - S0000005.LOG

Last committed transaction = 2013-03-20-06.21.14.000000 UTC

7

db2 "insert into t1 values (2,current timestamp)"

db2 archive log for db test

S0000000.LOG S0000001.LOG S0000002.LOG S0000003.LOG S0000004.LOG S0000005.LOG S0000006.LOG

8

cp TEST/NODE0000/LOGSTREAM0000/

C0000000/S0000006.LOG .

db2 "rollforward db test1 to end of logs"

Rollforward Status

Input database alias = test1

Number of members have returned status = 1

Member ID = 0

Rollforward status = DB working

Next log file to be read = S0000007.LOG

Log files processed = S0000001.LOG - S0000006.LOG

Last committed transaction = 2013-03-20-06.26.44.000000 UTC

9

db2 "rollforward db test1 complete"

Rollforward Status

Input database alias = test1

Number of members have returned status = 1

Member ID = 0

Rollforward status = not pending

Next log file to be read =

Log files processed = S0000001.LOG - S0000006.LOG

Last committed transaction = 2013-03-20-06.26.44.000000 UTC

 

db2 connect to test1

db2 "select * from t1"

C1 C2

----------- --------------------------

1 2013-03-20-15.21.14.698452

2 2013-03-20-15.26.44.622604

2 record(s) selected

10

db2 connect to test

db2 "select * from t1"

C1 C2

----------- --------------------------

1 2013-03-20-15.21.14.698452

2 2013-03-20-15.26.44.622604

2 record(s) selected

 

 

“데이터 무결성”을 확인하는 방법으로 DB2에서 제공하는 툴들이다. db2dart는 DB가 비활성화(offline)일 때 사용 가능하고, db2 inspace는 DB가 활성화된 상태에서 사용된다는 점이 사용 상 차이점일 듯 하다.

 

두 개의 툴이 사용되는 경우는 DB가 활성화되지 않는 상황이거나, 진단로그(db2diag.log)에 bad page 오류가 발생할 때 사용이 될 뿐 보통의 경우에는 사용하지 않는다. 필자의 경우도 많이 사용해 본 도구들은 아니다.

 

데이터 무결성 확인이외에도 두 개의 도구는 기능상 약간의 차이점이 있다.

(1) db2dart

    - 데이터 추출 기능

    - 테이블 공간의 고수위 마크(HWM)를 떨어뜨리는 기능

    - 유효하지 않는 색인(index) 고치기 기능

 

(2) db2 inspect

    - 압축된 테이블의 “압축으로 인한 스토리지 절감률” 확인

 

 

개인적으로는 DB가 접속되지 않는 상황에서 “테이블공간 목록과 콘테이너 정보”를 확인하고 싶었던 경우가 있다.

(경로재지정 복구를 하고자 함 이였는지, 특정 테이블공간에 대한 정보를 통하여 DB rebuild 복원을 하려고 했었던 것인지는 기억나지 않지만..)

 

이런 경우 db2dart 의 옵션 /ATSC 나 /DTSF 를 사용하면 “스토리지 그룹 정보”, “테이블 공간 정보”, “콘테이너 정보”들을 확인할 수 있다.

 

사용 예 (두 도구의 실행 결과는 db2diag.log가 있는 위치에 결과 파일이 생성된다)

$> db2dart sample /DTSF

Action option: DTSF

Connecting to Buffer Pool Services...

      Storage group file (automatic storage) report phase start.

      Header version:            0
      Header flavour:            1
      Number of storage groups:  1
      Checksum:                  0x0b050154

      Storage group ID:          0
      Storage group name:        DBSTORAGE
      Flavour:                   3
      Version:                   0
      State flags:               0x0000000000000000
      Last LSN:                  0x0000000000000000
      Initial LSN:               0x0000000000000000
      Next path ID:              1
      Checksum:                  0x193a7a22

      Number of storage paths:   1
      Storage path # 0:          /db2/instance/db2inst (id = 0, state = 0x0)

      Storage group file (automatic storage) report phase end.


      Tablespace file report phase start.
      Tablespace information for current database:
      --------------------------------------------


      Number of defined tablespaces:  8
      High water mark of used pools:  7
      Number of disabled tablespaces: 0

      Individual tablespace details:
      -------------------------------

      Information for Tablespace ID: 0

      (생략)

 

$> db2dart sample /LHWM
2 48   (tablespace ID 및 페이지 크기 지정)

The requested DB2DART processing has completed successfully!
Complete DB2DART report found in:/db2/instance/db2inst/sqllib/db2dump/DART0000/SAMPLE.RPT

High water mark:  1952 pages, 61 extents (extents #0 - 60)


Lower high water mark processing - phase start.
     Current high water mark:                    60
     Desired high water mark:                    1
     Number of used extents in tablespace:       57
     Number of free extents below original HWM:  4
     Number of free extents below desired HWM:   0
     Number of free extents below current HWM:   4


Final high water mark:  Extent #56 (57 extents, 1824 pages).

** This cannot be lowered further as there are not enough free extents
   to move the object holding the high water mark.

 

$> db2 "inspect check database results keep db.inspect"
$> cd ~/sqllib/db2dump
$> db2inspf db.inspect db.db2infp -e -s –w

 

DB에 bad page 오류가 발생을 하는 경우 진단로그에 tablespace id 및 object id 들이 기록이 된다.

db가 비활성상태라면 db2dart를 통하여 tablespace id 및 object id를 지정하여 결과 파일을 통하여 page 오류를 확인할 수 있을 것이고,

db가 온라인 상태라면 db2 inspect 를 통하여 “데이터 무결성’을 점검할 수 있을 것 같다.

 

본 도구들을 많이 사용해 봤다면, 특정 주제를 가지고 설명을 했을텐데 둘러보기 식으로 해당 도구에 대한 소개를 마친다.

+ Recent posts