레이블이 DB인 게시물을 표시합니다. 모든 게시물 표시
레이블이 DB인 게시물을 표시합니다. 모든 게시물 표시

2012-10-06

DBMS/오라클] Oracle에서_Split_생성해서_사용




출처 : http://mukeabi.egloos.com/2342599



우선 Table 타입을 선언합니다.

create or replace type split_tbl as table of varchar2(32767);
/

함수를 선언합니다.

create or replace function split
(
  p_list varchar2,
  p_del varchar2
) return split_tbl pipelined
is
  l_idx    pls_integer;
  l_list    varchar2(32767) := p_list;
  l_value    varchar2(32767);
begin
  loop
       l_idx := instr(l_list,p_del);
       if l_idx > 0 then
           pipe row(substr(l_list,1,l_idx-1));
           l_list := substr(l_list,l_idx+length(p_del));
       else
           pipe row(l_list);
           exit;
       end if;
  end loop;
  return;
end split;
/

사용법은

select * from table(split('1 2 3 4 5 6 7 8 9 10', ' '));

입니다.

출처: http://www.dulihana.com/blog/126

DBMS/오라클]Oracle에서_Split_-_쿼리로_만들기



출처 : http://syronia.tistory.com/entry/%EC%98%A4%EB%9D%BC%ED%81%B4-Split-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84%ED%95%9C-Query




오라클 FUNCTION기능을 이용하는 방법도 있지만 쿼리로 구현해보았다.

구분자가 |일 경우 ------------------------

SELECT substr(wdata,
                      instr(wdata, '|', 1, LEVEL) + 1,
                      instr(wdata, '|', 1, LEVEL + 1) - instr(wdata, '|', 1, LEVEL) - 1) name
FROM (
           SELECT '|' || 'Kim|Lee|Park' || '|' wdata
           FROM DUAL
          )
CONNECT BY LEVEL <= length(wdata) - length(REPLACE(wdata, '|')) - 1


만약 구분자가 2개라면 ---------------

SELECT substr(wdata,
                      instr(wdata, '||', 1, LEVEL) + 2,
                      instr(wdata, '||', 1, LEVEL + 1) - instr(wdata, '||', 1, LEVEL) - 2) id
FROM (
           SELECT '||' || 'babo||kim||hello||home' || '||' wdata
           FROM dual
          )
CONNECT BY LEVEL <= (length(wdata) - length(REPLACE(wdata, '||')))/2 - 1

DBMS/오라클]Oracle Database 10g DBA를 위한 20가지 주요 기능




Scheduler
dbms_job 패키지의 실행 주기 설정을 수작업으로 관리하는 것이 번거로우십니까? 이제 10g데이타베이스가 제공하는 Scheduler를 이용해 보십시오.
여러분들 중 일부는 백그라운드 데이타베이스 작업 스케줄을 설정하기 위해 dbms_job 패키지를 사용하고 있을 것입니다. 하지만 필자가 알기로는 대부분의 DBA들이 dbms_job 패키지를 사용하지 않고 있습니다.

이 패키지는 PL/SQL 코드 세그먼트만을 처리할 수 있으며, 데이타베이스 외부의 운영체제 파일 또는 실행 파일 이미지를 처리할 수 없다는 기능적 한계를 갖고 있습니다. 이 때문에 DBA들은 Unix의 cron, Windows의 AT 명령 등을 사용하여 운영 체제 레벨에서 스케줄링을 설정하는 방법을 선택합니다. 또는 그래픽 사용자 인터페이스를 제공하는 써드 파티 툴을 사용하기도 합니다.

그 러나 dbms_job은 이들과 다른 장점들이 있습니다. 이 중 하나는 데이터베이스가 실행되고 있는 경우에만 활성화된다는 것입니다. 만약 데이터베이스가 다운되어 있다면 해당 Job들은 실행되지 않습니다. 데이터베이스 외부에 존재하는 툴들은 수작업을 통해 데이터베이스가 실행되고 있는지 조사되어야 하며, 복잡한 작업일 수 있습니다. Dbms_job의 다른 장점으론 데이터베이스 내부에 존재한다는 것입니다. 그러므로 SQL*Plus와 같은 유틸리티를 이용해 쉽게 접근 가능하다는 것입니다.

Oracle Database 10g Scheduler는 모든 종류의 작업을 지원하는 내장형 작업 스케줄러 유틸리티를 제공합니다. 10g Scheduler의 가장 큰 장점은, 데이타베이스에 포함된 형태로 제공되므로 추가적인 비용이 들지 않는다는 것입니다. 이번 연재에서는 Scheduler의 기능에 대해 자세히 살펴보기로 합니다.
Creating Jobs Without Programs
개념의 이해를 돕기 위해 예를 통해 설명하겠습니다. 아카이브 로그 파일을 다른 파일시스템으로 이동하기 위해 아래와 같은 이름의 셸 스크립트를 생성했다고 가정해 봅시다:
/home/arup/dbtools/move_arcs.sh
별도의 프로그램을 생성하지 않고도 작업 유형으로 “Executable”을 지정하고 스케줄링을 설정할 수 있습니다.

 

begin

   dbms_scheduler.create_job

   (

      job_name      => 'ARC_MOVE_2',

      schedule_name => 'EVERY_30_MINS',

      job_type      => 'EXECUTABLE',

      job_action    => '/home/arup/dbtools/move_arcs.sh',

      enabled       => true,

      comments      => 'Move Archived Logs to a Different Directory'

   );

end;

/
또 Schedule Name을 설정하지 않고 작업을 생성하는 것도 가능합니다.

 

begin

   dbms_scheduler.create_job

   (

      job_name        => 'ARC_MOVE_3',

      job_type        => 'EXECUTABLE',

      job_action      => '/home/arup/dbtools/move_arcs.sh',

      repeat_interval => 'FREQ=MINUTELY; INTERVAL=30',

      enabled         => true,

      comments        => 'Move Archived Logs to a Different Directory'

   );

end;

/
앞의 두 가지 예를 통해 dbms_job과 비교했을 때의 Scheduler의 장점을 확인할 수 있습니다. Scheduler를 이용하면 PL/SQL 프로그램뿐 아니라 OS 유틸리티와 프로그램을 실행하는 것이 가능합니다. 이러한 기능을 활용하여 범용적인 데이타베이스 작업 관리 환경을 구현할 수 있습니다. 또 Scheduler는 자연 언어(natural language)를 사용하여 실행 주기를 정의할 수 있다는 매우 중요한 이점을 제공합니다. 스케줄을 매 30분 단위로 실행하고자 하는 경우, (PL/SQL 문법 대신) 자연 언어 형식의 표현을 사용하여 REPEAT_INTERVAL 매개변수를 아래와 같이 정의할 수 있습니다:
'FREQ=MINUTELY; INTERVAL=30'
좀 더 복잡한 예를 들어 설명해 보겠습니다. 운영 중인 애플리케이션이 오전 7시에서 오후 3시까지의 시간대에 집중적으로 사용된다고 가정해 봅시다. Statspack을 실행하면 월요일~금요일 오전 7시 ~ 오후 3시의 시스템 통계를 수집할 수 있습니다. DBMS_JOB.SUBMIT을 사용하여 작업을 생성하는 경우라면, NEXT_DATE 매개변수는 아래와 같이 정의됩니다:

 

DECODE

(

   SIGN

   (

      15 - TO_CHAR(SYSDATE,'HH24')

   ), 

   1,

      TRUNC(SYSDATE)+15/24,

   TRUNC

   (

      SYSDATE +

      DECODE

      ( 

          TO_CHAR(SYSDATE,'D'), 6, 3, 1

      )

    )

    +7/24

)
위의 코드가 이해하기 쉽습니까? 전혀 그렇지 않습니다.

이번에는 DBMS_SCHEDULER를 이용하는 방법을 살펴 보겠습니다. REPEAT_INTERVAL 매개변수는 아래와 같이 간단하게 정의됩니다:
'FREQ=DAILY; BYDAY=MON,TUE,WED,THU,FRI; BYHOUR=7,15'
이 매개변수를 이용해 실행 주기를 다양한 형태로 설정할 수 있습니다. 몇 가지 예가 아래와 같습니다:
● 매월 마지막 일요일:
FREQ=MONTHLY; BYDAY=-1SUN

● 매월 세 번째 금요일:
FREQ=MONTHLY; BYDAY=3FRI

● 매월 뒤에서 두 번째 금요일
FREQ=MONTHLY; BYDAY=-2FRI
마이너스(-) 기호는 숫자가 뒤에서부터 계산된다는 의미입니다.

설정된 실행 주기가 올바른지 확인하려면 어떻게 해야 할까요? 캘린더 문자열을 이용해서 날짜를 미리 확인할 수 있다면 편리하지 않을까요? EVALUATE_CALENDAR_STRING 프로시저를 이용하여 실행 예정 시각을 미리 검토할 수 있습니다. 위의 예 ? 월~금7:00 AM ~ 3:00 PM Statspack 실행 ? 에서 설정된 실행 주기를 확인하는 방법이 아래와 같습니다:

 

set serveroutput on size 999999

declare

   L_start_date    TIMESTAMP;

   l_next_date     TIMESTAMP;

   l_return_date   TIMESTAMP;

begin

   l_start_date := trunc(SYSTIMESTAMP);

   l_return_date := l_start_date;

   for ctr in 1..10 loop

      dbms_scheduler.evaluate_calendar_string(

        'FREQ=DAILY; BYDAY=MON,TUE,WED,THU,FRI; BYHOUR=7,15',

         l_start_date, l_return_date, l_next_date

      );

      dbms_output.put_line('Next Run on: ' ||

          to_char(l_next_date,'mm/dd/yyyy hh24:mi:ss')

      );

      l_return_date := l_next_date;

   end loop;

end;

/
실행 결과는 다음과 같습니다:
Next Run on: 03/22/2004 07:00:00
Next Run on: 03/22/2004 15:00:00
Next Run on: 03/23/2004 07:00:00
Next Run on: 03/23/2004 15:00:00
Next Run on: 03/24/2004 07:00:00
Next Run on: 03/24/2004 15:00:00
Next Run on: 03/25/2004 07:00:00
Next Run on: 03/25/2004 15:00:00
Next Run on: 03/26/2004 07:00:00
Next Run on: 03/26/2004 15:00:00
위 결과로 미루어 설정된 내용에 문제가 없음을 확인할 수 있습니다.
작업과 프로그램의 연계
위의 사례는 특정 프로그램과 연결되지 않은 작업을 생성하는 방법을 설명하고 있습니다. 이번에는 특정 OS 유틸리티를 사용하는 프로그램을 생성하고, 실행 주기를 정의한 스케줄을 설정한 뒤, 이 두 가지를 조합하여 새로운 작업을 생성하는 방법을 설명합니다.

먼저 작업 내에서 프로그램을 사용한다는 사실을 데이타베이스가 인지하도록 해야 합니다. 이 프로그램을 생성하려면, CREATE JOB 권한을 갖고 있어야 합니다.

 

begin

    dbms_scheduler.create_program

    (

       program_name   => 'MOVE_ARCS',

       program_type   => 'EXECUTABLE',

       program_action => '/home/arup/dbtools/move_arcs.sh',

       enabled        => TRUE,

       comments       => 'Moving Archived Logs to Staging Directory'

    );

end;

/
이제 named program unit을 생성하여, 그 유형을 “executable”로 지정하고 실행될 program unit의 이름을 정의하였습니다.

다음으로, 매 30분 간격으로 실행되는 EVERY_30_MIN이라는 이름의 스케줄을 생성합니다:

 

begin

    dbms_scheduler.create_schedule

    (

       schedule_name   => 'EVERY_30_MINS',

       repeat_interval => 'FREQ=MINUTELY; INTERVAL=30',

       comments        => 'Every 30-mins'

    );

end;

/
프로그램과 스케줄을 생성한 뒤, 이 두 가지를 연관시킴으로써 새로운 작업을 생성합니다.

 

begin

   dbms_scheduler.create_job

   (

      job_name      => 'ARC_MOVE',

      program_name  => 'MOVE_ARCS',

      schedule_name => 'EVERY_30_MINS',

      comments      => 'Move Archived Logs to a Different Directory',

      enabled       => TRUE

   );

end;

/
이제 매 30분마다 move_arcs.sh 셸 스크립트를 실행하는 작업이 생성되었습니다. 이 스케줄은 데이타베이스 내부의 Scheduler 기능을 통해 관리되며, 따라서 cron 또는 AT 유틸리티를 사용할 필요가 없습니다.
Classes, Plans, and Windows
작업 스케줄링 시스템에 효과적으로 활용하려면 작업의 우선순위 지정이 가능해야 합니다. 예를 들어 OLTP 워크로드가 실행 중인 시간대에 통계 수집 작업이 갑자기 실행되어 성능을 저하시킬 수 있습니다. 통계 수집 작업이 OLTP 성능에 영향을 미치지 않도록 하기 위해, Scheduler가 제공하는 job classes, resource plans, and Scheduler Windows기능을 활용할 수 있습니다.

Job class는 할당된 자원을 공유하는 “resource consumer group”으로 매핑됩니다. Job class를 생성하기 위해, 먼저 OLTP_GROUP이라는 이름의 resource consumer group을 정의해 보겠습니다.

 

begin

   dbms_resource_manager.clear_pending_area();

   dbms_resource_manager.create_pending_area();

   dbms_resource_manager.create_consumer_group (

       consumer_group => 'oltp_group',   

       comment => 'OLTP Activity Group'

   );

   dbms_resource_manager.submit_pending_area();

end;

/
다음에는 resource plan을 생성합니다.

 

begin

   dbms_resource_manager.clear_pending_area();

   dbms_resource_manager.create_pending_area();

   dbms_resource_manager.create_plan

      ('OLTP_PLAN', 'OLTP Database Activity Plan');

   dbms_resource_manager.create_plan_directive(

      plan => 'OLTP_PLAN',

      group_or_subplan => 'OLTP_GROUP',

      comment => 'This is the OLTP Plan',

      cpu_p1 => 80, cpu_p2 => NULL, cpu_p3 => NULL, cpu_p4 => NULL,

      cpu_p5 => NULL, cpu_p6 => NULL, cpu_p7 => NULL, cpu_p8 => NULL,

      parallel_degree_limit_p1 => 4,

      active_sess_pool_p1 => NULL,

      queueing_p1 => NULL,

      switch_group => 'OTHER_GROUPS',

      switch_time => 10,

      switch_estimate => true,

      max_est_exec_time => 10,

      undo_pool => 500,

      max_idle_time => NULL,

      max_idle_blocker_time => NULL,

      switch_time_in_call => NULL

   );

   dbms_resource_manager.create_plan_directive(

      plan => 'OLTP_PLAN',

      group_or_subplan => 'OTHER_GROUPS',

      comment => NULL,

      cpu_p1 => 20, cpu_p2 => NULL, cpu_p3 => NULL, cpu_p4 => NULL,

      cpu_p5 => NULL, cpu_p6 => NULL, cpu_p7 => NULL, cpu_p8 => NULL,

      parallel_degree_limit_p1 => 0,

      active_sess_pool_p1 => 0,

      queueing_p1 => 0,

      switch_group => NULL,

      switch_time => NULL,

      switch_estimate => false,

      max_est_exec_time => 0,

      undo_pool => 10,

      max_idle_time => NULL,

      max_idle_blocker_time => NULL,

      switch_time_in_call => NULL

   );

   dbms_resource_manager.submit_pending_area();

end;

/
마지막으로 앞에서 생성된 resource consumer group을 이용해 job class를 생성합니다.

 

begin

   dbms_scheduler.create_job_class(

      job_class_name => 'OLTP_JOBS',

      logging_level => DBMS_SCHEDULER.LOGGING_FULL,

      log_history => 45,

      resource_consumer_group => 'OLTP_GROUP',

      comments => 'OLTP Related Jobs'

   );

end;

/
이 프로시저에서 사용된 매개변수들을 설명해 보겠습니다. LOGGING_LEVEL 매개변수는 해당 job class를 위해 얼마나 많은 로그 데이타를 기록할 것인지 정의하는데 사용됩니다. LOGGING_FULL은 job class에 포함된 작업의 모든 활동(생성, 삭제, 실행, 변경 등)을 로그에 기록함을 의미합니다. 로그는 DBA_SCHEDULER_JOB_LOG 뷰를 통해 확인할 수 있으며, LOG_HISTORY 매개변수에 저장된 대로 45일간 보관됩니다 (디폴트 값은 30일입니다). 이 class와 연관된 resource_consumer_group 도 정의되어 있습니다. DBA_SCHEDULER_JOB_CLASSES 뷰를 통해 정의된 job class들을 확인할 수 있습니다.

작업을 생성하는 과정에서, 필요한 경우 해당 작업을 job class에 할당할 수 있습니다. 예를 들어 collect_opt_stats() 저장 프로시저를 실행하여 옵티마이저 통계를 수집하는 COLLECT_STATS 작업을 생성하면서, 아래와 같이 job class를 할당할 수 있습니다.

 

begin

   dbms_scheduler.create_job

   (

      job_name        => 'COLLECT_STATS',

      job_type        => 'STORED_PROCEDURE',

      job_action      => 'collect_opt_stats',

      job_class       => 'OLTP_JOBS',

      repeat_interval => 'FREQ=WEEKLY; INTERVAL=1',

      enabled         => true,

      comments        => 'Collect Optimizer Stats'

   );

end;

/
위 명령을 실행하면 생성된 작업이 OLTP_JOBS 클래스에 할당됩니다. OLTP_JOBS 클래스에 적용되는 OLTP_GROUP resource plan을 통해 프로세스에 할당되는 CPU 자원, 다른 그룹으로 전환되기 전에 최대 실행 가능한 횟수, 전환되는 그룹 등을 설정할 수 있습니다. 동일한 job class에 할당된 작업은 동일한 resource plan의 적용을 받습니다. 이 기능을 활용하면 서로 다른 유형의 작업이 같은 리소스를 두고 경합을 벌이는 상황을 방지할 수 있습니다.

Scheduler Window는 특정 resource plan이 사용되는 시간대를 의미합니다. 예를 들어, 실시간 의사결정 작업에 관련한 업데이트 배치 작업이 주간에는 높은 우선순위를 갖는 반면 야간에는 낮은 우선순위를 갖는다고 가정해 봅시다. 이 경우 시간대별로 다른 resource plan을 정의하고, 정의된 내용을 Scheduler Window를 이용해 적용할 수 있습니다.
모니터링
실행 중인 작업의 상태는 DBA_SCHEDULER_JOB_LOG 뷰를 통해 확인할 수 있습니다. 이 뷰의 STATUS 컬럼은 작업의 현재 상태를 표시하는데 사용됩니다. 만일 STATUS 컬럼이 FAILED로 표시된다면, DBA_SCHEDULER_JOB_RUNS_DETAILS 뷰를 통해 그 원인을 확인할 수 있습니다.
관리
지금까지 여러 가지 유형의 오브젝트(program, schedule, job, job class 등)를 생성하는 방법에 대해 설명했습니다. 이렇게 생성된 오브젝트를 변경할 필요가 있다면, DBMS_SCHEDULER 패키지가 제공하는 API를 사용하면 됩니다.

Enterprise Manager 10g 홈 페이지에서 Administration 링크를 클릭하면, 그림 1과 같이 Administration 화면이 표시됩니다. Scheduler와 관련된 작업은 우측 하단의 “Scheduler” 항목에 표시됩니다 (그림의 붉은색 원 참조).

그림 1: Administration 페이지
이 페이지에서 제공되는 하이퍼링크를 활용하면 작업의 생성, 삭제, 관리와 같은 Scheduler 관련 작업을 쉽게 수행할 수 있습니다. 몇 가지 예를 들어 설명해 보겠습니다. 이미 작업을 생성해 둔 상태이므로, Job 탭을 클릭하면 그림 2와 같은 화면이 표시될 것입니다.

그림 2: 작업 스케줄의 확인
COLLECT_STATS 작업을 클릭하여 그 속성을 변경해 보도록 합시다. “Name” 필드를 클릭하면 그림 3과 같은 화면이 표시됩니다.

그림 3: 작업 매개변수
지금까지 확인한 것처럼, EM을 이용하면 작업, 스케줄, 옵션 등에 관련한 매개변수를 수정할 수 있습니다. 변경 작업을 완료한 뒤 “Apply” 버튼을 누르면 변경사항은 영구적으로 적용됩니다. “Apply” 버튼을 누르기 전에 “Show SQL” 버튼을 눌러 실행되는 SQL 구문을 확인하는 것도 가능합니다. 또 SQL 구문을 스크립트에 저장하여 나중에 실행하거나, 템플릿 용도로 활용할 수도 있습니다.

DBMS/오라클] 데이터 암호화 기능



Oracle 8i에서 데이터 암호화 기능 (글 오라클)작성일 : 2003/11/30 21:22
 

  조회수 : 1044

  
 출 처 : 오라클 Technical Bulletins, http://211.106.111.2:8880/bulletin/list.jsp?seq=12036
------------------------------------------------------------------------

No. 12036

데이터 암호화 기능 소개(8.1.6 new feature)
=========================================

개    요
=========
Oracle 8i Release2(8.1.6)에서는 데이터를 암호화하여 저장할 수 있는 향상된
기능(DES Encryption)을 제공한다
(Oracle 8i Release3(8.1.7)에서는 Triple DES Encryption)

즉 신용카드번호, 패스워드 등 보안이 필요한 데이터를 암호화된 형태로 저장하여
기존의 3rd Party Tool이나, Application Logic으로 구현하던 암호화 정책을
데이터베이스 차원에서 구현할 수 있도록 해준다.

DBMS_OBFUSCATION_TOOLKIT
========================
암 호화 기능을 이용하려면 DBMS_OBFUSCATION_TOOLKIT을 이용해야 한다.

이 패키지는 4개의 프로시져로 이루어져 있다.
- VARCHAR2 타입을 Encrypt/Decrypt할 수 있는 2개의 프로시져
- RAW 타입을 Encrypt/Decrypt할 수 있는 2개의 프로시져
(다른 타입은 지원하지 않으므로 number인 경우는 to_char 이용)

DBMS_OBFUSCATION_TOOLKIT을 이용하기 위해서는 :
1) SYS 유저로
   @$ORACLE_HOME/rdbms/admin/dbmsobtk.sql
   @$ORACLE_HOME/rdbms/admin/prvtobtk.plb
2) grant execute on dbms_obfuscation_toolkit to public;


제 한 사 항
===========
1) DES(Data Encryption Standard) symmetric key algorithm 방식을 이용.
   즉 암호화할 때 이용한 key를 분실했을 경우 데이터를 해독할 방법이 없다.

2) Encrypt하려는 data가 8 bytes 배수(8,16,... bytes)이어야 한다.

3) 미국무부의 암호화기술 수출제한조치에 의해 56-bit key를 사용.

4) 미국무부의 암호화기술 수출제한조치에 의해 한번 암호화된 데이터를
   또다시 암호화할 수 없다.

*) 많은 테이블을 Encrypt/decrypt할 경우 CPU 사용량을 증가시킬 수 있다.
*) 아래의 예제는 UTF8을 사용할 경우 한글 데이터를 암호화할 수 없다.
   (RPAD의 제약으로)

사 용 예
========
1) encrypt/decrypt에 이용할 FUNCTION을 만든다.
   (만약 input string이 8 byte 배수가 아니면 패딩을 한다)
*) 8.1.6에서는 key값이 8 byte 이상이어야 함(8.1.7 이후에는 제한없음)


- - - - - - - - - - - - - - - Code begins here - - - - - - - - - - - - - - -
REM ------------------------------------------------------------------------
REM DISCLAIMER:
REM    This script is provided for educational purposes only. It is NOT 
REM    supported by Oracle World Wide Technical Support.
REM    The script has been tested and appears to work as intended.
REM    You should always run new scripts on a test instance initially.
REM ------------------------------------------------------------------------

CREATE OR REPLACE PACKAGE CryptIT AS
   FUNCTION encrypt( Str VARCHAR2, 
                     hash VARCHAR2 ) RETURN VARCHAR2;
   FUNCTION decrypt( xCrypt VARCHAR2,
                     hash VARCHAR2 ) RETURN VARCHAR2;
END CryptIT;
/

CREATE OR REPLACE PACKAGE BODY CryptIT AS
   crypted_string VARCHAR2(2000);

   FUNCTION encrypt( Str VARCHAR2, 
                     hash VARCHAR2 ) RETURN VARCHAR2 AS
   pieces_of_eight INTEGER := ((FLOOR(LENGTH(Str)/8 + .9)) * 8);

   BEGIN

      dbms_obfuscation_toolkit.DESEncrypt(
               input_string     => RPAD( Str, pieces_of_eight ),
               key_string       => RPAD(hash,8,'#'),
               encrypted_string => crypted_string );
      RETURN crypted_string;
   END;

   FUNCTION decrypt( xCrypt VARCHAR2,
                     hash VARCHAR2 ) RETURN VARCHAR2 AS
   BEGIN
      dbms_obfuscation_toolkit.DESDecrypt(
               input_string     => xCrypt,
               key_string       => RPAD(hash,8,'#'),
               decrypted_string => crypted_string );
      RETURN trim(crypted_string);
   END;
END CryptIT;
/

- - - - - - - - - - - - - - -  Code ends here  - - - - - - - - - - - - - - -


2) Encrypt하여 데이터 입력

drop table encrypt_table;
create table encrypt_table( id number, passwd varchar(10) );

insert into encrypt_table values( 1, CryptIT.encrypt('tiger', 'key_a'));
insert into encrypt_table values( 2, CryptIT.encrypt('tiger', 'key_b'));

3) Decrypt하여 데이터 조회

SQL> select id, passwd from encrypt_table where passwd = 'tiger';

no rows selected

-> 물론 Decrypt하지 않으면 암호화된 데이터와 비교된다.

주의) encrypt된 데이터를 화면에 출력하면, terminal emulator가 오작동할 수 있다.
      그럴 경우, terminal emulator 프로그램 종료 후 다시 시작.

SQL> col passwd format a60
SQL> select id, dump(passwd) passwd from encrypt_table;

        ID PASSWD
---------- -------------------------------------------------------------
         1 Typ=1 Len=8: 246,27,80,184,227,225,245,31
         2 Typ=1 Len=8: 175,231,213,125,85,223,46,133


-> 저장장치에 Encrypt된 값으로 저장된다.

select id, CryptIT.decrypt(passwd,'key_a') passwd
from encrypt_table
where CryptIT.decrypt(passwd,'key_a') = 'tiger';

        ID PASSWD
---------- -------------------------------------------------------------
         1 tiger

select id, CryptIT.decrypt(passwd,'key_b') passwd
from encrypt_table
where CryptIT.decrypt(passwd,'key_b') = 'tiger';

        ID PASSWD
---------- ------------------------------------------------------------
         2 tiger

-> Encrypt할 때 사용한 Key로만 Decrypt할 수 있다.

주의) Table에 접근 권한이 있는 다른 유저도 Key값을 알면 Decrypt할 수 있다.

4) 관련 ORA number
ORA error 28231 "Invalid input to Obfuscation toolkit"
- input data, key값이 NULL일 경우 발생

ORA error 28232 "Invalid input size for Obfuscation toolkit"
- input data가 8 bytes 배수가 아닐 경우 발생

ORA error 28233 "Double encryption not supported by DESEncrypt in Obfuscation toolkit"
- encrypt data를 다시 encrypt경우 발생


관 련 자 료
===========
Oracle8i Supplied PL/SQL Packages Reference Release 2 (8.1.6)

DBMS/오라클] ora-09925 해결책(파일시스템_풀인_경우)



oracle의 admin/sid/pfile 에 보면 init 파일이 존재하는데 이곳에서 다음의 블록 부분을 확인한다.
사용자 삽입 이미지

그런다음 그 위치로 가서 디렉토리의 용량이 찼는지 확인한 다음, 만약 찼으면 파일들을 지워준다.
사용자 삽입 이미지
그러면 해결될 것이당~~^^)/

DBMS/오라클] EXP-00003: 세그먼트... 에러가 발생하는 경우 해결 방법



증상 :  EXP-00003: 세그먼트... 에러가 발생하는 경우 해결 방법

0. 오라클 클라이언트의 버전을 동일 버전으로 업그레이드 한다.

1. oracle 9.x 버전(클라이언트) 에서 10g 서버의 데이터 익스포트시 :

EXP-00003 : no storage definition found for segment(7,1955)
라는 에러 발생시

dba 권한 사용자가 아래의 명령을 실행하면 해결 된다. export시 참조하는 view를 만드는 쿼리문이다.
SQL> @?/rdbms/admin/catexp


2. ==>(원문 출처 : )  http://link.allblog.net/13777149/http://gampol.tistory.com/entry/9i-EXP-00003
When you use old version of exp to export tables with LOB column from Oracle 9.2.0.5 or higher version, you will get an error "EXP-00003 : no storage definition found for segment .....", actually this is an Oracle bug, you could temporary get it resolved by replace a view "exu9tne", as following:
    Before exporting, run the following SQL under sys:
CREATE OR REPLACE VIEW exu9tne (
tsno, fileno, blockno, length) AS
SELECT ts#, segfile#, segblock#, length
FROM sys.uet$
WHERE ext# = 1
UNION ALL
SELECT * FROM SYS.EXU9TNEB
/
    After exporting, run the following to restore the view definition according to Metalink Notes.
CREATE OR REPLACE VIEW exu9tne (
tsno, fileno, blockno, length) AS
SELECT ts#, segfile#, segblock#, length
FROM sys.uet$
WHERE ext# = 1

DBMS/오라클] Database Link사용법



출처: http://bosoa.egloos.com/1442337

Database Link사용법

원격지에 있는 데이터베이스를 link하는 법은 다음과 같다.

원격지의 Database의 Service Name이 piruks.kang.com이다.
나의 tnsnames.ora파일에는 Database alias가 piruks가 잡혀있다.

piruks.kang.com(원 격지DB)의 init.ora내용
db_name = "piruks"
db_domain = kang.com
instance_name = piruks
service_names = piruks.kang.com

maddog.kang.com(로컬 DB)의 tnsnames.ora내용
PIRUKS =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST = 10.0.0.102)(PORT = 1521))
    )
    (CONNECT_DATA =
      (SERVICE_NAME = PIRUKS.KANG.COM)
    )
  )





원 격지의 패러미터파일(init.ora)에 global_names를 true로 하면 dblink의 이름과 접속하는 db의 이름이 동일하도록 요구한다.

global_names = true [ or false ]

이 설정은 다음과 같이 해서 알 수 있다.
SQL> select name, value from v$parameter where name='global_names';

NAME                 VALUE
-------------------- ------
global_names         FALSE


현재 데이터베이스의 이름은 global_names에 질의함으로써 알 수 있다.

SQL> connect kang/xxxxxx@piruks;
연 결되었습니다.
SQL> select * from global_name;

GLOBAL_NAME
--------------------------------------
PIRUKS.KANG.COM



local db에 kang이라는 사용자로 접속한다.
SQL> connect kang/xxxxxx
연결되었습니다.

이 제 원격지(piruks.kang.com)의 DB에 kang이라는 사용자로 연결되는 Database link
를 생성한다.
여기서 piruks.kang.com은 원격지의 [Database name].[Domain name]이다.
보통 [Database name].[Domain name]는 Service Name이라 불리운다.
하지만 위와 같이 해서 제대로 않되는 경우가 있다.
이럴때는 global_name에 있는 이름을 dblink이름으로 정하면 해결된다.
piruks는 database connect string이다.
SQL> create database link piruks.kang.com
  2  connect to kang identified by xxxxxx
  3  using 'piruks';

데이타베이스 링크가 생성되었습니다.



사 용법은 다음과 같다.
보통의 DML문장에 '@piruks.kang.com'을 추가한다.
SQL> select * from tab at piruks.kang.com;

TNAME                                                        TABTYPE         CLUSTERID
------------------------------------------------------------ ------------
-- ----------
TEST                                                         TABLE

SQL> select * from test at piruks.kang.com;

NAME                        AGE
-------------------- ----------
강명규                       27

SQL> insert into test at piruks.kang.com values('홍길동', 30);

1 개의 행이 만들어졌습니다.

SQL> update test at piruks.kang.com set age=31 where age=30;

1 행이 갱신되었습니다.

SQL> select * from test at piruks.kang.com;

NAME                        AGE
-------------------- ----------
강명규                       27
홍길동                       31

SQL> delete from test at piruks.kang.com where age=31;

1 행이 삭제되었습니다.

데이터베이스링크에서 DDL문장은 적용되지 않는다.
SQL> drop table test at piruks.kang.com;
drop table test at piruks.kang.com
                *
1 행에 오류:
ORA-02021: 원격 데이터베이스에 DDL 조작들이 허용되지 않습니다


SQL> drop database link piruks.kang.com;

데이타베이스 링크가 삭제되었습니다.

SQL>

삭 제할대 DBLINK의 소유자로 삭제 하여야 한다.

========================================================================
Database Link(데이터베이스 링크)
먼저 디비 링크를 사용하기 위해서는 v$parameter 에 global_names=false로 되어 있어
야 합니다..
init.ora를 고치던지.아님 alter system set global_names=false;로 해 줘야 합니다.
그리고 grant create database link to scott; 로 해당 유저한테 권한을 부여 합니다.
그리고 tnsnames.ora파일을 수정해 해당 연결하고자 하는 database의 알리아스를 만
들어 주면 됩니다.

우선 고려되어야 사항은 ORACLE INSTANCE가 두 개 이상이고 각기 다른 PLATFORM에
서 운용된다는 가정하에서 각각의 HOST NAME과 ORACLE_SID는 다르고
NLS_CHARACTER_SET 은 동일하게 되어 있어야 합니다.

만약 같은 MACHINE에서 INSTANCE의 ORACLE_SID가 같다면 TNS ERROR가 발생할
것이다.
또한 미래를 위해 다른 MACHINE이라 할지라도 ORACLE_SID는 규칙에 의해 다르게 가
져가는 것이 좋습니다.
그리고, NLS_CHARACTER_SET이 동일하게 되어 있지 않으면 DATA 입/출력 시 한글 데
이타가 ?????로 나타날 것입니다.

그럼 환경 점검이 끝났으니 ORACLE7에서 ORACLE V6(ORACLE7의 경우도 비슷)에 있
는 TABLE의 DATA를 DB LINK를 이용하여 SELECT하거나 VIEW를 작성하여 보겠습니
다.

HOST NAME    :      HP7    ->    SUN7     
ORACLE_SID   :      ORA7   -     ORATEST  이라 할 때

   1)  HP7 에서 SUN7로 dblink생성하기.

       scott/tiger 로 Login

       SQL> create public database link HP7TOSUN7
            connect to scott identified by tiger
            using 'ORATEST';
       로 하면 된다.

       이때 V2인 경우의 ORATEST는 $ORACLE_HOME/network/admin directory의
       tnsnames.ora file 내에 지정된 service name이다.
       tnsnames.ora의 service name이 잘 setting 되어 있는지 확인하는 방법
       : SQL*Plus scott/tiger@service name했을 때, SQL*Plus에 log-in되어야
       합니다.

   2> SUN7 에 있는 TABLE의 select 및 view(view는 필요에 따라 생성) 작성,
      HP에서 작업
   
       SQL> select * from emp@HP7TOSUN7;

       SQL> create view emp_view as select * from emp@HP7TOSUN7  a
                 where a.deptno = 10;
                   
   3> HP7 에서 SYNONYM을 생성하여 사용하는 경우

       SQL> create synonym emp for emp@HP7TOSUN7;
       SQL> select * from emp;

로 한다면 간단히 분산 DB의 환경에서 사용 할 수 있습니다.

select 를 제외한 DML(insert,update,delete) 를 하려면,sqlplus log-in 시에 다음과 같
은 option 이 display 되어야 합니다.

   SQL*Plus: Release 3.3.3.0.0 - Production on Mon Jan 19 14:18:47 1998
   Copyright (c) Oracle Corporation 1979, 1996.  All rights reserved.

   Connected to:
   Oracle7 Server Release 7.3.3.4.0 with the 64-bit option - Production
   Release With the distributed, ......
   ------------------------------------

   remote 작업의 예

   select * from table_name@HP7TOSUN7;
   insert into table_name@HP7TOSUN7;
   delete table_name@HP7TOSUN7;

   (단 SERVER TO SERVER로 NETWORK 환경이 구축되어 있어야 하고,
   listener 가 반드시 떠 있어야 합니다.)

SQL>   create public database link link_test
  2    connect to scott identified by tiger
  3    using 'haksan21'; -- service name

데이타베이스 링크가 생성되었습니다.

SQL>  select * from all_db_links;

OWNER      DB_LINK              USERNAME   HOST       CREATED
---------- -------------------- ---------- ---------- --------
PUBLIC     HSA.WORLD            HSA        haksan21   99/03/12
PUBLIC     HSE.WORLD            HSE        haksan21   99/03/30
PUBLIC     HSR.WORLD            HSR        haksan21   99/03/23
PUBLIC     LINK_TEST.WORLD      SCOTT      haksan21   99/05/07

SQL> drop public database link link_test;

데이타베이스 링크가 삭제되었습니다.

SQL> select * from all_db_links;

OWNER      DB_LINK              USERNAME   HOST       CREATED
---------- -------------------- ---------- ---------- --------
PUBLIC     HSA.WORLD            HSA        haksan21   99/03/12
PUBLIC     HSE.WORLD            HSE        haksan21   99/03/30
PUBLIC     HSR.WORLD            HSR        haksan21   99/03/23

** export, import 작업을 수행할 경우 (또는 동일한 환경으로 새로운 유저를 만들경
우) ??

Synonym 처음
* 목적 : 다른 사용자가 소유한 테이블을 쉽게 사용하기 위해
         OBject 이름을 짧게 하기 위해

* SYNONYM의 정보를 가진 Data Dictionary
  USER_SYNONYMS
  ALL_SYNONYMS

* 예문
  create synonym tmp00 for fsr.tmp00;

* 삭제 : drop synonym synonym_name

*** DB LINK의 사용

    다른 기계의 데이타베이스를 사용하고자 할 때 이용
   
    create public database link link_name
    connect to user_name identified by password
    using 'db_alias_name';
   
    확인 : all_db_links
   
    사용 : select * from user_name.table_name@link_name;
    synonym : create synonym synonym_name for
user_name.table_name@link_name;

View 처음
* 뷰는 테이블이나 다른 뷰를 기반으로 한 논리적 테이블이다. 스스로 자료를 가지지 않으나 조회, 수정할 수 있는 창문과 같다. 뷰의 근거가 되는 테이블을 base table이라 한다. 뷰 는
  select 문장의 형태로 data dictionary에 저장된다.

* 잇점 : 1) DB의 특정 부분만 조회하므로 접근을 제한할 수 있다.
         2) 복잡한 질의를 단순화할 수 있다. (여러 테이블의 조인관계를 숨길 수 있다.)

* 뷰를 만드는 여러 예문
  create or replace view empvu30
  as select * from emp where deptno = 30
     with check option constraint empvu30_ck; -- deptno가 30인 자료만 가능

  create or replace view empvu30 (id_number, employee, job) -- alias 변경
  as select empno, ename, job from emp where deptno = 30 with read only; --
delete불가
 
  create or replace view v_yycnt as
  select sum(aa) aa, sum(bb) bb, sum(cc) cc
  from (
       select count(*) aa, 0 bb, 0 cc from emp
        where to_char(hiredate, 'yy') = '81'
       union all
       select 0 aa, count(*) bb, 0 cc from emp
        where to_char(hiredate, 'yy') = '82'
       union all
       select 0 aa, 0 bb, count(*) cc from emp
        where to_char(hiredate, 'yy') = '83');
 
* 뷰의 정보를 가진 Data Dictionary
  USER_VIEWS;
  ALL_VIEWS;
  DESC view_name;

* 삭제 : drop view view_name;

DBMS/오라클]Case Statement



출처 : http://www.techonthenet.com/oracle/functions/case.php


Oracle/PLSQL: Case Statement


Starting in Oracle 9i, you can use the case statement within an SQL statement. It has the functionality of an IF-THEN-ELSE statement.
The syntax for the case statement is:
CASE  [ expression ]
  WHEN condition_1 THEN result_1
  WHEN condition_2 THEN result_2
  ...
  WHEN condition_n THEN result_n
  ELSE result
END
expression is optional. It is the value that you are comparing to the list of conditions. (ie: condition_1, condition_2, ... condition_n)
condition_1 to condition_n must all be the same datatype. Conditions are evaluated in the order listed. Once a conditionis found to be true, the case statement will return the result and not evaluate the conditions any further.
result_1 to result_n must all be the same datatype. This is the value returned once a condition is found to be true.

Note:
If no condition is found to be true, then the case statement will return the value in the ELSE clause.
If the ELSE clause is omitted and no condition is found to be true, then the case statement will return NULL.
You can have up to 255 comparisons in a case statement. Each WHEN ... THEN clause is considered 2 comparisons.

Applies To:
  • Oracle 9i, Oracle 10g, Oracle 11g

For example:
You could use the case statement in an SQL statement as follows: (includes the expression clause)
select table_name,
CASE owner
  WHEN 'SYS' THEN 'The owner is SYS'
  WHEN 'SYSTEM' THEN 'The owner is SYSTEM'
  ELSE 'The owner is another value'
END
from all_tables;
Or you could write the SQL statement using the case statement like this: (omits the expression clause)
select table_name,
CASE
  WHEN owner='SYS' THEN 'The owner is SYS'
  WHEN owner='SYSTEM' THEN 'The owner is SYSTEM'
  ELSE 'The owner is another value'
END
from all_tables;
The above two case statements are equivalent to the following IF-THEN-ELSE statement:
IF owner = 'SYS' THEN
     result := 'The owner is SYS';
ELSIF owner = 'SYSTEM' THEN
    result := 'The owner is SYSTEM'';
ELSE
    result := 'The owner is another value';
END IF;

The case statement will compare each owner value, one by one.

One thing to note is that the ELSE clause within the case statement is optional. You could have omitted it. Let's take a look at the SQL statement above with the ELSE clause omitted.
Your SQL statement would look as follows:
select table_name,
CASE owner
  WHEN 'SYS' THEN 'The owner is SYS'
  WHEN 'SYSTEM' THEN 'The owner is SYSTEM'
END
from all_tables;
With the ELSE clause omitted, if no condition was found to be true, the case statement would return NULL.

For Example:
Here is an example that demonstrates how to use the case statement to compare different conditions:
select
CASE
  WHEN a < b THEN 'hello'
  WHEN d < e THEN 'goodbye'
END
from suppliers;

Frequently Asked Questions


Question:  Can you create a case statement that evaluates two different fields? I want to return a value based on the combinations in two different fields.
Answer:  Yes, below is an example of a case statement that evaluates two different fields.
select supplier_id,
CASE
  WHEN supplier_name = 'IBM' and supplier_type = 'Hardware' THEN 'North office'
  WHEN supplier_name = 'IBM' and supplier_type = 'Software' THEN 'South office'
END
from suppliers;
So if supplier_name field is IBM and the supplier_type field is Hardware, then the case statement will return North office. If the supplier_name field is IBM and the supplier_type is Software, the case statement will return South office.

DBMS/오라클]Broken된 Job을 자동으로 재실행 시키는 방법



출처 : http://ggang-tong.tistory.com/32



[oracle]Broken된 Job을 자동으로 재실행 시키는 방법

--------------------------------------------------------------------------------
BROKEN 된 JOB을 자동으로 재실행 시키는 방법
=========================================

1. broken job
~~~~~~~~~~~~~~
Oracle에서 특정 작업을 주기적으로 실행시키기 위해서는 job을 이용하게 된다.
이것은 snp라는 background process가 각 job의 interval간격으로 작업을 실행
하는데, snapshot과 같은 것이 job의 대표적인 예이며, dbms_job package를
이용하여 직접 job을 등록 및 관리, 삭제가 가능하다.

이러한 job이 문제가 발생하여 수행이 오류가 발생하면 1분, 2분, 4분, 8분과
같은 간격으로 자동으로 재실행을 하게 되고, 이 주기가 해당 job의 inerval보다
크게 되면 그때부터는 interval간격마다 job을 실행하도록 시도한다. job의
interval이 1분보다 작으로 interval간격대로 시도하고, 예를 들어 interval이
2 분 20초마다이면, 1분, 2분, 이후에는 2분 20초 마다씩 새로 fail된 job을
실행해 본다. 이렇게 fail이 발생한 job을 자동으로 재실행하는 것은 최대 16번
이며, 16번 시도후에는 job이 broken상태가 되어 더 이상 snp process는 시도를
하지 않게 된다.

oracle이 이렇게 16번 시도 후 job을 broken상태로 하는 이유는 16번 시도 때까지
문제가 해결되지 않은 job이라면, 예를 들어 network이 장시간 down되어 snapshot
refresh가 안 되는 것과 같이 문제가 장기화될 가능성이 많고 그러한 job을 계속
시도해 보는 것 자체가 cpu를 많이 소모하는 낭비되는 작업이라는 판단 때문이다.
그러나 일단 job이 broken되면, 그 job을 fail 상태로 만든 원인이 제거된 후에도
여전히 실행되지 않은 상태로 있게 되어 db admin이 수시로 broken된 job이 있는
지를 확인하고 manual하게 dbms_job.run등을 실행하여 다시 실행하도록 할 필요가
있다.

이러한 db admin 작업을 덜고 database 자체에서 broken job에 대해서도 계속
run을 시도하고자 하는 경우 이 문서를 이용하여 작업하면 된다.

2. dba_jobs view에 대해서
~~~~~~~~~~~~~~~~~~~~~~~~~~
dba_jobs(혹은 user_jobs)는 등록된 job에 관한 여러가지 정보를 사용자게 제공
한다.

특히 job의 broken과 관련하여 중요한 몇개의 column에 대해서 살펴본다.
broken : 해당 job이 broken되었으면 Y, 그렇지 않으면 N로 나타난다.
failures : job이 시도되었으나 fail된 숫자이다. 이 숫자가 16이 되면 broken이
Y가 되어 더 이상 snp process는 시도하지 않게 되어 이 숫자도
증가가 되지 않는다.
단, dbms_job.run을 user가 manual하게 실행해도 여전히 문제가 발생
하면 이 숫자가 이미 16이상이라도 시도할 때마다 계속 1씩 증가한다.
next_date: job이 다음에 실행될 시간이다.

job이 실행을 시작하는 시점에 last_date + interval = next_date로
계산한 후 job 실행이 끝나면 (혹은 오류 발생 후) 시작 시점에
계산된 next_date값이 dba_jobs에 기록되어 확인이 가능해 진다.
이 시간이 과거로 되어 있으면, failures가 0이고 broken이 N이라
하더라도 snp는 그 job을 run하려고 시도하지 않는다.
this_date: 이것은 현재 실행되는 job이 실행을 시작한 시간을 나타낸다. 이미
실행이 끝난 job이라면 이 부분은 null로 나타나면 이 컬럼에 값이
있는 job에 대해서는 dba_jobs_running에도 정보가 나타난다.

3. broken된 job을 실행되도록 하는 procedure

먼저, broken된 job을 찾아 broken을 false로 만들어 주고 snp가 다시 실행하도록
next_date를 지정해 주는 procedure를 만든다.
이 procedure는 아래 3-2와 같으며 수행한 기록을 남기기 위해 job_log라는
table을 만들었는데 이 부분은 빼도 무관하다.

3-1 log table 생성
아래 procedure를 실행시키기 전에 먼저 이와 같이 table을 만든다.

SQL> create table job_log (jobno number,
jobname varchar2(30),
jobdate date);

3-2 broken job을 snp가 다시 실행하도록 하기 위해 다음과 같은 release_job을
관리하고자 하는 job의 owner에서 생성한다.

(1) broken job을 선택할 때 dba_jobs 대신에 user_jobs를 이용한다.
dbms_job package는 항상 해당 owner의 job만을 대상으로 작동하므로,
dba_jobs를 확인하고 보이는 job에 대해서 연산하면 owner가 아닌 경우
그러한 job이 없다는 오류가 발생하게 된다.

(2) dbms_job.run을 바로 실행하지 않은 이유는 dbms_job.run은 procedure
내에서 call하지 못하도록 정의되어 실제 사용하면 오류가 발생하거나
수행이 되지 않는다.

(3) broken='Y' 뿐 아니라 failures가 15이상인 것을 함께 check하는 이유는
이 procedure로 인해 일단 broken이 N로 변경된 상태에서 여전히 오류가
있으면 다음 수행 때 broken이 N여서 제외되기 때문이다.

(4) dbms_job.change의 세번째 argument가 next_date인데 이렇게 next_date를
원하는 시간, 혹은 약간의 미래로 맞추어 놓아야 release_job이 수행된
이후 snp가 이 시간에 broken으로 모아진 job을 실행하게 된다.

(5) 결국 broken으로 선택된 job들을 release_jobs가 수행될 때마다 한번씩
snp process로 하여금 다시 시도되도록 지정되어 진다.

(6) procedure source
create or replace procedure release_jobs as
cursor my_broken_jobs is
select job, what from user_jobs where broken = 'Y' or failures > 15;
begin
for broken_jobs in my_broken_jobs
loop
begin
dbms_job.broken(broken_jobs.job,FALSE);
dbms_job.change(broken_jobs.job, null, sysdate+1/1440, null);
insert into job_log values (broken_jobs.job,
broken_jobs.what,
sysdate);
commit;
Exception
when others then
null;
end;
end loop;
end;
/

4. release_jobs를 job으로 등록한다.

위에서 정의한 release_job을 job으로 등록하여 broken된 job을 찾아 실행해주는
작업 자체가 주기적으로 실행되도록 한다.
release_jobs 자체는 network을 타거나 space를 필요로 하는 등의 작업이 아니라,
fail 이 발생할 우려는 거의 없다.
이 release_jobs를 등록한 job의 next_date가 미래이고 fail이 없는지만 확인하면,
나머지 broken job들은 여기에서 등록된 job이 관리하게 된다.

SQL>variable job number;
SQL>exec dbms_job.submit(:job, 'RELEASE_JOBS;',sysdate,'sysdate+1/1440');
SQL>exec dbms_job.run(:job);
SQL>commit;

출처 : http://kr.blog.yahoo.com/jhoony73/590306.html?p=1&pm=l

DBMS/오라클] 패키지 문법


원본출처 : http://www.oracleclub.com/lecture/1075



Package(패키지)

 
  패키지(package)는 오라클 데이터베이스에 저장되어 있는 서로 관련있는 PL/SQL 프로지져와 함수들의 집합 이다.
  패키지는 선언부와 본문 두 부분으로 나누어 진다.

패키지 선언절 문법

  • - 선언절은 패키지에 포함될 PL/SQL 프로시저나, 함수, 커서, 변수, 예외절을 선언 한다.
  • - 패키지 선언부에서 선언한 모든 요소들은 패키지 전체에 적용 된다.
  • - 즉 선언부에서 선언한 변수는 PUBLIC 변수로 사용 된다.

패키지 본문 문법

  • - 패키지 본문은 패키지에서 선언된 부분의 실행을 정의 한다.
  • - 즉 실재 프로시져나 함수의 내용에 해당하는 부분이 온다.

아래는 네 개의 프로시저를 하나의 패키지로 생성하는 아주 간단한 예제이다.
프로시저명프로시저 기능
all_emp_info모든 사원의 사원 정보 (사번, 성명, 입사일)
all_sal_info모든 사원의 급여 정보 (평균급여, 최고급여, 최소급여)
dept_emp_info특정 부서의 사원 정보 (사번, 성명, 입사일)
dept_sql_info특정 부서의 급여 정보 (평균급여, 최고급여, 최소급여)

패키지 선언부 생성 예제

 
SQL> CREATE OR REPLACE PACKAGE emp_info AS

       PROCEDURE all_emp_info;   -- 모든 사원의  사원 정보
        
        PROCEDURE all_sal_info;   -- 모든 사원의  급여 정보

        -- 특정 부서의  사원 정보
        PROCEDURE dept_emp_info (v_deptno IN  NUMBER) ;

        -- 특정 부서의  급여 정보
        PROCEDURE dept_sal_info (v_deptno IN  NUMBER) ;

    END emp_info;
    /
    

패키지 본문 생성 예제

 
SQL> CREATE OR REPLACE PACKAGE BODY emp_info AS

     -- 모든 사원의  사원 정보 
     PROCEDURE all_emp_info
     IS

         CURSOR emp_cursor IS
         SELECT empno, ename, to_char(hiredate, 'RRRR/MM/DD') hiredate
         FROM emp
         ORDER BY hiredate;

     BEGIN

         FOR  aa  IN emp_cursor LOOP

             DBMS_OUTPUT.PUT_LINE('사번 : ' || aa.empno);
             DBMS_OUTPUT.PUT_LINE('성명 : ' || aa.ename);
             DBMS_OUTPUT.PUT_LINE('입사일 : ' || aa.hiredate);

         END LOOP;

         EXCEPTION
           WHEN OTHERS THEN
             DBMS_OUTPUT.PUT_LINE(SQLERRM||'에러 발생 ');

     END all_emp_info;


     -- 모든 사원의  급여 정보 
     PROCEDURE all_sal_info
     IS
    
         CURSOR emp_cursor IS
         SELECT round(avg(sal),3) avg_sal, max(sal) max_sal, min(sal) min_sal
         FROM emp;
    
     BEGIN

         FOR  aa  IN emp_cursor LOOP
 
             DBMS_OUTPUT.PUT_LINE('전체급여평균 : ' || aa.avg_sal);
             DBMS_OUTPUT.PUT_LINE('최대급여금액 : ' || aa.max_sal);
             DBMS_OUTPUT.PUT_LINE('최소급여금액 : ' || aa.min_sal);
         
         END LOOP;


         EXCEPTION
            WHEN OTHERS THEN
               DBMS_OUTPUT.PUT_LINE(SQLERRM||'에러 발생 ');
     END all_sal_info;


     --특정 부서의  사원 정보
     PROCEDURE dept_emp_info (v_deptno IN  NUMBER)
     IS

         CURSOR emp_cursor IS
         SELECT empno, ename, to_char(hiredate, 'RRRR/MM/DD') hiredate
         FROM emp
         WHERE deptno = v_deptno
         ORDER BY hiredate;

     BEGIN

         FOR  aa  IN emp_cursor LOOP

             DBMS_OUTPUT.PUT_LINE('사번 : ' || aa.empno);
             DBMS_OUTPUT.PUT_LINE('성명 : ' || aa.ename);
             DBMS_OUTPUT.PUT_LINE('입사일 : ' || aa.hiredate);

         END LOOP;

        EXCEPTION
            WHEN OTHERS THEN
               DBMS_OUTPUT.PUT_LINE(SQLERRM||'에러 발생 ');

     END dept_emp_info;


     --특정 부서의  급여 정보
     PROCEDURE dept_sal_info (v_deptno IN  NUMBER)
     IS
    
         CURSOR emp_cursor IS
         SELECT round(avg(sal),3) avg_sal, max(sal) max_sal, min(sal) min_sal
         FROM emp 
         WHERE deptno = v_deptno;
             
     BEGIN

         FOR  aa  IN emp_cursor LOOP 
 
             DBMS_OUTPUT.PUT_LINE('전체급여평균 : ' || aa.avg_sal);
             DBMS_OUTPUT.PUT_LINE('최대급여금액 : ' || aa.max_sal);
             DBMS_OUTPUT.PUT_LINE('최소급여금액 : ' || aa.min_sal);
         
         END LOOP;

         EXCEPTION
             WHEN OTHERS THEN
                DBMS_OUTPUT.PUT_LINE(SQLERRM||'에러 발생 ');

     END dept_sal_info;        
    
  END emp_info;
  /
    

패키지 실행

패키지의 실행은 패키지 명 다음에 점(.)을 찍고 프로시저냐 함수 명을 적어주면 된다.
 
-- DBMS_OUTPUT.PUT_LINE을 출력하기 위해 사용
SQL> SET SERVEROUTPUT ON ; 

-- 패키지 실행
SQL> EXEC emp_info.all_emp_info;

SQL> EXEC emp_info.all_sal_info;

SQL> EXEC emp_info.dept_emp_info(10);

SQL> EXEC emp_info.dept_sal_info(10); 
    
 
 

DBMS/오라클] 배치, 자바배치 프로세싱




출처 : http://richarm.egloos.com/5546389

오라클 export 배치작업 일관련

#특정유저 백업
c:\oracle\ora92\bin\exp user/pass file=d:\db_backup\%date%.dmp OWNER=user log=d:\db_backup\%date%.log

#전체백업
c:\oracle\ora92\bin\exp user/pass file=d:\db_backup\%date%.dmp fully=y log=d:\db_backup\%date%.log



출처 : http://www.zetswing.com/bbs/board.php?bo_table=ORACLE_TIP2&wr_id=24
crontab 이용한 백업
test 삼아서 scott 유저를 전체 export 할려구 합니다....
backup.sh라는 파일로 스크립트를 생성을 햇는데.... oracle 유저로 스크립트를 실행 시키면 정상적인 파일이 생성이 됩니다...
문제는 여기서 부터 입니다...
backup.sh라는 파일을  crontab -e oracle에  등록을 하면 등록된 시간에 정상적으로 수행이 되지를 않습니다...
dump 파일이 생성이 되는 디렉토리 역시  oracle 유저로 권한및 소유권이 생성이 ?榮쨉?....
cron 데몬도 정상적으로 올라 왔구여.... 도대체가 뭐가 문제인지 모르겠습니다...

/oracle/app/oracle/product/10.2.0/backup.sh

setenv DATE `date +%Y%m%d`
exp system/123456 file='/log/imsi/$DATE.dump' log='/log/imsi/$DATE.log' owner=scott
crontab -l oracle
33 16 * * * /oracle/app/oracle/product/10.2.0/backup.sh

실행은
00 02 * * 6 /app/oracle/bin/export.sh TGDOM
쉘 내역
more /app/oracle/bin/export.sh
ORACLE_HOME=/app/oracle/product/10.1.0; export ORACLE_HOME
ORACLE_SID=$1;export ORACLE_SID
PATH=$ORACLE_HOME/bin:$PATH
EXPFILE=/backup/ORACLE/exp/$ORACLE_SID`date '+_%y%m%d'`.dmp
LOGFILE=/backup/ORACLE/exp/$ORACLE_SID`date '+_%y%m%d'`.log
find /backup/ORACLE/exp/ -name "*.dmp" -mtime +21 -exec rm -r {} \;
find /backup/ORACLE/exp/ -name "*.log" -mtime +21 -exec rm -r {} \;
exp system/123456 full=y file=$EXPFILE log=$LOGFILE compress=n statistics=none buffer=1000000 rows=y
date >> $LOGFILE 


 1  2007-11-28 17:54:54   
내용은 실행 및 로그 보시면서
입맛에 맛게 고치시면 됩니다. 




Oracle Batch Processing  JAVA 
2006/05/15 16:37

 

이전 아티클에서는 database insert, delete speedup에 대하여 알아보았다. 이번 편에서는 데이터베이스
transation중 가장 비용이 많이 드는 update 문장에 대한 batch처리 방법을 오라클을 기준으로
이용하여 알아보겠다.

 Batch에 대한 이야기를 시작하며

앞서 이미 배치의 기본개념과 JDBC 2.0에 추가되었던 addBatch()메소드와 executeBatch()메소드를
이용하여 driver가 어떻게 작동하는지와 그 API를 사용한 코딩방법에 대하여 살펴보았다.

이미 배치에 관련된 프로젝트한지는 상당히 되었으나, 대부분의 작업은 한쪽의 database의
select를 한후 다른 database의 insert를 하는 트랜잭션이었다.
현재 수행하는 프로젝트에서는 적게는 30만건정도에서 많게는 200만건정도의 레코드를
select해온후 각각의 레코드를 이용하여 billing을 하는 경우인데 위에서 말한 30~200만건의
레코드가 바로 update 대상이었다.

보통 2개의 데이터베이스에서 5개정도의 테이블을 한꺼번에 transaction을 일으킨다는 것은
쉬운 일이 아니다. 게다가 그중에 update처리를 해야 하는 테이블이 있을 경우 tuning을 하지
않는다면 speed가 현저하게 떨어지는(보통 떨어지는 것이 아니라, 아예 멈춰있는 것처럼 보인다)
것을 금새 느낄 수 있으리라~

사실 JDBC API의 standard batching을 사용할 때 이미 update에 대한 부분까지도 모두 빠른
transaction을 사용할 수 있었다면 이번과 같은 update batching에 대한 글은 insert, delete작업과
함께 끝났어야 했다.

자, 이제 전체를 아우르는 개념설명부터 시작하여 각각의 경우에 대한 처리방법에 대하여
살펴보도록 하자.

Update Batching

데이터베이스로의 퍼포먼스 향상 및 불필요한 작업의 수를 줄이려한다면 그냥 생각하기에도
매번 발생하는 INSERT, DELETE, UPDATE처리를 한번의 배치작업으로 처리할 수 있게끔 만드는 것이
최선으로 방법으로 생각이 되어질 수 있을 것이다.
이러한 것을 반영시켜놓은 것이 Sun의 JDBC 2.0 Specification이었는데, 지금 여기서 논하고자 하는
오라클에서는 약간의 용어를 다르게 쓰고 있다.
크게 2가지의 모델을 가져다 쓰는데

1. Standard model
Oracle8i release 8.1.6과 Sun의 JDBC 2.0 Specification을 구현한 것을 말한다.

2. Oracle-specific model
릴리즈 8.1.5부터 지원되기 시작했으며, Sun의 JDBC2.0Spec과는 완전 독립적으로 처리하는 배치를
가르켜 Oracle-specific모델이라 말한다.

note. 여기서 한가지 주의할 점은 위의 두가지 형태를 섞어서 사용할 수 없는데, 소위 또 호기심이
발동해서 사용해보겠다고 하면 해보라. 당장 exception이 발생할테니.. ^^


위에서 모델 두가지를 설명했고 빨간 글씨로 주의를 한번 줬는데 그럼 이렇게 생각할지도 모르겠다.
"놀새야~! 그럼 도대체 뭘가지고 쓰란 말이냐"라고 이야기한다면 대답은 이렇다.
" 상황에 맞게 적절한 걸 가져다 써라" 라는 것인데 해당 기간업무가 펴~~엉생~ 오라클로만 돌아가면
오라클 모델로 사용해도 될테고, 범용성 문제가 제기 된다면 stanard모델을 사용하면 될터이고..

오라클에서 이야기하는 그 상황에 대하여 한번 보자.

  • 오라클모델을 쓰게 되면 이식성은 떨어지지만, 결과적으로는 엄청난 퍼포먼스 향상을 이룰것이다.
  • Standard모델은 이식성은 아주 좋지만, 약간의 성능향상을 이룰수 있을 것이다.


아~ 평생 오라클로 할것인가? 사실 앞선 테스트만 하더라도 standard insert, delete 배치성능은 상당했다.
그런데 자기네 모델을 쓰면 쨉도 안되는다는 것인데 당신이 직접 오라클 모델을 사용하여 테스트하면
아마도 눈이 휘둥그레질것이다

왜냐~ 전에 배치작업을 사용하지 않고 작업할때 밖에서 나가서 커피한잔 마시고, 화장실 갔다오고 해도
안되던 것이
"엔터키를 한방치고 자리에서 슬슬 일어나려고 할때 이미 트랜잭션작업들이 끝나기때문이다."

오라클모델에서 사용하는 statement

일반적으로 반복되는 쿼리에 대하여 값만 바꾸는 형태의 statement는 무엇인가? 그렇다.
PreparedStatement이다.
안타깝게도 오라클에서는PreparedStatement밖에 사용하질 못한다.
또한 배치에 관련된 작업은 UPDATE, INSERT, DELETE로 한정할 수 있으며 SELECT에 대한 것은
이미 이전의 article인 select speedup에서 처리하는 것을 보여주었다.


note. 위에서 PreparedStaement만 사용한다고 했는데 CallableStatement도 배치를 지원해주냐고 물으면
" 그런거 구현하려고 한적없다" 라는 대답만 듣게 될 것이다.



오라클배치모델의 특징

  • 배치에 대한 어떠한 세팅도 이루어져 있지 않다면 connection은 자동으로 1건데이터에 대한 각각의
    배치처리를 시도하려고 한다.
  • 오라클에서 이야기하는 최상의 퍼포먼스값은 batch작업의 크기가 5에서 30사이일때라고 이야기한다.
  • PreparedStatement에 아무리 열나게 배치를 올리고 있다고 하더라도 COMMIT만 만나면 driver는
    아무런 생각없이 바로 sendBatch()라는 request를 데이터베이스로 날려서 현재 올려진 데이터를
    update시켜버린다.


당신~ 질문하나 하겠다. JDBC에서 database commit이 언제 날아가는 지 아나? 바로 statement가 닫힐때나
connection이 close될때 자동으로 날아간다. 알고 있었는가? 알고 있었으면 말구~~ ^^

본격적인 배치작업

자, 이제 본격적인 오라클모델 배치를 시작하도록 하자.

1. Connection에 batch value setting하기

처음에 우선 오라클데이터베이스와 연결이 되어진다면 connection객체를 얻게 될 것이다. 여기서부터
배치에 대한 세팅이 들어가져야 하는데 코딩은 아래와 같다.

        ((OracleConnection)conn).setDefaultExecuteBatch(20);

오호. 무엇하는 코드인고? 당연히 오라클 모델의 connection을 불러오는 것이다.
위의 코드는 간단하면서 객체지향의 polymorphism이 그대로 묻어난 예술적인 코드이다. ^^

즉 우리가 얻어낸 connection이란 놈은 해당 jdbc driver의 super class인 java.sql.Connecdion인터페이스
이다. 그것을 conn.getClass().getName()하게 되면 oracle.jdbc.driver.OracleConnection이란 예쁜 객체를
돌려주게 되는 데 겉껍데기로 싸여있으니 제 구실을 못할 것 같아서 casting을 이용하여
꺼꾸로 끄집어 낸것이다.

거기에는 default배치값을 세팅할 수 있는데 위에서 이야기했던 최상의 퍼포먼스를 낼수 있는
5~30사이의 값을 세팅하여 넣었다.

자, 보통의 코딩에서 connection을 추출해냈다면 당신의 습관적인 다음 코딩은 무엇인가?
바로 connection에서 statement를 뽑아낼 것인데, 위에서도 말했듯이 오라클은 
PreparedStaement만을 지원한다고 했으므로 PreparedStatement를 이용하여 값을 세팅하도록 한다.

2. Statement에 batch value setting하기

  1. 아래의 코딩은 일반적으로 당신도 많이 하는 것일테고 그냥 보자.
    PreparedStatement ps = conn.prepareStatement
                                  ("INSERT INTO dept VALUES (?,?,?)");
    ps.setInt (1,12);
    ps.setString (2,"Carouser");
    ps.setString (3,"Korea");

  2. 여기서부터가 중요한데 저렇게 해서 PreparedStaement를 세팅한 후에 다시 오라클배치를
    할수 있도록 속성을 끌어내도록 해야 한다. 예제는 OraclePreparedStatement객체를 뽑은 후 
    setExecuteBatch()메소드안의 기본배치 사이즈를 2개로 잡은 것이다. 아래처럼 잡으면
    위의 connection이 잡은 배치사이즈와는 다른 형태인 것임을 알아두어야 한다.

    ((OraclePreparedStatement)ps).setExecuteBatch(2);

    혹여, 현재 PreparedStaement에 배치사이즈를 알고 싶다면 아래처럼 한번 찍어보면 될것이다

    System.out.println (" 배치크기 " +
                       ((OraclePreparedStatement)ps).getExecuteBatch());

  3. 자, 이제 어떤 시점에 데이터베이스에 update시켜달라고 요청해야 하는데, 아래처럼 하면
    바로 업데이트 될것 같은가? 안된다. 왜냐하면 batchSize를 2로 했기 때문에 그냥 버퍼링한다고
    보면 된다.
    System.out.println ("Number of rows updated so far: "
                                      + ps.executeUpdate ());
  4. 자, 아래의 값을 다시 한번 세팅시킨후 다시 executeUpdate()를 수행하면 어찌 될것인가?
    위의 PS에서 세팅한 batch value인 2와 같은 수의 데이터가 세팅되어져 있으므로 드디어
    Oracle로의 배치작업이 자동으로 시작되게 된다. 와~ 인공지능수준인가?
    ㅎㅎ, 당신도 가볍게 짤수 있는 코드이긴 하다.
    ps.setInt (1, 11);
    ps.setString (2, "ienvyou");
    ps.setString (3, "Korea");

    int rows = ps.executeUpdate ();
    System.out.println ("Number of rows updated now: " + rows);

    ps.close ();


자 간단한 샘플코드 하나 보자.
import java.sql.*; import oracle.sql.*; import oracle.jdbc.driver.*; class BatchTest{ public static void main(String[] args) throws Exception{ Connection conn = null; Connection conn2 = null; PreparedStatement pstmt= null; try { Class.forName("oracle.jdbc.driver.OracleDriver"); String url = "jdbc:oracle:thin:@192.168.0.137:1521:abn"; conn = DriverManager.getConnection(url, "abs", "abs");((OracleConnection)conn).setDefaultExecuteBatch(30); conn.setAutoCommit(false); pstmt = conn.prepareStatement(" update ABT230 SET update_ymd = ? where customer_no = ?");((OraclePreparedStatement)pstmt).setExecuteBatch(20); // 요거는 오라클배치 conn2 = DriverManager.getConnection(url, "abs", "abs"); Statement stmt = conn2.createStatement(); ResultSet rset = stmt.executeQuery("select customer_no from abt230"); int count = 0; while(rset.next()) { pstmt.setString(1, "20039999"); pstmt.setInt(2, rset.getInt(1)); // pstmt.addBatch(); // 요거는 standard배치 pstmt.executeUpdate(); count++; if( (count % 10000 ) == 0) { System.out.println(new java.util.Date() + "] " + count ); } } pstmt.executeUpdate(); conn.commit(); } catch (Exception e) { conn.rollback(); e.printStackTrace(); } finally { pstmt.close(); conn.close(); conn2.close(); } } }


위에서 connection과 statement에 batch를 먹였는데 그건 코딩하는 사람 맘일테고,
아참. 그리고 commit mode설정하는거 잊지 마셔야 합니다. 안그러면 1건당
시스템이 소위 뻑났을때 데이터 무결성이 깨질수도 있다는 것만 명심하시면 될것 같슴다.

간략하게나마 하는 방법을 적어드렸는데 맘에 드시는지 모르겠슴다.

자자.. 이제 배치의 세계는 이만으로 하고 다음 아티클은 WAF가 있겠군요..

수고들 하십시오.




하단 문서의 출처 : http://grapro.tistory.com/69
Oracle Batch Update model - batch speedup(3)
 
Standard batch model과 oracle batch model를 비교해본후 오라클의 배치모델에 대한 사용법과 그 설명을 하도록 한다. ( 2003/04/17 ) 593
Written by ienvyou - 최지웅
 
1 of 1
 

이전 아티클에서는 database insert, delete speedup에 대하여 알아보았다. 이번 편에서는 데이터베이스
transation중 가장 비용이 많이 드는 update 문장에 대한 batch처리 방법을 오라클을 기준으로 
이용하여 알아보겠다.

▶ Batch에 대한 이야기를 시작하며

앞서 이미 배치의 기본개념과 JDBC 2.0에 추가되었던 addBatch()메소드와 executeBatch()메소드를
이용하여 driver가 어떻게 작동하는지와 그 API를 사용한 코딩방법에 대하여 살펴보았다.

이미 배치에 관련된 프로젝트한지는 상당히 되었으나, 대부분의 작업은 한쪽의 database의 
select를 한후 다른 database의 insert를 하는 트랜잭션이었다. 
현재 수행하는 프로젝트에서는 적게는 30만건정도에서 많게는 200만건정도의 레코드를
select해온후 각각의 레코드를 이용하여 billing을 하는 경우인데 위에서 말한 30~200만건의
레코드가 바로 update 대상이었다. 

보통 2개의 데이터베이스에서 5개정도의 테이블을 한꺼번에 transaction을 일으킨다는 것은 
쉬운 일이 아니다. 게다가 그중에 update처리를 해야 하는 테이블이 있을 경우 tuning을 하지
않는다면 speed가 현저하게 떨어지는(보통 떨어지는 것이 아니라, 아예 멈춰있는 것처럼 보인다)
것을 금새 느낄 수 있으리라~

사실 JDBC API의 standard batching을 사용할 때 이미 update에 대한 부분까지도 모두 빠른
transaction을 사용할 수 있었다면 이번과 같은 update batching에 대한 글은 insert, delete작업과
함께 끝났어야 했다.

자, 이제 전체를 아우르는 개념설명부터 시작하여 각각의 경우에 대한 처리방법에 대하여
살펴보도록 하자.

▶Update Batching

데이터베이스로의 퍼포먼스 향상 및 불필요한 작업의 수를 줄이려한다면 그냥 생각하기에도
매번 발생하는 INSERT, DELETE, UPDATE처리를 한번의 배치작업으로 처리할 수 있게끔 만드는 것이
최선으로 방법으로 생각이 되어질 수 있을 것이다.
이러한 것을 반영시켜놓은 것이 Sun의 JDBC 2.0 Specification이었는데, 지금 여기서 논하고자 하는
오라클에서는 약간의 용어를 다르게 쓰고 있다.
크게 2가지의 모델을 가져다 쓰는데

1. Standard model
Oracle8i release 8.1.6과 Sun의 JDBC 2.0 Specification을 구현한 것을 말한다.

2. Oracle-specific model
릴리즈 8.1.5부터 지원되기 시작했으며, Sun의 JDBC2.0Spec과는 완전 독립적으로 처리하는 배치를
가르켜 Oracle-specific모델이라 말한다.


note. 여기서 한가지 주의할 점은 위의 두가지 형태를 섞어서 사용할 수 없는데, 소위 또 호기심이
발동해서 사용해보겠다고 하면 해보라. 당장 exception이 발생할테니.. ^^


위에서 모델 두가지를 설명했고 빨간 글씨로 주의를 한번 줬는데 그럼 이렇게 생각할지도 모르겠다.
"놀새야~! 그럼 도대체 뭘가지고 쓰란 말이냐"라고 이야기한다면 대답은 이렇다.
" 상황에 맞게 적절한 걸 가져다 써라" 라는 것인데 해당 기간업무가 펴~~엉생~ 오라클로만 돌아가면
오라클 모델로 사용해도 될테고, 범용성 문제가 제기 된다면 stanard모델을 사용하면 될터이고..

오라클에서 이야기하는 그 상황에 대하여 한번 보자.

  • 오라클모델을 쓰게 되면 이식성은 떨어지지만, 결과적으로는 엄청난 퍼포먼스 향상을 이룰것이다.
  • Standard모델은 이식성은 아주 좋지만, 약간의 성능향상을 이룰수 있을 것이다.
아~ 평생 오라클로 할것인가? 사실 앞선 테스트만 하더라도 standard insert, delete 배치성능은 상당했다.
그런데 자기네 모델을 쓰면 쨉도 안되는다는 것인데 당신이 직접 오라클 모델을 사용하여 테스트하면
아마도 눈이 휘둥그레질것이다

왜냐~ 전에 배치작업을 사용하지 않고 작업할때 밖에서 나가서 커피한잔 마시고, 화장실 갔다오고 해도
안되던 것이 
"엔터키를 한방치고 자리에서 슬슬 일어나려고 할때 이미 트랜잭션작업들이 끝나기때문이다."오라클모델에서 사용하는 statement

일반적으로 반복되는 쿼리에 대하여 값만 바꾸는 형태의 statement는 무엇인가? 그렇다. 
PreparedStatement이다. 
안타깝게도 오라클에서는PreparedStatement밖에 사용하질 못한다.
또한 배치에 관련된 작업은 UPDATE, INSERT, DELETE로 한정할 수 있으며 SELECT에 대한 것은
이미 이전의 article인 select speedup에서 처리하는 것을 보여주었다.


note. 위에서 PreparedStaement만 사용한다고 했는데 CallableStatement도 배치를 지원해주냐고 물으면
" 그런거 구현하려고 한적없다" 라는 대답만 듣게 될 것이다.
오라클배치모델의 특징
  • 배치에 대한 어떠한 세팅도 이루어져 있지 않다면 connection은 자동으로 1건데이터에 대한 각각의 배치처리를 시도하려고 한다.
  • 오라클에서 이야기하는 최상의 퍼포먼스값은 batch작업의 크기가 5에서 30사이일때라고 이야기한다.
  • PreparedStatement에 아무리 열나게 배치를 올리고 있다고 하더라도 COMMIT만 만나면 driver는 아무런 생각없이 바로 sendBatch()라는 request를 데이터베이스로 날려서 현재 올려진 데이터를 update시켜버린다.
당신~ 질문하나 하겠다. JDBC에서 database commit이 언제 날아가는 지 아나? 바로 statement가 닫힐때나
connection이 close될때 자동으로 날아간다. 알고 있었는가? 알고 있었으면 말구~~ ^^

▶본격적인 배치작업

자, 이제 본격적인 오라클모델 배치를 시작하도록 하자. 

1. Connection에 batch value setting하기

처음에 우선 오라클데이터베이스와 연결이 되어진다면 connection객체를 얻게 될 것이다. 여기서부터
배치에 대한 세팅이 들어가져야 하는데 코딩은 아래와 같다.

        ((OracleConnection)conn).setDefaultExecuteBatch(20);

오호. 무엇하는 코드인고? 당연히 오라클 모델의 connection을 불러오는 것이다. 
위의 코드는 간단하면서 객체지향의 polymorphism이 그대로 묻어난 예술적인 코드이다. ^^

즉 우리가 얻어낸 connection이란 놈은 해당 jdbc driver의 super class인 java.sql.Connecdion인터페이스
이다. 그것을 conn.getClass().getName()하게 되면 oracle.jdbc.driver.OracleConnection이란 예쁜 객체를
돌려주게 되는 데 겉껍데기로 싸여있으니 제 구실을 못할 것 같아서 casting을 이용하여
꺼꾸로 끄집어 낸것이다.

거기에는 default배치값을 세팅할 수 있는데 위에서 이야기했던 최상의 퍼포먼스를 낼수 있는
5~30사이의 값을 세팅하여 넣었다.

자, 보통의 코딩에서 connection을 추출해냈다면 당신의 습관적인 다음 코딩은 무엇인가?
바로 connection에서 statement를 뽑아낼 것인데, 위에서도 말했듯이 오라클은  
PreparedStaement만을 지원한다고 했으므로 PreparedStatement를 이용하여 값을 세팅하도록 한다.

2. Statement에 batch value setting하기
  1. 아래의 코딩은 일반적으로 당신도 많이 하는 것일테고 그냥 보자. PreparedStatement ps = conn.prepareStatement ("INSERT INTO dept VALUES (?,?,?)"); ps.setInt (1,12); ps.setString (2,"Carouser"); ps.setString (3,"Korea");
  2. 여기서부터가 중요한데 저렇게 해서 PreparedStaement를 세팅한 후에 다시 오라클배치를 할수 있도록 속성을 끌어내도록 해야 한다. 예제는 OraclePreparedStatement객체를 뽑은 후 setExecuteBatch()메소드안의 기본배치 사이즈를 2개로 잡은 것이다. 아래처럼 잡으면 위의 connection이 잡은 배치사이즈와는 다른 형태인 것임을 알아두어야 한다. ((OraclePreparedStatement)ps).setExecuteBatch(2); 혹여, 현재 PreparedStaement에 배치사이즈를 알고 싶다면 아래처럼 한번 찍어보면 될것이다 System.out.println (" 배치크기 " + ((OraclePreparedStatement)ps).getExecuteBatch());
  3. 자, 이제 어떤 시점에 데이터베이스에 update시켜달라고 요청해야 하는데, 아래처럼 하면 바로 업데이트 될것 같은가? 안된다. 왜냐하면 batchSize를 2로 했기 때문에 그냥 버퍼링한다고 보면 된다. System.out.println ("Number of rows updated so far: " + ps.executeUpdate ());
  4. 자, 아래의 값을 다시 한번 세팅시킨후 다시 executeUpdate()를 수행하면 어찌 될것인가? 위의 PS에서 세팅한 batch value인 2와 같은 수의 데이터가 세팅되어져 있으므로 드디어 Oracle로의 배치작업이 자동으로 시작되게 된다. 와~ 인공지능수준인가? ㅎㅎ, 당신도 가볍게 짤수 있는 코드이긴 하다. ps.setInt (1, 11); ps.setString (2, "ienvyou"); ps.setString (3, "Korea"); int rows = ps.executeUpdate (); System.out.println ("Number of rows updated now: " + rows); ps.close ();
자 간단한 샘플코드 하나 보자.

import! java.sql.*;
import! oracle.sql.*;
import! oracle.jdbc.driver.*;
class  BatchTest{
    public static void main(String[] args) throws Exception{
        Connection conn = null;
        Connection conn2 = null;
        PreparedStatement pstmt= null;
        try {
            Class.forName("oracle.jdbc.driver.OracleDriver");
            String url = "jdbc:oracle:thin:@192.168.0.137:1521:abn";
            conn = DriverManager.getConnection(url, "abs", "abs");
            ((OracleConnection)conn).setDefaultExecuteBatch(30);

            conn.setAutoCommit(false);

            
            pstmt = conn.prepareStatement(" update ABT230 SET update_ymd = ? where customer_no = ?");
            ((OraclePreparedStatement)pstmt).setExecuteBatch(20); // 요거는 오라클배치

            conn2  = DriverManager.getConnection(url, "abs", "abs");
            Statement stmt = conn2.createStatement();
            ResultSet rset = stmt.executeQuery("select customer_no from abt230");
            int count = 0;
            while(rset.next()) {
                pstmt.setString(1, "20039999");
                pstmt.setInt(2, rset.getInt(1));
            //  pstmt.addBatch();   // 요거는 standard배치
                pstmt.executeUpdate();
                count++;
                if( (count % 10000 ) == 0) {
                    System.out.println(new java.util.Date() + "] " + count );
                }
            }
            pstmt.executeUpdate();
            conn.commit();
        } catch (Exception  e) {
            conn.rollback();
            e.printStackTrace();
        } finally {
            pstmt.close();
            conn.close();
            conn2.close();
        }
    }
}

위에서 connection과 statement에 batch를 먹였는데 그건 코딩하는 사람 맘일테고,
아참. 그리고 commit mode설정하는거 잊지 마셔야 합니다. 안그러면 1건당 
시스템이 소위 뻑났을때 데이터 무결성이 깨질수도 있다는 것만 명심하시면 될것 같슴다.

간략하게나마 하는 방법을 적어드렸는데 맘에 드시는지 모르겠슴다.

자자.. 이제 배치의 세계는 이만으로 하고 다음 아티클은 WAF가 있겠군요..

수고들 하십시오.

DBMS/오라클] 로우합치기


MySQL] Row합치기

SELECT GROUP_CONCAT(NAME SEPARATOR ';') FROM USER_TEST GROUP BY GROUPNUM;

출처 : http://xe.issro.net/MySQL/297 




출처 : http://yangwenry.springnote.com/pages/233637



  •   예제쿼리

emp.ename 컬럼, 그리고 그룹번호로 emp.deptno 컬럼을 예로 들어 쿼리를 구성해 보자.
emp 테이블의 deptno, ename 컬럼은 아래와 같다.

10    CLARK
10    KING
10    MILLER
20    ADAMS
20    FORD
20    JONES
20    SCOTT
20    SMITH
30    ALLEN
30    BLAKE
30    JAMES
30    MARTIN
30    TURNER
30    WARD



이것을




10    CLARK,KING,MILLER
20    ADAMS,FORD,JONES,SCOTT,SMITH
30    ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD



요렇게 바꿔보자...


최초에 제시한 두 개의 쿼리들에 대해서,
오라클 개발자인 Tom Kyte가 제시한 쿼리들을 참고용으로 남기도록 하겠습니다.

SELECT     DEPTNO
         , LTRIM (SYS_CONNECT_BY_PATH (ENAME, ','), ',') PATH#
      FROM (SELECT ENAME
                 , DEPTNO
                 , ROW_NUMBER () OVER (PARTITION BY DEPTNO ORDER BY ENAME) RN
                 , COUNT (*) OVER (PARTITION BY DEPTNO) CNT
              FROM EMP)
     WHERE RN = CNT
START WITH RN = 1
CONNECT BY PRIOR RN = RN - 1
       AND PRIOR DEPTNO = DEPTNO













ORA-30004 에러 발생시....대처방법]=================

........ SYS_CONNECT_BY_PATH ( item_nm, ',' ) .......

위 문장이 들어간 쿼리를 실행 시 아래와 같은 오류가 발생항경우
ORA-30004: when using SYS_CONNECT_BY_PATH function, cannot have seperator as part of column value

원인 : item_nm 컬럼에 연결 구분자로 사용하는 ',' 가 존재하여서 발생함해결책 : item_nm 을 replace하던가 아니면 다른 특수 문자로 연결