OCILib를 사용하는 경우 필수 파일

* instantclient-basic-windows.x64-12.1.0.2.0.zip
    - oci.dll
    - oraociei12.dll
    - oraons.dll

* msvcr100.dll

* ocilibw.dll


오라클 테이블의 레코드 사이즈 계산을 쉽게하자

오라클 DB 테이블의 레코드 사이즈 계산을 할 필요가 있었다.

구할려고 보니, 데이터 형별로 계산 공식이 틀리다.


쉽게 계산하기 위해 HTML 로 작성해봤다.

사용방법은 DB테이블 Description의 레코드별 사이즈 부분을 TextArea에 붙여 넣고 실행하면 된다.


주요 소스 내용은 아래와 같다.


<head>
	<title>테이블 레코드 계산</title>
	<script src="http://code.jquery.com/jquery-1.5.min.js" type="text/javascript" 
                      charset="utf-8"></script>
    <script>
        /* 계산 공식 출처 
        	http://epoonet.egloos.com/4479971
        */
    	var pTable = [
    		{ type: "char", 	calc:function(n){ return n; }},
    		{ type: "varchar2", calc:function(n){ return n; }},
    		{ type: "nchar",	calc:function(n){ return 2 * n;  }},
    		{ type: "nvarchar2",calc:function(n){ return 2 * n; }},
    		{ type: "number",   calc:function(n){ return 1+ Math.ceil(n/2); }},
    		{ type: "date",		calc:function(n){ return 7; }},
    		{ type: "raw",		calc:function(n){ return n; }},
    		{ type: "long",		calc:function(n){ return n; }},
    		{ type: "long raw", calc:function(n){ return n; }},
    		{ type: "bfile", 	calc:function(n){ return 530; }},
    		{ type: "rowid", 	calc:function(n){ return 6; }},
    		{ type: "timestamp",calc:function(n){ return 13; }}
    	];
    	
        $(document).ready(
            function(){
            $("#DoIt").click(function() {
				var resultText = "";
				var inputText = $("textarea#src").val().toLowerCase().split("\n");
				var lineString;
				var size =0, sum =0;
				var buf;
				
				for(var i=0; i<inputText.length; i++) {
				
					size = 0;
				
					if ( lineString=$.trim(inputText[i]) ) {
						for(var j=0; j<pTable.length; j++) {
							if ( lineString.substr(0, pTable[j].type.length) 
                                                              == pTable[j].type ) {
								try {
								   size = Number(lineString.split("(")[1].split(")")[0]);
								} catch (e) { 
								   size = 0;
								}

								size = pTable[j].calc(size);
								break;
							}
						}
					}
					
					sum += size;
					resultText += lineString + " : " + size + "<br/>";
					
				}
				
				resultText += "<br/><b>sum: " + sum + "</b>";
				$("#result").html( resultText );
				
            });
        });
    </script>
	<style type="text/css">
        * { font-size:12px; font-family:맑은고딕 }
    </style>
</head>


<body>
<textarea id="src" rows="20" cols="80"></textarea>
<br/>
<input type="button" value="Do It!" id="DoIt">
<br/>
<br/>
<span id="result"></span>


직접 실행을 위한 URL은 아래와 같다.

http://host.newtype.pe.kr/tools/ora_table_size.htm 

오라클 Plan 정보 보는 방법

보통 set autotrace on 명령으로 plan 정보를 보는데, 회사의 DBA가 추천한 방법이 있어서 기록을 남긴다.

 

1. 사용자 계정으로 DB 접속 ( 시스템 계정의 경우 불필요한 쿼리까지 포함됨 )

$ sqlplus system/

 

2. 쿼리 실행

SQL> alter session set sql_trace = true;

SQL> 쿼리실행 ( 복수의 쿼리 가능 )

SQL> alter session set sql_trace = false;

          ( udump 생성을 위해 필수 )

 

3. 결과 확인

$ cd $ORACLE_BASE

$ cd ./admin/DB명/udump

$ ls ltr

     ( 가장 최근 파일 확인 )

$ tkprof 파일명.trc tt.txt sys=no;

     ( Trace 파일을 사람이 해석 가능 하도록 변환 )

$ cat tt.txt



오라클에서 조회한 결과를 Text 파일로 저장하기

SELECT 결과를 CSV 포멧으로 저장 하려면 아래와 같이 처리 한다.


sqlplus에 로긴 하여 위와 같이 입력하면, output.lst 파일에 원하는 내용이 저장된다.

다른 방법으로 파이프를 이용하는 방법이 있다.


쉘에서 이렇게 입력을 하면, 원하는 결과를 얻을 수 있다.

[출처]

오라클에서 다른 계정으로 데이터 마이그레이션 방법

오라클에서 다른 계정의 테이블을 Import 해오고자 한다.
테이블 이름만 동일하고 계정 및 테이블 스페이스가 다른 경우의 Import 과정이다.

oraid 계정에서 devdb 계정으로 Import 하는 과정을 설명한다.

1. Table Space 생성
SQL> CREATE TABLESPACE TS_DEV_DATA
DATAFILE '/home/dba/oradata/ts_dev_data.dbf' SIZE 2048M
AUTOEXTEND OFF;

SQL> CREATE TABLESPACE TS_DEV_IDX
DATAFILE '/home/dba/oradata/ts_dev_idx.dbf' SIZE 1024M
AUTOEXTEND OFF;

Data를 위한 TS_DEV_DATA아 Index를 위한 TS_DEV_IDX 를 생성 했다.
개발 목적의 테이블 스페이스 이므로 용량 자동 증가는 꺼두었다.

2. devdb 계정 생성 
SQL> CREATE USER devdb IDENTIFIED BY devdb
DEFAULT TABLESPACE TS_DEV_DATA
TEMPORARY TABLESPACE TEMP
QUOTA UNLIMITED ON TS_DEV_DATA;

SQL> GRANT CONNECT,RESOURCE TO FRDEV;

계정을 생성하고, 필요한 권한을 부여 했다.

3. Export
$ exp oraid/orapwd file=tbl_exp_20100201.dmp tables="tbl_test1, tbl_test2, tbl_test3" ignore=y

Source DB에 접속 하여 필요한 테이블을 Export 한다.

4. Table 생성
SQL> CREATE TABLE TBL_TEST1 
(
SEQ              NUMBER(10) NOT NULL,
CODE             VARCHAR2(10) NOT NULL,
DATA             VARCHAR2(20) NOT NULL
CONSTRAINT PK_TEST1
    PRIMARY KEY (SEQ) USING INDEX 
    TABLESPACE TS_DEV_IDX
)
TABLESPACE TS_DEV_DATA;

SQL> CREATE TABLE TBL_TEST2
 - 생략 - 
SQL> CREATE TABLE TBL_TEST3
 - 생략 - 

Export를 하게 되면 테이블 생성 정보까지 포함된다. 
그대로 Import 하게 되면, 새로 생성한 테이블 스페이스에 저장되는 것이 아니고, 
기존 테이블 스페이스에 저장 되므로 원하는 공간에 미리 테이블을 생성 해 둔다.

5. Import
$ imp oraid/orapwd fromuser=oraid touser=devdb file=tbl_user.dmp tables=tbl_user ignore=y

Import 받고자 하는 devdb 계정을 touser 파라미터로 지정한다.
테이블 생성시 발생하는 오류를 건너 뛰기 위해 ignore 파라미터를 추가한다.




Oracle Bulk Loader

Oracle DB의 특정 테이블에 대용량의 데이터를 Insert 하고 싶을때,
Oracle Bulk Loader를 사용한다.

Loader를 사용하기 위해서는 컨트롤 파일과, 데이터 파일이 필요하다.

컨트롤 파일은 데이터 파일 정보와 테이블 정보를 가지고 있고,
데이터 파일은 DB에 넣고자 하는 데이터 목록을 가지고 있다.

1. 테이블 생성
CREATE TABLE TEST_TBL (
    NAME CHAR(20),
    SCORE NUM(5),
    UPDATE DATE );

2. 컨트롤 파일 생성
$ cat test.ctl
LOAD DATA
INFILE test.dat
INTO TABLE TEST_TBL
FIELDS TERMINATED BY ','
(NAME, SCORE, UPDATE SYSDATE)
3. 데이터 파일 생성
$ head test.dat
USER1,70
USER2,80
USER3,90
USER4,100

4. 로더 실행
$ sqlldr tiger control=test.ctl log=test.log
 * Oracle 계정명은 tiger

보다 상세한 사용법은 아래 링크를 참고 하세요
http://infolab.stanford.edu/~ullman/fcdb/oracle/or-load.html
http://www.orafaq.com/wiki/SQL*Loader_FAQ



Oracle import / export


Export

 * 사용자 지정
$ exp oracle_id/oracle_pwd file='dump_filename.dmp' owner=oracle_id compress=y log=log_filename.log

 * 테이블 지정
$ exp oracle_id/oracle_pwd file='dump_filename.dmp' tables=table_name

 * 테이블 조건 지정
$ exp oracle_id/oracle_pwd file='dump_filename.dmp' tables=table_name query=\"where seq=\'007\'\"


Import

 * 사용자 지정
$ imp oracle_id/oracle_pwd file='dump_filename.dmp' fromuser=oracle_id touser=oracle_id log=log_filename.log

 * 테이블 지정
imp oracle_id/oracle_pwd file='dump_filename.dmp' tables=table_name ignore=y

 * 테이블 조건 지정
  테이블 조건 별로 Insert를 할 수가 없다.
  ignore=y 옵션을 주면 에러가 발생해도 계속 진행한다.
   -> DB오류가 발생하면 해당 레코드는 Insert 하지 않는다. (PK가 중복등..)

Pro*c를 이용한 오라클 PL/SQL function 호출

Proc*c를 이용해 Sub Program(Procedure or Function)을 호출 하는 방법에 대해 설명한다.

자료를 찾아보면 대부분의 샘플 코드가 Procedure 로만 되어 있다.
여기서는 Function을 호출 해 보기로 한다.
사실 호출 방법은 거의 비슷 하다.

- 컴파일 방법
PL/SQL 구문을 사용하기 때문에 proc 컴파일 옵션을 주어야 한다.
그런데, 컴파일 옵션을 주면 다른 Pro*c 구문에서 컴파일이 안되는 일이 발생을 한다.
따라서, PL/SQL 구문을 사용하는 소스는 별도 .pc 파일로 때어 내어서 별도 컴파일을 해야한다.

$ proc include=../inc iname=sub.pc SQLCHECK=SEMANTICS userid=oracleid/oraclepwd

여기서 기존 컴파일 방법과 다른 부분은 "SQLCHECK=SEMANTICS" 옵션이 추가된 것과,
userid 옵션이 추가된 것이다.
userid 옵션에는 오라클 로긴을 위한 ID와 Password를 넣어 준다.


- 샘플 소스
/** @file       sub.pc
 * @brief       Remote Function Call  
 * @author    newtype
 * @date    2009-04-10
 */

#ifndef _SUB_PC_
#define _SUB_PC_

#include "common.h"

EXEC SQL INCLUDE SQLCA;

/** @brief 오라클 Function을 호출 한다.
 * @param in [IN] 오라클 Fucntion Parameter
 * @param out [OUT] 오라클 Fucntion Return 값을 반환
 * @return 0     정상처리
 * @return 그외  오라클 에러 코드
 * @remarks 참고 사항
 * @warning 주의 사항
*/
int CallOracleFunction(char* in, char* out)
{    
    EXEC SQL BEGIN DECLARE SECTION;
        char szOut[MAX_BUF] = "";
    EXEC SQL END DECLARE SECTION;

    EXEC SQL EXECUTE
    BEGIN
        :szOut := ORA_SAMPLE_FUNC( in );
        IF szOut = 'newtype' THEN
            strcpy( out, szOut) ;
            COMMIT;
        ELSE
            strcpy( out, 'newtype.pe.kr');
            ROLLBACK;
        END IF;
    END;
    END-EXEC;

    return nErrCode;
}

#endif


ORA-01002 fetch out of sequence


ORA-01002 fetch out of sequence 는 여러 경우에 발생을 한다.

그중 한가지에 대한 오류 발생 사례 이다.

DB Link로 Remote 테이블을 참조 하고 있는 프로세스가 작업을 수행하고 있는 상태에서,
Remote DB가 Shutdown 되는 경우 위 에러가 발생 한다.
Remote DB가 Start되고 새로운 세션을 맺어야 프로세스가 정상 동작을 한다.
세션을 유지한 상태에서 Remote DB가 Start 되어도 위 에러가 계속 발생한다.

말이 어렵다 테스트 해보자.

1. ORA TNS에 RemoteDB를 추가한다.

2. DB 링크를 생성한다.
CREATE PUBLIC DATABASE LINK test_link CONNECT TO remoteDBid IDENTIFIED BY remoteDBpwd USING 'remoteDB';
CREATE SYNONYM test_proc FOR test_proc_table@test_link
SELECT * FROM test_proc

3. 데이터를 삽입한다.
insert into test_proc values ( 1, 1 );
insert into test_proc values ( 2, 1 );
~~~~~~
insert into test_proc values ( 33, 1 );
insert into test_proc values ( 44, 1 );

4. 테스트 프로그램을 작성한다.
$ vi t.pc
#include <stdio.h>

EXEC SQL INCLUDE SQLCA;

#define MAX_ARRAY_SIZE 10

int main()
{
    int n;

    EXEC SQL BEGIN DECLARE SECTION;
        int  hSEQ; 
        char hOracleUser[20] = "localDBid";
        char hOraclePwd[20]  = "localDBpwd";
    EXEC SQL END DECLARE SECTION;  
    
    EXEC SQL CONNECT :hOracleUser IDENTIFIED BY :hOraclePwd;
    if(sqlca.sqlcode !=0)
    {
        printf( "login fail\n");  
        return 0;
    }
    
    for (n=0; n<10; n++)
    {
        printf( "start of job\n");  
 
        EXEC SQL DECLARE CUR_TEST_PROC CURSOR FOR  
            SELECT SEQ FROM TEST_PROC;
     
        EXEC SQL OPEN CUR_TEST_PROC;         

        while(1)
        {
            EXEC SQL FETCH CUR_TEST_PROC INTO :hSEQ;
                
            sleep(3);            
                
            if(sqlca.sqlcode != 0) 
            {
                if(sqlca.sqlcode == 1403) 
                {
                    printf( "data not found\n");
                    break;
                }
                else 
                {
                    printf( "fetch Error [%20s]\n" , sqlca.sqlerrm.sqlerrmc);
                    break;
                }
            }

            printf("fetch [%d]\n", hSEQ );
        } 
        EXEC SQL CLOSE CUR_TEST_PROC;      
        printf( "end of job\n");  
        
    } /* for */

    return 0;
}




$ proc iname=t.pc
$ cc -o t t.c -L/home/oracle/app/oracle/product/8.1.7/lib -lclntsh -R/home/oracle/app/oracle/product/8.1.7/lib

5. 프로세스 구동
start of job

6. Remote DB Shutdown

7. 에러 확인
fetch Error [ORA-01002: fetch out of sequence]


이거 원인을 파악하려고, 오늘 하루를 다 보냈다. ㅡ.ㅡ


참고 
DB 링크 조회 쿼리
SELECT * FROM USER_DB_LINKS 

시노님 조회 쿼리
SELECT * FROM TAB WHERE TABTYPE='SYNONYM'


oracle 유용한 쿼리

  • Join UPDATE

  • UPDATE AA A
    SET A.TARGET = (
        SELECT B.TARGET   
          FROM BB B
         WHERE A.PK = B.PK
    ) WHERE EXISTS ( SELECT 1 FROM BB B WHERE A.PK = B.PK );
    


  • 쿼리 시간 보기

  • set timing on;
    set timing off;
    


  • PLAN 정보 보기

  • set autot on exp
    사용자 쿼리
    set autot off
    rollback;
    


  • 현재 연결된 세션

  • select count(*) from v$session;
    


  • 세션 정보

  • select status, sid from v$session;
    


  • 사용자명 별 연결 세션

  • select USERNAME, count(1) from v$session group by USERNAME;
    


  • 케릭터셋 보기

  • select * from v$nls_parameters WHERE PARAMETER='NLS_CHARACTERSET'
    


  • 메모리에 올라가 있는 쿼리 보기

  • select * from V$SQL_SHARED_MEMORY
    


  • 메모리에 올라가 있는 쿼리 지우기
    (CPU, DISK 사용량이 증가하므로, 야간에 작업할 것)

    ALTER SYSTEM FLUSH SHARED_POOL;
    


  • 연결된 커서 갯수 및 쿼리 출력

  •    SELECT a.cnt,
              a.user_name,
              b.sql_text,
              a.hash_value
         FROM ( SELECT COUNT (*) cnt,
                       user_name,
                       hash_value
                  FROM v$open_cursor
              GROUP BY user_name,
                       hash_value) a,
              v$sqlarea b
        WHERE a.hash_value = b.hash_value AND ROWNUM <= 100
     ORDER BY cnt DESC;
    


  • 특정 테이블의 외래키 출력

  •    SELECT A.TABLE_NAME table_name
            , A.CONSTRAINT_NAME key_name
            , B.TABLE_NAME referencing_table
            , B.CONSTRAINT_NAME foreign_key_name 
            , B.STATUS fk_status  
         FROM USER_CONSTRAINTS A
            , USER_CONSTRAINTS B  
        WHERE A.CONSTRAINT_NAME = B.R_CONSTRAINT_NAME 
          AND B.CONSTRAINT_TYPE = 'R'
          AND A.OWNER = '대문자 계정명'
          AND ( A.TABLE_NAME = '대문자 테이블명' OR B.TABLE_NAME = '대문자 테이블명' )
     ORDER BY 1, 2, 3, 4;
    


  • Oracle Admin계정으로 로긴

  • $ sqlplus "/as sysdba"
    


  • Oracle 종료
  • (Oracle 계정에서)

    $ lsnrctl stop 
    $ sqlplus "/as sysdba"
    
    SHUTDOWN [NORMAL | TRANSACTIONAL | IMMEDIATE | ABORT ]
    

    NORMAL : 현재 사용자가 연결을 끊을때까지 대기 default 옵션
    TRANSACTIONAL : 접속된 사용자들이 새롭게 트랜잭션을 시작할 수 없습니다. 사용자의 트랜잭션이 끝나면 연결이 종료되면 모든 트랙잭션이 종료되면 데이터베이스가 즉시 종료 됩니다.
    IMMEDIATE : 현재 진행중인 SQL문이 완료되지 않으며 현재 사용자의 연결을 즉시 끝으며, 모든 트랜잭션을 롤백합니다.
    ABORT : 강제 종료라고 할수 있으며 다음번 데이터베이스 시작시 인스턴스 복구가 필요합니다.


  • Oracle 시작
  • (Oracle 계정에서)

    $ sqlplus "/as sysdba"
    
    STARTUP [NORMAL | NOMOUNT | MOUNT]
    exit
    
    $ lsnrctl start
    


  • Oracle 기본포트 1521 변경 방법
  • (Oracle 계정에서)

    $ lsnrctl stop
    $ sqlplus "/as sysdba"
    
    ALTER SYSTEM SET LOCAL_LISTENER = "(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1522))";
    ALTER SYSTEM REGISTER;
    exit
    
    $ lsnrctl start
    



  • Pro-C 관련 유용한 사이트

  • http://hobak.emailkorea.net/hobak/
    http://www.lsbu.ac.uk/oracle/oracle7/api/doc/PC_22/
    http://www.lsbu.ac.uk/oracle/oracle9i/appdev.920/a97269/toc.htm


  • Pro-C 관련 글

  • shared_pool 메모리 사용량
    Sql 바인딩
    하드파싱 쿼리 잡는 방법
    $sqltext 에 대해서