Top Banner
http://www.ggola.com 4. Application Support.......................................4-4 4.1. SQL*Plus Enhancement...............................4-5 4.1.1....................................Internal Connection 4-5 4.1.2...........................................장장 prompt장 장장 4-6 4.1.3...........................................File Handling 4-9 4.2. SQL Statement.....................................4-12 4.2.1....................................장장장 Merge Statement 4-12 4.2.2..........................................DUAL table장 장장 4-18 4.2.3......................................Regular Expression 4-19 4.2.3.1............................REGEXP_LIKE (Like 장장) 4-19 4.2.3.2...........................REGEXP_INSTR(Instr 장장) 4-21 4.2.3.3.......................REGEXP_REPLACE(Replace 장장) 4-23 4.2.3.4.........................REGEXP_SUBSTR(Substr 장장) 4-24 4.2.3.5........................................장장장 Format 4-25 4.2.4.........................q Operator (quotation장 장장 장장) 4-27 4.3. Partitioned Outer Join............................4-30 4.3.1..............................................Dense Data 4-30 4.3.2.........................Outer Join using Partition By 4-30 4.3.3..................................................Example 4-31 4.4. SQL Model Clause..................................4-43 4.4.1.................................................Overview [email protected] 1
128
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: O10g app support_11

http://www.ggola.com 장 경 상

4. Application Support...............................................................................4-4

4.1. SQL*Plus Enhancement.............................................................4-5

4.1.1. Internal Connection..............................................................4-5

4.1.2. 쉬운 prompt의 변경................................................................4-6

4.1.3. File Handling.........................................................................4-9

4.2. SQL Statement........................................................................4-12

4.2.1. 확장된 Merge Statement.....................................................4-12

4.2.2. DUAL table의 성능...............................................................4-18

4.2.3. Regular Expression.............................................................4-19

4.2.3.1..........................................................REGEXP_LIKE (Like 확장)

4-19

4.2.3.2.......................................................REGEXP_INSTR(Instr 확장)

4-21

4.2.3.3............................................REGEXP_REPLACE(Replace 확장)

4-23

4.2.3.4.................................................REGEXP_SUBSTR(Substr 확장)

4-24

4.2.3.5............................................................................다양한 Format

4-25

4.2.4. q Operator (quotation의 다른 표현)......................................4-27

4.3. Partitioned Outer Join..............................................................4-30

4.3.1. Dense Data........................................................................4-30

4.3.2. Outer Join using Partition By...............................................4-30

4.3.3. Example.............................................................................4-31

4.4. SQL Model Clause...................................................................4-43

4.4.1. Overview............................................................................4-43

4.4.2. Three Groups for Model......................................................4-43

4.4.3. Rules..................................................................................4-44

4.4.4. Other Options.....................................................................4-46

4.4.5. Model Example...................................................................4-48

4.4.5.1...................................................................Test Data 환경 구성

4-48

4.4.5.2............................................................OLAP error 발생 시 처리

4-53

4.4.5.3.........................................................................SQL Model Test

4-55

[email protected] 1

Page 2: O10g app support_11

http://www.ggola.com 장 경 상

4.5. Materialized View....................................................................4-61

4.5.1. 기본 Materialized View 생성.................................................4-61

4.5.2. Query Rewrite....................................................................4-64

4.5.3. Materialized View Management.........................................4-70

4.5.3.1...............................................Check Fast Refreshable MView

4-71

4.5.3.2......................................................................Tuning Procedure

4-72

4.5.4. Partition Change Tracking (PCT).........................................4-74

4.5.4.1....................................................................PCT Enhancement

4-74

4.5.4.2.............................................................................PCT Truncate

4-74

4.5.4.3...............................................................................PCT Refresh

4-74

4.5.5. Other Operation.................................................................4-75

4.5.5.1...................................................................Partition Operation

4-75

4.5.5.2..........................................................................Execution Plan

4-75

4.5.5.3..........................................................................Trusted Option

4-77

4.6. Indexes...................................................................................4-79

4.6.1. IOT(Index Organized Table) Partition..................................4-79

4.6.2. Bitmap Index......................................................................4-82

4.6.3. Update Local Partitioned Index...........................................4-83

4.6.4. Unusable Index...................................................................4-85

4.6.5. Hash Partitioned Global Index............................................4-86

4.7. Scheduling..............................................................................4-94

4.7.1. Advanced Scheduling.........................................................4-94

4.7.2. Scheduler Components......................................................4-94

4.7.3. Basic Scheduler Component 생성.......................................4-95

4.7.4. Calendaring Expressions....................................................4-98

4.7.5. Job Using Program & Schedule.........................................4-100

4.7.6. Advanced Components....................................................4-103

[email protected] 2

Page 3: O10g app support_11

http://www.ggola.com 장 경 상

4.7.6.1....................................................................................Job Class

4-103

4.7.6.2......................................................................................Window

4-105

4.7.7. Scheduler Management...................................................4-107

4.7.7.1...............................................Disable & Enable Components

4-107

4.7.7.2...............................................................................Running Job

4-108

4.7.7.3................................................................Program & Schedule

4-108

4.7.7.4......................................................................................Window

4-109

4.7.7.5................................................Control Component Attributes

4-110

4.7.7.6.......................................................................................Priority

4-112

4.7.7.7.........................................................................Dictionary View

4-113

4.7.8. Using em..........................................................................4-114

4.8. Special Sort...........................................................................4-118

4.8.1. Case, Accent를 무시하는 Sort............................................4-118

4.8.1.1.............................................................................................개요

4-118

4.8.1.2................................................................................Parameters

4-118

4.8.2. Example...........................................................................4-119

4.8.2.1..................................................................................NLS_SORT

4-119

4.8.2.2.................................................................................NLS_COMP

4-120

4.8.2.3....................................................................................NLSSORT

4-121

[email protected] 3

Page 4: O10g app support_11

http://www.ggola.com 장 경 상

4. Application Support

이번 장에서는 oracle10g에서 새롭게 확장된 SQL*Plus 와 분석적(Analytical) SQL을

설명하며 보다 진보된 application을 구현하거나 지원하기 위한 새로운 partition

운영방식과 materialized view, index, scheduling, special sort등의 추가되거나

변화된 개념에 대해 다룬다.

이러한 방식들을 잘 이해하는 것도 중요하겠지만 이들을 application에 얼마나 잘

적용하는가 또는 DBA가 application을 위해 얼마나 잘 구성을 하는가가 보다 더

중요한 부분이다.

일례로 과거부터 oracle이 지원하는 훌륭한 scheduling 기법인 job feature를 제대로

사용하면 아주 유용한 많은 부분들을 OS상에서 처리하는 site들도 많이 있다. 필자가

느끼기에 그렇게 되는 주요한 이유는 job을 활용하는 강점에 대한 이해가 부족하다는

것이다. 만일 그런 site들이 job을 잘 활용해 왔다면 oracle10g로 upgrade를

진행하면서 이번에 확장된 scheduling 기법을 매우 유용한 features로 활용할 수

있었을 것이다.

CF. 현재 잘된다고 그것이 항상 최선이라는 착각을 일으키지 말자. 새로운 기법의

탄생과 변화를 인지하지 못하면 항상 현재가 최선이 될 것이다. Export dumpfile

로부터 DDL scripts를 file로 만들어주는 import option이나, 아주 모양이 좋은 DDL

scripts를 추출해 주는 oracle packages를 몰라서 못쓰면 되겠는가. 알아도 “필요하지

않아서 사용하지 않는다”면 모르지만.

[email protected] 4

Page 5: O10g app support_11

http://www.ggola.com 장 경 상

4.1. SQL*Plus Enhancement

이번에 oracle10g에서 많은 사용자들에게 익숙한(특히나 DBA라면 더더욱) SQL*Plus

의 기능을 확장하여 사용자 편의성을 증대 시켰다.

CF. 주의할 점은 oracle10g임으로 oracle10g client의 sqlplus 실행 file을 가지고 이

기능을 활용해야 한다. Oracle9i의 sqlplus 실행 file을 가지고 oracle10g database

에 접속을 하면 새로운 기능들을 사용할 수 없다는 말이다.

4.1.1.Internal Connection

Oracle9i에서 기존에 사용하던 “internal login”이 제거되면서 DBA 입장에서는 최초

login의 불편함과 shell scripts상에서 많이 사용하던 “connect internal”을 더 이상

사용할 수가 없어서 생기는 문제들로 약간은 짜증스러웠던 것이 사실이다.

이번에 oracle10g에서는 인용부호를 사용하지 않아도 되도록 함으로써 internal

connection에 있어서 약간의 불편함이 해소되었다. 다음의 예를 통해 그 변화를

확인해보자.

[NEWSVC]LIRACLE:/app/oracle> sqlplus "/as sysdba"

SQL*Plus: Release 10.1.0.4.0 - Production on Tue Jul 12 15:02:58 2005

Copyright (c) 1982, 2005, Oracle. All rights reserved.

Connected to:

Oracle Database 10g Enterprise Edition Release 10.1.0.4.0 - Production

With the Partitioning, OLAP and Data Mining options

SQL> exit

Disconnected from Oracle Database 10g Enterprise Edition Release

10.1.0.4.0 - Production

With the Partitioning, OLAP and Data Mining options

[NEWSVC]LIRACLE:/app/oracle> sqlplus / as sysdba

SQL*Plus: Release 10.1.0.4.0 - Production on Tue Jul 12 15:03:03 2005

Copyright (c) 1982, 2005, Oracle. All rights reserved.

[email protected] 5

Page 6: O10g app support_11

http://www.ggola.com 장 경 상

Connected to:

Oracle Database 10g Enterprise Edition Release 10.1.0.4.0 - Production

With the Partitioning, OLAP and Data Mining options

SQL>

CF. 주의할 점은 “/”와 “as” 사이에 반드시 공란이 있어야 한다는 것이다.

4.1.2.쉬운 prompt의 변경이전 oracle version과 달리 prompt의 변경이 매우 쉽게 되었다.

접속 사용자, 권한 심지어 날짜와 connection identifier까지도 아주 간단히 만들 수

있다. 위 예의 환경에 이어서 내용들을 어떻게 하나씩 하는지 확인해보자.

먼저 사용자 이름을 표시해보자.

SQL> set sqlprompt "_user > "

SYS >

이번에는 추가로 login에 사용된 권한을 함께 표시해보자.

SYS > set sqlprompt "_user _privilege> "

SYS AS SYSDBA>

이번엔 원하는 형식의 날짜와 특정 문자들을 함께 표시해 보자. 날짜를 표시하게 되면

현재 시간이 계속적으로 표시되는 장점도 있다.

SYS AS SYSDBA> set sqlprompt "_user':'_privilege 'on' _date> "

SYS:AS SYSDBA on 07/12 15:11>

이번에는 connection identifier까지 추가해보자.

SYS:AS SYSDBA on 07/12 15:11> set sqlprompt "_user':'_privilege 'on' _date

'using' _connect_identifier> "

string beginning ""_user':'_..." is too long. maximum size is 50 characters.

SYS:AS SYSDBA on 07/12 15:12> set sqlprompt "_user':'_privilege 'using'

_connect_identifier> "

SYS:AS SYSDBA using NEWSVC>

위에서 보듯 prompt로 정의되는 글자는 50자까지 가능하다는 것을 알 수 있다. 그래서

[email protected] 6

Page 7: O10g app support_11

http://www.ggola.com 장 경 상

날짜를 제외하고 만들어 보았다.

그러나 대부분의 경우 사용자가 이를 직접 사용하기 보다는 각 oracle client의 모든

사용자에게 global하게 적용하기 위해 일정한 prompt format을 주는 것이

일반적이다. 이를 위해선 glogin.sql을 수정함으로써 적용이 가능하다. 다음은 default

editor와 prompt를 설정하는 예이다.

[NEWSVC]LIRACLE:/app/oracle> vi $ORACLE_HOME/sqlplus/admin/glogin.sql

….

….

….

COLUMN other_tag_plus_exp FORMAT a29

COLUMN other_plus_exp FORMAT a44

-- User defined format

set sqlprompt "_user> "

define _editor=vi

~

~

~

~

:wq

[NEWSVC]LIRACLE:/app/oracle> sqlplus / as sysdba

SQL*Plus: Release 10.1.0.4.0 - Production on Tue Jul 12 15:21:35 2005

Copyright (c) 1982, 2005, Oracle. All rights reserved.

Connected to:

Oracle Database 10g Enterprise Edition Release 10.1.0.4.0 - Production

With the Partitioning, OLAP and Data Mining options

SYS> select * from global_name;

GLOBAL_NAME

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

[email protected] 7

Page 8: O10g app support_11

http://www.ggola.com 장 경 상

NEWSVC

SYS> ed

select * from global_name

/

~

~

~

~

:q

1* select * from global_name

SYS >

위와 같이 각 사용자가 사용하는 oracle client의 glogin.sql을 조절하면 일괄적으로

적용할 수 있다. 물론, sqlplus를 시작하는 위치에 login.sql이 존재하고 이 file에도

적용을 했다면 이 login.sql이 우선한다.

CF. oracle9i 까지는 login.sql이 sqlplus 명령으로 접속할 때 한번만 수행이 되기

때문에 SQL prompt 상에서 connect 명령을 통해 재 접속을 하면 현재의 설정이

그대로 유지되었다. 그러나 이러한 경우엔 위험한 문제가 생길 수 있다. 예를 들어

prompt에 database이름을 표시했는데 connect 명령으로 다른 database로 이동을

하게 되면 prompt에 표시된 database 이름이 변하지 않기 때문에 사용자 실수의

위험이 있다는 말이다. Oracle10g는 매번 접속이 이루어질 때마다 login.sql을

수행하도록 함으로써 이런 위험을 없앴다.

다음은 이런 oracle10g의 새로운 기능을 무시하고 싶은 경우의 해결책이다. 어떤

이유로든 oracle10g sqlplus의 기능을 사용하고 싶지 않다면 다음과 같이 할 수 있다.

SYS> exit

Disconnected from Oracle Database 10g Enterprise Edition Release

10.1.0.4.0 - Production

With the Partitioning, OLAP and Data Mining options

[NEWSVC]LIRACLE:/app/oracle> sqlplus -c 9.2 scott/tiger

SQL*Plus: Release 10.1.0.4.0 - Production on Tue Jul 12 15:40:15 2005

Copyright (c) 1982, 2005, Oracle. All rights reserved.

[email protected] 8

Page 9: O10g app support_11

http://www.ggola.com 장 경 상

Connected to:

Oracle Database 10g Enterprise Edition Release 10.1.0.4.0 - Production

With the Partitioning, OLAP and Data Mining options

_user> exit

Disconnected from Oracle Database 10g Enterprise Edition Release

10.1.0.4.0 - Production

With the Partitioning, OLAP and Data Mining options

[NEWSVC]LIRACLE:/app/oracle>

위에서 보듯 oracle10g의 명령이 무시되어 일반 text로 표시가 되었다.

4.1.3.File Handling

기존의 sqlplus를 사용하면서 현재 사용하고 있는 sql문을 file로 저장하기 위해 save

명령을(혹은 editor를 통해 저장을) 사용했었다. 그러나 이 방법은 각기 다른 연속된

sql을 저장하기 위해선 매번 다른 이름의 file로 저장을 해야 하는 불편이 있었다.

이번에 option이 추가되어 append가 가능하게 되었다.

다음과 같이 여러 sql을 한 file로 저장하는 과정을 살펴보자.

[NEWSVC]LIRACLE:/app/oracle> sqlplus scott/tiger

SQL*Plus: Release 10.1.0.4.0 - Production on Tue Jul 12 15:44:04 2005

Copyright (c) 1982, 2005, Oracle. All rights reserved.

Connected to:

Oracle Database 10g Enterprise Edition Release 10.1.0.4.0 - Production

With the Partitioning, OLAP and Data Mining options

SCOTT> select * from global_name;

GLOBAL_NAME

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

NEWSVC

[email protected] 9

Page 10: O10g app support_11

http://www.ggola.com 장 경 상

SCOTT> save app.sql

Created file app.sql

SCOTT> select sysdate from dual;

SYSDATE

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

12-JUL-05

SCOTT> save app.sql append

Appended file to app.sql

SCOTT> select 'end of file' from dual;

'ENDOFFILE'

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

end of file

SCOTT> save app.sql append

Appended file to app.sql

SCOTT> !cat app.sql

select * from global_name

/

select sysdate from dual

/

select 'end of file' from dual

/

SCOTT> conn system/manager

Connected.

SYSTEM>

CF. 만일 append가 아니라 overwrite를 원하면 “replace” option을 사용하면 되는데

이는 default option임으로 아무런 option을 주지 않으면 overwrite 된다.

CF. connect 명령으로 새로이 connection을 해도 prompt는 항상 최신 정보를

[email protected] 10

Page 11: O10g app support_11

http://www.ggola.com 장 경 상

보여주고 있다.

[email protected] 11

Page 12: O10g app support_11

http://www.ggola.com 장 경 상

참조

==============================================

=================

internal connection : o9i 37p

[email protected] 12

Page 13: O10g app support_11

http://www.ggola.com 장 경 상

4.2. SQL Statement

4.2.1.확장된 Merge Statement

Oracle9i에서 새롭게 탑재된 merge문은 table간의 data를 비교하여 update, insert

를 한 문장으로 처리하는 기능이었다. 이제 oracle10g에서는 이 기능을 조건의 확장과

delete절의 추가를 통해 더욱 유용하게 쓸 수 있도록 하였다.

Oracle9i에서는 이를 “upsert”라고 부르기도 했으며 이 절의 주된 의미는 조건을

표시하는 “on”절이 참이면 update를 아니면 insert를 하는 것이었고 특히 data

warehousing같은 시스템에서 data처리를 할 때 유용한 기능이었다.

이제 어떤 변화가 있는지 살펴보기 위하여 테스트 환경을 먼저 만들어 보자. 아래 두

table은 sports 항목과 상태를 다루는 code성 table 그리고 sports 항목별 lesson

비용과 경쟁대회가 열리는 계절 및 해당 운동과 관련한 용품에 대한 정보를 제공하는

연락처를 표시한 table이다.

운동 code 3자리는 I/O/X (Indoor, Outdoor, BOTH)와 B/N(Ball sports, Non-Ball

sports) 그리고 ?(숫자-sequence)로 구성이 되어있다.

[NEWSVC]LIRACLE:/app/oracle> cd temp

[NEWSVC]LIRACLE:/app/oracle/temp> sqlplus scott/tiger

SQL*Plus: Release 10.1.0.4.0 - Production on Wed Jul 13 11:10:13 2005

Copyright (c) 1982, 2005, Oracle. All rights reserved.

Connected to:

Oracle Database 10g Enterprise Edition Release 10.1.0.4.0 - Production

With the Partitioning, OLAP and Data Mining options

SCOTT> create table sports (

2 sp_code varchar2(3), sp_name varchar2(30), sp_stat varchar2(1));

Table created.

[email protected] 13

Page 14: O10g app support_11

http://www.ggola.com 장 경 상

SCOTT> insert into sports values ('IB1', 'SQUASH', 'Y');

1 row created.

SCOTT> insert into sports values ('IB2', 'RACKETBALL', 'N');

1 row created.

SCOTT> insert into sports values ('OB1', 'BASEBALL', 'Y');

1 row created.

SCOTT> insert into sports values ('OB2', 'TENNIS', 'Y');

1 row created.

SCOTT> insert into sports values ('OB3', 'SOCKER', 'Y');

1 row created.

SCOTT> insert into sports values ('XN1', 'SWIMMING', 'N');

1 row created.

SCOTT> insert into sports values ('XB1', 'BASKETBALL', 'N');

1 row created.

SCOTT> commit;

Commit complete.

SCOTT> select * from sports;

SP_ SP_NAME S

[email protected] 14

Page 15: O10g app support_11

http://www.ggola.com 장 경 상

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

IB1 SQUASH Y

IB2 RACKETBALL N

OB1 BASEBALL Y

OB2 TENNIS Y

OB3 SOCKER Y

XN1 SWIMMING N

XB1 BASKETBALL N

7 rows selected.

SCOTT> create table sports_inform (

2 sp_code varchar2(3), sp_price number default 0,

3 sp_season varchar2(10), sp_tel varchar2(20));

Table created.

SCOTT> insert into sports_inform values ('IB1', 100000, 'SPRING', '080-

2134-1211');

1 row created.

SCOTT> insert into sports_inform values ('OB1', 200000, 'SUMMER', '060-

2197-2384');

1 row created.

SCOTT> insert into sports_inform values ('OB2', 150000, 'FALL', '031-976-

3842');

1 row created.

SCOTT> commit;

Commit complete.

[email protected] 15

Page 16: O10g app support_11

http://www.ggola.com 장 경 상

SCOTT> select * from sports_inform;

SP_ SP_PRICE SP_SEASON SP_TEL

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

IB1 100000 SPRING 080-2134-1211

OB1 200000 SUMMER 060-2197-2384

OB2 150000 FALL 031-976-3842

SCOTT>

이제 스포츠 종목과 상태를 담고 있는 table과 그 중에서 일부 항목에 대한 정보를 담고

있는 table이 생성되었다. 이를 토대로 테스트를 진행해 보자.

1. merge문으로 작성하는 update, insert 구문에 where condition을 통해 보다

세밀하게 조절이 가능해졌다. 다음의 예는 sports와 sports_inform을 비교하여

일치하는 항목이 있으면 sports_inform의 lesson 비용을 2배로 늘리고 없으면 insert

를 하되 insert를 모두 상태정보 status =’Y’인 경우에 한하여 이를 허용하도록 한다.

형식은 다음과 같다.

merge into target_table(DML)

using source_table

on (join condition)

when matched then

update set column_name = value

where condition

when not matched then

insert (column list) values (value

list)

where condition;

시나리오는 다음과 같다. 현재 sports table에 있는 항목 중 상태가 ‘Y’인 것 중에서

sports_inform에 있는 항목은 lesson비용을 두 배로 늘리고 없는 항목은 기본 정보를

가지고 새로 sports_inform에 추가하는 것이다. 다양한 case를 위하여

sports_inform에 있는 항목 중 ‘IB1’의 상태를 ‘N’로 바꾸고 해보자.

SCOTT> update sports set sp_stat = 'N' where sp_code = 'IB1';

[email protected] 16

Page 17: O10g app support_11

http://www.ggola.com 장 경 상

1 row updated.

SCOTT> merge into sports_inform si

2 using sports s

3 on (s.sp_code = si.sp_code)

4 when matched then

5 update set si.sp_price = si.sp_price*2

6 where s.sp_stat = 'Y'

7 when not matched then

8 insert (si.sp_code, si.sp_season, sp_tel) values (s.sp_code, 'N/A', '114')

9 where s.sp_stat = 'Y';

3 rows merged.

SCOTT> select * from sports_inform;

SP_ SP_PRICE SP_SEASON SP_TEL

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

IB1 100000 SPRING 080-2134-1211

OB1 400000 SUMMER 060-2197-2384

OB2 300000 FALL 031-976-3842

OB3 0 N/A 114

SCOTT> commit;

Commit complete.

결과를 보면 ‘IB1’은 상태가 ‘N’이기 때문에 즉, matched는 되었지만 where절의

조건에 따라 상태가 ‘N’이어서 금액의 변화가 없고 나머지 항목은 금액이 두 배로

늘어났다. 또한 sports에 있는 항목 중 상태가 ‘Y’이고 sports_inform에 없는 ‘OB3’는

새로이 sports_inform에 추가되었다.

2. 두 번째 기능은 delete절을 사용하는 것이다. 이는 table data의 cleansing을 위해

특정 조건의 data를 merge operation진행 중에 삭제를 하는 기능이다. 두 번째

시나리오는 다음과 같다.

[email protected] 17

Page 18: O10g app support_11

http://www.ggola.com 장 경 상

첫 번째와 거의 유사하지만 update를 진행하면서 where 조건에 해당하는 data를

삭제하고 insert를 할 때는 where절에 상관이 없이 unmatched data 전체를 insert

하는 것이다. 즉, sports에서 상태가 ‘N’인데 sports_inform에 있는 data는 delete

하고 sports_inform에 없고 sports에만 있는 data는 상태와 상관이 없이 모두 insert

를 하는 내용이다. 따라서 최종적으로 sports_inform에서 ‘IB1’은 삭제될 것이고

sports에서 sports_inform에 없는 ‘IB2’, ‘XN1’, ‘XB1’은 모두 새로 추가될 것이다.

조건에 상관이 없이 추가가 되도록 sports에서 ‘XN1’의 상태를 ‘Y’로 바꾸고

진행해보자.

SCOTT> update sports set sp_stat = 'Y' where sp_code = 'XN1';

1 row updated.

SCOTT> merge into sports_inform si

2 using sports s

3 on (s.sp_code = si.sp_code)

4 when matched then

5 update set si.sp_price = si.sp_price/2

6 delete where (s.sp_stat = 'N')

7 when not matched then

8 insert (si.sp_code, si.sp_season, sp_tel) values (s.sp_code, 'N/A', '114');

7 rows merged.

SCOTT> select * from sports_inform;

SP_ SP_PRICE SP_SEASON SP_TEL

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

OB1 200000 SUMMER 060-2197-2384

OB2 150000 FALL 031-976-3842

OB3 0 N/A 114

IB2 0 N/A 114

XN1 0 N/A 114

XB1 0 N/A 114

[email protected] 18

Page 19: O10g app support_11

http://www.ggola.com 장 경 상

6 rows selected.

SCOTT> select * from sports;

SP_ SP_NAME S

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

IB1 SQUASH N

IB2 RACKETBALL N

OB1 BASEBALL Y

OB2 TENNIS Y

OB3 SOCKER Y

XN1 SWIMMING Y

XB1 BASKETBALL N

7 rows selected.

SCOTT> commit;

Commit complete.

전체 sports 항목 7개 중에서 삭제된 ‘IB1’을 제외하고 모두 sports_inform table에

나타나고 있다. 만일, 이 기능을 merge가 없이 진행을 하려고 했다면 delete, update,

insert의 3가지 SQL을 모두 사용해야 했을 것이다.

4.2.2.DUAL table의 성능Oracle database를 사용하는 사람들 중 특수 table “DUAL”을 써보지 않은 사람은

없을 것이다. 즉, table과 상관없이 필요한 data를 만들기 위해서 dual을 쓰는 경우는

매우 많다는 것이다. Oracle9i까지 이 dual은 특정 table로서 항상 full table scan이

이루어지도록 되어 있었고 특별한 tuning도 할 수가 없었다. 그러나 oracle10g의

optimizer는 이를 특별한 plan으로 만들어 그 성능을 향상시키고 있다.

솔직히 측정을 하기가 어렵긴 하지만 dual을 많이 사용하는 application의 성능이

매우 좋아졌다는 것이 오라클의 주장이다. 다음의 예는 plan의 변화를 보여준다.

Oracle9i에서는 다음과 같은 plan을 보여주지만

Rows Execution Plan

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

[email protected] 19

Page 20: O10g app support_11

http://www.ggola.com 장 경 상

0 SELECT STATEMENT GOAL: CHOOSE

1 TABLE ACCESS (FULL) OF 'DUAL'

Oracle10g부터는 아래와 같이 변경이 되었다.

Rows Execution Plan

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

0 SELECT STATEMENT MODE: ALL_ROWS

0 FAST DUAL

4.2.3.Regular Expression

Oracle10g는 과거의 단순한 test search기능을 확장하여 다양한 형식을 지원한다.

이를 REGEXP(REGular EXPression)이라 하여 internal function의 prefix로

사용한다.

EX. REGEXP_LIKE(string, pattern, [c|i|n|m])

Argument는 source string, 찾고자 하는 pattern, 찾는 option을 설정한다.

1. pattern : 찾고자 하는 pattern을 정의할 때 사용되는 특수문자는 일반적으로

사용되는 것들이다. 즉, “^”는 처음 시작위치를 “$”는 마지막 끝나는 위치를 “()”에는

원하는 string list를 “[]”는 range를 표시할 때 주로 사용한다.

2. match_parameter : “c” (case sensitive)가 default로 대소문자를 구분한다.

대소문자를 구분하지 않는 “i”는 case-insensitive를 뜻하며 “n”은 period “.”를

newline character와 match시킨다는 의미이며, “m”은 source string이 multiple

line으로 구성되었다고 판단한다는 option이다.

4.2.3.1. REGEXP_LIKE (Like 확장)

다음의 간단한 예를 통하여 보다 세밀해진 like(?)를 보자. 이 예는 운동종목의 이름이

“BALL’로 끝나는 것 중에서 “BAS” 또는 “RAC”로 시작하고 중간에 “E” 또는 “KET”가

들어가 있는 것을 찾는 방법이다.

SCOTT> select * from sports where regexp_like(sp_name, '^(BAS|RAC)(e|

KET)BALL$');

SP_ SP_NAME S

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

IB2 RACKETBALL N

XB1 BASKETBALL N

원하는 결과 중에서 “BASEBALL”이 빠졌다. 그것은 default로 적용된 match option

[email protected] 20

Page 21: O10g app support_11

http://www.ggola.com 장 경 상

“c” 즉, 대소문자 구분의 영향이다. 다시 해보자.

SCOTT> select * from sports where regexp_like(sp_name, '^(BAS|RAC)(e|

KET)BALL$', 'i');

SP_ SP_NAME S

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

IB2 RACKETBALL N

OB1 BASEBALL Y

XB1 BASKETBALL N

예민한 사람이면 위 조건에서 서두문자 “^”를 없애도 되지 않는가 하고 생각할지도

모른다. 물론 현재 상태로서는 없어도 된다. 다음을 보자.

SCOTT> select * from sports where regexp_like(sp_name, '(BAS|RAC)(e|KET)BALL$',

'i');

SP_ SP_NAME S

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

IB2 RACKETBALL N

OB1 BASEBALL Y

XB1 BASKETBALL N

그러나 만일 다른 data가 들어 있었다고 가정해보자. 예를 들어 “BASEBALL”과 비슷한

“ABASEBALL”이 존재했다고 가정해보자. 그러면 “^”의 차이는 확실하게 들어난다.

다시 확인해 보자.

SCOTT> insert into sports values ('XX1', 'ABASEBALL', 'N');

1 row created.

SCOTT> select * from sports where regexp_like(sp_name, '(BAS|RAC)(e|KET)BALL$',

'i');

SP_ SP_NAME S

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

IB2 RACKETBALL N

OB1 BASEBALL Y

[email protected] 21

Page 22: O10g app support_11

http://www.ggola.com 장 경 상

XX1 ABASEBALL N

XB1 BASKETBALL N

SCOTT> select * from sports where regexp_like(sp_name, '^(BAS|RAC)(e|

KET)BALL$', 'i');

SP_ SP_NAME S

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

IB2 RACKETBALL N

OB1 BASEBALL Y

XB1 BASKETBALL N

SCOTT> rollback;

Rollback complete.

한가지 더 유용한 정보로 “range”를 주는 것이 있다. 다음은 운동종목 코드가 “OB”로

시작하는 것 중에서 마지막 값이 “1”에서 “2”까지 것만 출력하는 예이다. 이런 range를

주기 위해선 “[]”를 사용한다.

SCOTT> select sp_code from sports;

SP_

-----

IB1

IB2

OB1

OB2

OB3

XN1

XB1

7 rows selected.

SCOTT> select sp_code from sports where regexp_like(sp_code, '^OB[1-2]');

[email protected] 22

Page 23: O10g app support_11

http://www.ggola.com 장 경 상

SP_

-----

OB1

OB2

4.2.3.2. REGEXP_INSTR(Instr 확장)

이 function은 현재 사용되고 있는 instr function의 기능을 확장하여 다양한 pattern

과 case sensitive에 대한 선택 등을 할 수 있게 해준다.

먼저, 일반적인 instr function의 예를 보자. 다음은 character ‘BASEBALL’에서

character ‘A’의 위치를 찾는데 첫 번째 SQL은 세 번째 character부터 ‘A’를 만나는

첫 번째 위치를, 두 번째 SQL은 두 번째 character부터 ‘A’를 만나는 첫 번째 위치를

return 한다.

SCOTT> select instr(sp_name, 'A', 3, 1)

2 from sports where sp_name = 'BASEBALL';

INSTR(SP_NAME,'A',3,1)

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

6

SCOTT> select instr(sp_name, 'A', 2, 1)

2 from sports where sp_name = 'BASEBALL';

INSTR(SP_NAME,'A',2,1)

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

2

EX. REGEXP_INSTR(string, pattern, position, occurrence, return_option[0|1],

[c|i|n|m])

기본 방식은 기존의 Instr function과 동일하지만 마지막 4, 5번째 option이

추가되었다. 마지막 5번째는 앞서 배운 match_parameter와 동일하며 4번째 option

은 숫자 0 또는 1을 선택하는데 default인 0은 찾아낸 string의 첫 번째 character의

position을, 1은 찾아낸 character의 마지막 다음 position을 return한다. 예를 들어

“AAB BB”에서 “AB”를 찾았고 return_option을 0 또는 지정하지 않으면 2를 1을 주게

되면 마지막 위치 다음인 4를 return하게 된다.

CF. 실제 작업을 해보면 5번째 option을 지정하는 경우에는 반드시 4번째 option을

[email protected] 23

Page 24: O10g app support_11

http://www.ggola.com 장 경 상

지정해야 했다. 그렇지 않으면 ‘invalid number’오류 메시지가 나왔다) 물론, 4번째

option만 주고 5번째 option을 주지 않으면 5번째 option의 default인 대소문자

구분이 적용되었다. (4, 5번째 option을 모두 적용하지 않는 것은 상관없다. 그렇다면

default인 < 0, ‘c’ > 가 적용될 것이다)

다음은 4번째 option을 1로 즉, 찾아낸 string의 마지막 위치의 다음 위치( + 1)를

return하는 예이다. 새로운 regular expression을 통해 첫 번째 SQL은 하나 이상의

blank “[blank]+”로 시작하는 문자를 1번째 위치부터 찾아서 3번째에 있는 blank를

찾은 후 그 다음위치를 return하며 두 번째 SQL은 하나 이상의 blank로 시작하지 않는

“[^blank]+” 3번째 문자열의 마지막 위치의 다음 위치를 return한다.

SCOTT> select regexp_instr('Anymall autumn, ball Aux, Basket any', '[ ]+',

1, 3, 1)

2 from dual;

REGEXP_INSTR('ANYMALLAUTUMN,

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

22

SCOTT> select regexp_instr('Anymall autumn, ball Aux, Basket any', '[^ ]+',

1, 3, 1)

2 from dual;

REGEXP_INSTR('ANYMALLAUTUMN,

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

21

첫 번째 SQL은 21번째 위치에 있는 3번째 blank의 다음 위치 22를, 두 번째 SQL은

blank로 시작하지 않는 3번째 string인 ‘ball’의 마지막 위치인 20의 다음위치인 21을

return하였다. 다음은 좀 더 복잡한 예이다. (“Anymall 대신 Cnymall”을 사용하고

있다)

SCOTT> select regexp_instr('Cnymall autumn, ball Aux, Basket any',

2 '[A|b][[:alpha:]]{5}', 1, 2, 0, 'i') from dual;

REGEXP_INSTR('CNYMALLAUTUMN,

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

[email protected] 24

Page 25: O10g app support_11

http://www.ggola.com 장 경 상

27

SCOTT> select regexp_instr('Cnymall autumn, ball Aux, Basket any',

2 '[A|b][[:alpha:]]{5}', 1, 2, 0, 'c') from dual;

REGEXP_INSTR('CNYMALLAUTUMN,

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

0

이 SQL은 “A” 혹은 “B”로 시작하여 알파벳 5자리이상 붙어있는 문자 중 두 번째

존재하는 문자열의 첫 번째 위치를 대소문자 구분 없이 return한 예이다.

마지막 4개의 parameter를 다시 표현하면 < 1, 2, 0, 'i' 첫 번째 위치에서 찾기

시작, 두 번째 일치하는 문자, 찾은 문자의 첫 번째 position, 대소문자 구분 없이 > 이

된다. 위 문장에서 조건에 맞는 data는 “autumn과 Basket”인데 두 번째 위치에 있는

“Basket”의 “B”의 position이 return되었다. 두 번째 SQL에서 보듯 대소문자를

구분하도록 하면 [A|b]로 시작해야 함으로 아무것도 찾을 수 없다. 물론, [a|B]로 하면

동일한 결과를 얻을 수 있을 것이다.

4.2.3.3. REGEXP_REPLACE(Replace 확장)

기본적인 내용은 원래의 replace function과 regexp_instr의 조합으로 볼 수 있다.

EX. REGEXP_REPLACE(string, pattern, replace_string, start_position,

occurrence, [c|i|n|m])

간단하게 예를 통해 이해해보자. 다음은 “-“로 구분된 숫자 형식의 전화번호를 부분

부분별로 잘라서 “\n”으로 match하여 원하는 format으로 출력하는 내용이다.

SCOTT> select sp_tel from sports_inform where sp_code = 'OB2';

SP_TEL

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

031-976-3842

SCOTT> select regexp_replace(sp_tel,

2 '([[:digit:]]{3})\-([[:digit:]]{3})\-([[:digit:]]{4})','(\1) [\2-\3]')

3 from sports_inform where sp_code = 'OB2';

REGEXP_REPLACE(SP_TEL,'([[:DIG

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

[email protected] 25

Page 26: O10g app support_11

http://www.ggola.com 장 경 상

(031) [976-3842]

다음은 이와 유사하게 한 문자씩 대응하여 변경시켜 출력하는 예이다.

SCOTT> select sp_season from sports_inform where sp_code = 'OB1';

SP_SEASON

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

SUMMER

SCOTT> select regexp_replace(sp_season, '(.)', '[\1]')

2 from sports_inform where sp_code = 'OB1';

REGEXP_REPLACE(SP_SEASON,'(.)'

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

[S][U][M][M][E][R]

4.2.3.4. REGEXP_SUBSTR(Substr 확장)

사실 이 기능은 substr의 숫자로 잘라내는 것과 달리 pattern 문자열로 잘라내는

것이기 때문에 regexp_instr과 substr의 조합이라고 하는 것이 더 어울릴 것 같다.

EX. REGEXP_SUBSTR(string, pattern, position, occurrence, [c|i|n|m])

다음의 예는 앞서 regexp_instr으로 찾아낸 문자열 “Basket”의 position을 substr을

통해 문자열로 return하는 것이다.

SCOTT> select regexp_substr('Cnymall autumn, ball Aux, Basket any',

2 '[A|b][[:alpha:]]{5}', 1, 2, 'i') from dual;

REGEXP_SUBSTR('CNYMALLAUTUMN,

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

Basket

문장의 변화는 거의 없지만 position이 아니라 string이 return되었다. 다음은 “,”와 “,”

사이에 있는 첫 번째 문자열을 찾는 경우이다. 테스트를 위해 문자열에 약간의 변화를

주었다.

SCOTT> select regexp_substr('Cnymall autumn,ballAux, Basket any',

2 ',[[:alpha:]]+,') from dual;

REGEXP_SUBSTR('CNYMALLAUTUMN,

[email protected] 26

Page 27: O10g app support_11

http://www.ggola.com 장 경 상

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

,ballAux,

위의 예는 “,”와 “,”사이에 알파벳만 있는 문자열을 return하고 있다. 다음은 긴 URL

에서 main site만 잘라내는 예이다. URL의 특성상 “http://” 시작하는 알파뉴메릭

(alphanumeric characters ) 문자열 중 “.”으로 끝나거나 “.”이 없는 문자열 최소 3개

이상 최대 4개를 찾고 마지막 찾은 문자열 끝이 “/”로 끝나거나 혹은 없는 것만

잘라내는 것이다.

SCOTT> select

regexp_substr('http://www.ggola.com/technology/global/kr/index.html',

2 'http://([[:alnum:]]+\.?){3,4}/?') from dual;

REGEXP_SUBSTR('HTTP://WWW.GGOL

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

http://www.ggola.com/

4.2.3.5. 다양한 Format

지금까지 살펴본 regular expression의 생명은 얼마나 다양한 pattern을 생각해 내고

사용하느냐에 있다. 다음은 그 다양한 형식을 설명한 내역이다. 이 부분은 한글화로 할

경우 너무 오해의 소지가 많기 때문에 원래 설명 그대로를 싣는다.

Operatio

n

Description

\a The backslash character can have four different meanings

depending on the context. It can:

* Matches zero or more occurrences

+ Matches one or more occurrences

? Matches zero or one occurrence

| Alternation operator for specifying alternative matches

^b Matches the beginning-of-line character

$b Matches the end-of-line character

.c Matches any character in the supported character set except

NULL

[ ]d Bracket expression for specifying a matching list that should

match any one of the expressions represented in the list. A

nonmatching list expression begins with a circumflex (^) and

specifies a list that matches any character except for the

[email protected] 27

표 4-1

Regexp Format

Page 28: O10g app support_11

http://www.ggola.com 장 경 상

expressions represented in the list.

( ) Grouping expression, treated as a single subexpression

{m} Matches exactly m times

{m,} Matches at least m times

{m,n} Matches at least m times but no more than n times

\ne The backreference expression (n is a digit between 1 and 9)

matches the nth subexpression enclosed between '(' and ')'

preceding the \n

[..]f Specifies one collation element, and can be a multicharacter

element (for example, [.ch.] in Spanish)

[: :]g Specifies character classes (for example, [:alpha:]). It matches

any character within the character class.

[==]h Specifies equivalence classes. For example, [=a=] matches all

characters having base letter 'a'.

다음은 특수한 표현인 “[]”의 설명이다.

Operation Description

[:alnum:] All alphanumeric characters

[:alpha:] All alphabetic characters

[:blank:] All blank space characters.

[:cntrl:] All control characters (nonprinting)

[:digit:] All numeric digits

[:graph:] All [:punct:], [:upper:], [:lower:], and [:digit:] characters.

[:lower:] All lowercase alphabetic characters

[:print:] All printable characters

[:punct:] All punctuation characters

[:space:] All space characters (nonprinting)

[:upper:] All uppercase alphabetic characters

[:xdigit:] All valid hexadecimal characters

4.2.4.q Operator (quotation의 다른 표현)

현재 여러분이 사용하고 있는 문자열에 quotation을 사용하는 경우 그것이 문자열이냐

아니면 인용부호이냐를 구분하기 위해 두 개의 quotation을 연달아 사용하여 이를

구분하곤 했다. 이는 한 column에 여러 개의 quotation을 사용하고 싶은 경우 매우

불편하여 사용상의 어려움이 많았다. 실수할 여지도 많았고. Oracle10g가 제공하는

[email protected] 28

표 4-2

Regexp 중괄호 용법

Page 29: O10g app support_11

http://www.ggola.com 장 경 상

새로운 operator “q”는 이런 불편함을 해소시켜 준다.

SCOTT> select 'I''m Ggola. You''re Jane' from dual;

'I''MGGOLA.YOU''REJANE'

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

I'm Ggola. You're Jane

위 문장은 quotation 2개를 하나의 문자열로 표시하기 위하여 두 번씩 2회 quotation

을 적용한 전형적인 표현형식이다. 다음은 이것을 oracle10g에서 어떻게 쉽게 할 수

있는지 보여준다.

SCOTT> select q'xI'm Ggola. You're Janex' from dual;

'I''MGGOLA.YOU''REJANE'

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

I'm Ggola. You're Jane

Quotation을 한번만 사용하고도 잘 표현이 된다. 그런데 무언가 지저분하고 복잡해

보인다. 다시 다른 표현을 사용해 보자.

SCOTT> select q'[I'm Ggola. You're Jane]' from dual;

'I''MGGOLA.YOU''REJANE'

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

I'm Ggola. You're Jane

이제 좀 이해가 될 것이다. 새로운 operation “q”는 “pair”로 지정된 문자열 사이의

모든 것을 무조건 하나의 string으로 return해주는 역할을 해준다. 지정된 pair문자가

기호가 아닌 일반 문자라도 마찬가지라는 뜻이다. 따라서 일반 문자 혹은 pair 기호인

“[], (), {}, <>”를 사용해도 된다.

SCOTT> select q'!I'm Ggola. You're Jane!' from dual;

'I''MGGOLA.YOU''REJANE'

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

I'm Ggola. You're Jane

SCOTT> select q'@I'm Ggola. You're Jane@' from dual;

[email protected] 29

Page 30: O10g app support_11

http://www.ggola.com 장 경 상

'I''MGGOLA.YOU''REJANE'

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

I'm Ggola. You're Jane

SCOTT> select q'iI'm Ggola. You're Janei' from dual;

'I''MGGOLA.YOU''REJANE'

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

I'm Ggola. You're Jane

SCOTT> select q'eI'm Ggola. You're Janee' from dual;

'I''MGGOLA.YOU''REJANE'

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

I'm Ggola. You're Jane

SCOTT> select q'II'm Ggola. You're JaneI' from dual;

select q'II'm Ggola. You're JaneI' from dual

*

ERROR at line 1:

ORA-00923: FROM keyword not found where expected

위에서 보듯 pair만 맞추어 주면 원하는 결과를 얻을 수 있지만 마지막 SQL문의 error

에서처럼 문자열의 시작 character와 q operator의 문자가 같은 경우는 error가

return되었다.

[email protected] 30

Page 31: O10g app support_11

http://www.ggola.com 장 경 상

OCP point

==============================================

=================

1. merge문의 on절과 matched(update), unmatched(insert)문장 사용법 이해

2. q operator 사용법

참조

==============================================

=================

merge : o9i 367p

[email protected] 31

Page 32: O10g app support_11

http://www.ggola.com 장 경 상

4.3. Partitioned Outer Join

4.3.1.Dense Data

대용량 database를 가지고 특정 report를 구성하는 시스템에서 fact table(실 data)

과 dimension table(차원 data)간의 관계는 매우 흔한 방식이다. 그러나 특정한 case

즉, fact table에 존재하지만 dimension의 조합으로 나타나지 않는 경우 이를

표현하는 방식은 매우 다양할 수 있다. 이런 data를 흔히 “Dense Data”라 한다. 만일,

fact table에 이런 dense data를 표현하기 위해 실 data값은 없어도 dimension만

갖는 rows가 존재한다면 이는 join을 통해 매우 쉽게 구성할 수 있을 것이다. 그러나

실제 data를 구축하는 과정에서 불필요하게 dimension만 있는 fact data를 만드는

일은 없을 것이며 있다 하더라도 그것은 작업자가 직접 insert를 해야 하는 일일 것이다.

다시 말하면, 국가별로 전기 히터를 판매한 기록을 날짜 별 report로 볼 때 적도에 있는

나라들은 판매량이 없을 터이니 나타나지 않을 것이다. 사용자가 이를 보고 싶다면

outer join을 통해 빈 row를 넣든지 아니면 fact table에(판매량 table)에 판매량이

없는 적도 국가들도 빈 row로 넣어주어야 한다는 말이다. 그러나 outer join의 경우

dimension의 차원에 의해 join을 함으로 없는 날짜는 나타나도 국가를 표현할 수는

없고 빈 row를 넣는다는 것은 사용자가 매번 작업을 해야 하며 작업 시점에 따라 틀린

결과를 가져올 수도 있는 문제가 있다.

4.3.2.Outer Join using Partition By

앞서 설명한 dense data를 표현하기 위해 가상의 data를 만들고 SQL function으로

LAG, LEAD와 같은 것들을 사용하는 등 복잡하고 성능이 떨어지는 SQL문을 사용하는

것은 매우 불편한 일이었다. 그러나 지금 소개하는 oracle10g의 partitioned outer

join을 사용하면 그 성능이 매우 우수하며 논리적으로 이해가 쉬운 문장을 만들 수 있다.

예를 들어 1개의 column 가진 dimension table에 10가지 data가 있고 fact table

에는 dimension의 동일 차원에 해당되는 data가 12가지가 있다고 해보자. 이를

가지고 report를 만든다고 가정하자. 그리고 fact table중 dimension과 match되는

data 가 7가지라면 fact table의 5가지 data는 report되지 않을 것이다. 하지만

사용자 요구사항은 dimension은 모두 표시가 되어야 한다. 그래야 없는 dimension도

볼 수 있고 존재하는 data와의 비교가 가능해지기 때문이다. 이런 경우에 partitioned

outer join을 사용하면 outer join에서 지정하는 partition별로 dimension에

해당하는 outer join을 각각 만들어주기 때문에 원하는 dimension별로 모든 fact

data를 하나의 report로 출력할 수 있다.

[email protected] 32

Page 33: O10g app support_11

http://www.ggola.com 장 경 상

CF. 간단히 정의하면 partitioned outer join은 outer join을 partition by로 지정한

column별로 각각 수행하는 역할을 해준다고 하겠다.

CF. 이 기능은 ANSI, ISO SQL Standard(표준) 이다.

4.3.3.Example

실례를 보면 이해하기가 쉽다. 먼저 테스트를 위한 환경 data를 만들어 보자.

COTT> conn sys/manager

Connected.

SYS> grant execute on dbms_lock to scott;

Grant succeeded.

SYS> conn scott/tiger

Connected.

SCOTT> alter session set nls_date_format = 'YYYYMMDD';

Session altered.

SCOTT> create table ptj1 (sale_date varchar2(8));

Table created.

SCOTT> select sysdate from dual;

SYSDATE

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

20050718

SCOTT> begin

2 for i in 0..10 loop

3 insert into ptj1 values (to_char(sysdate + i, 'YYYYMMDD'));

4 end loop;

5 end;

6 /

[email protected] 33

Page 34: O10g app support_11

http://www.ggola.com 장 경 상

PL/SQL procedure successfully completed.

SCOTT> select * from ptj1;

SALE_DAT

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

20050718

20050719

20050720

20050721

20050722

20050723

20050724

20050725

20050726

20050727

20050728

11 rows selected.

먼저, sys user로 scott에게 나중에 사용할 sleep function을 위한 권한을 부여했다.

그리고 나서 dimension 역할을 하는 ptj1 table을 만들어 오늘 날짜부터 10일 후까지

총 11일의 time dimension data를 구성하였다. 다음은 fact table을 만든다. 이

table은 스포츠 항목과 날짜에 따른 판매량을 갖는다고 가정하며 두 가지 형태의 data

를 만들 것이다. 첫 번째는 sports table에서 상태가 ‘Y’인 스포츠에 대하여 오늘

날짜부터 3일 후의 날짜를 갖는 data를 loop를 11회 돌며(“오늘날짜 + 3일 후 +

loopcount”) 가상의 판매량을 넣어 입력하였고 두 번째는 동일한 방식이지만 sports

table의 상태가 ‘Y’인 것과 스포츠 항목이 ‘SQUASH’인 data에 대하여 날짜를 역으로

“-“하여(“오늘날짜 – 1일 – loopcount”)를 1000회 돌려 구성한다.

SCOTT> create table sports_sold (

2 sp_id varchar2(3), sale_date varchar2(8), sp_volumn number);

Table created.

SCOTT> !more ptj_env1.sql

[email protected] 34

Page 35: O10g app support_11

http://www.ggola.com 장 경 상

declare

cursor cur_item is

select sp_code from sports where sp_stat = 'Y' ;

begin

for vs_item in cur_item loop

for i in 0..10 loop

insert into sports_sold values

(vs_item.sp_code, to_char(sysdate + i + 3, 'YYYYMMDD'),

(i + 1) * to_number(to_char(sysdate + i, 'SSSSS'))

);

dbms_lock.sleep(0.5);

end loop;

commit;

end loop;

end;

/

SCOTT> @ptj_env1.sql

PL/SQL procedure successfully completed.

SCOTT> select count(*) from sports_sold;

COUNT(*)

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

44

SCOTT> !more ptj_env2.sql

declare

cursor cur_item is

select sp_code from sports

where sp_stat = 'Y' or sp_name = 'SQUASH' ;

begin

for vs_item in cur_item loop

for i in 0..1000 loop

[email protected] 35

Page 36: O10g app support_11

http://www.ggola.com 장 경 상

insert into sports_sold values

(vs_item.sp_code, to_char(sysdate -1 -i, 'YYYYMMDD'),

(i + 1) * to_number(to_char(sysdate + i, 'SS'))

);

end loop;

commit;

end loop;

end;

/

SCOTT> @ptj_env2.sql

PL/SQL procedure successfully completed.

SCOTT> select count(*) from sports_sold;

COUNT(*)

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

5049

이제 3가지 SQL을 수행해 보자. 첫 번째는 가장 흔한 방식인 join이며 두 번째는

dimension값이 없는 fact table을 표현하는 일반적인 outer join이며 마지막 세

번째는 oracle10g가 주장하는 partition별 outer join을 통한 report format이다.

SCOTT> set pagesize 60

SCOTT> select sp_id, sale_date, sp_volumn,

2 sum(sp_volumn) over(partition by sp_id order by sale_date) "Sum_Vol"

3 from sports_sold join ptj1 using(sale_date);

SP_ SALE_DAT SP_VOLUMN Sum_Vol

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

OB1 20050721 47155 47155

OB1 20050722 94312 141467

OB1 20050723 141468 282935

OB1 20050724 188628 471563

OB1 20050725 235785 707348

[email protected] 36

Page 37: O10g app support_11

http://www.ggola.com 장 경 상

OB1 20050726 282948 990296

OB1 20050727 330106 1320402

OB1 20050728 377272 1697674

OB2 20050721 47161 47161

OB2 20050722 94322 141483

OB2 20050723 141486 282969

OB2 20050724 188648 471617

OB2 20050725 235815 707432

OB2 20050726 282978 990410

OB2 20050727 330148 1320558

OB2 20050728 377312 1697870

OB3 20050721 47166 47166

OB3 20050722 94334 141500

OB3 20050723 141501 283001

OB3 20050724 188672 471673

OB3 20050725 235840 707513

OB3 20050726 283014 990527

OB3 20050727 330183 1320710

OB3 20050728 377360 1698070

XN1 20050721 47172 47172

XN1 20050722 94344 141516

XN1 20050723 141519 283035

XN1 20050724 188692 471727

XN1 20050725 235870 707597

XN1 20050726 283044 990641

XN1 20050727 330225 1320866

XN1 20050728 377400 1698266

32 rows selected.

Sports table의 상태가 ‘Y’인 4개의 항목에 대하여 스포츠 항목에 대한 날짜 별

판매량을 날짜 순서의 sum을 포함하여 출력하였다. Time dimension은 18일부터 28

일까지 총 11일이지만 이에 해당하는 fact data는 (18 + 3) = 21일부터 28일까지 총

8일 이다. 따라서 data는 4 X 8 = 32 rows가 된다.

[email protected] 37

Page 38: O10g app support_11

http://www.ggola.com 장 경 상

자 이제 사용자가 time dimension의 모든 날짜를 기준으로 data를 보고자 한다.

그렇다면 보통은 다음과 같이 작업을 진행할 것이다.

SCOTT> select sp_id, sale_date, sp_volumn,

2 sum(sp_volumn) over(partition by sp_id order by sale_date) "Sum_Vol"

3 from sports_sold ss right outer join ptj1 using(sale_date)

4 order by sp_id, sale_date;

SP_ SALE_DAT SP_VOLUMN Sum_Vol

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

OB1 20050721 47155 47155

OB1 20050722 94312 141467

OB1 20050723 141468 282935

OB1 20050724 188628 471563

OB1 20050725 235785 707348

OB1 20050726 282948 990296

OB1 20050727 330106 1320402

OB1 20050728 377272 1697674

OB2 20050721 47161 47161

OB2 20050722 94322 141483

OB2 20050723 141486 282969

OB2 20050724 188648 471617

OB2 20050725 235815 707432

OB2 20050726 282978 990410

OB2 20050727 330148 1320558

OB2 20050728 377312 1697870

OB3 20050721 47166 47166

OB3 20050722 94334 141500

OB3 20050723 141501 283001

OB3 20050724 188672 471673

OB3 20050725 235840 707513

OB3 20050726 283014 990527

OB3 20050727 330183 1320710

OB3 20050728 377360 1698070

XN1 20050721 47172 47172

XN1 20050722 94344 141516

[email protected] 38

Page 39: O10g app support_11

http://www.ggola.com 장 경 상

XN1 20050723 141519 283035

XN1 20050724 188692 471727

XN1 20050725 235870 707597

XN1 20050726 283044 990641

XN1 20050727 330225 1320866

XN1 20050728 377400 1698266

20050718

20050719

20050720

35 rows selected.

총 3개의 rows가 추가되었다. 이는 dimension에 있으나 fact table에 존재하는 않는

내역이다. 그러나 최종 사용자 입장에서 이런 식의 data는 별로 의미가 없다. 그냥 18

일부터 20일까지 아무런 data가 생성되지 않았다는 의미일 뿐이다. 이제 스포츠

항목에 대하여 개별 outer join을 해보자.

SCOTT> select sp_id, sale_date, sp_volumn, sum(sp_volumn)

2 over(partition by sp_id order by sale_date) "Sum_Vol"

3 from sports_sold partition by(sp_id)

4 right outer join ptj1 using(sale_date);

SP_ SALE_DAT SP_VOLUMN Sum_Vol

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

IB1 20050718

IB1 20050719

IB1 20050720

IB1 20050721

IB1 20050722

IB1 20050723

IB1 20050724

IB1 20050725

IB1 20050726

IB1 20050727

IB1 20050728

OB1 20050718

[email protected] 39

Page 40: O10g app support_11

http://www.ggola.com 장 경 상

OB1 20050719

OB1 20050720

OB1 20050721 47155 47155

OB1 20050722 94312 141467

OB1 20050723 141468 282935

OB1 20050724 188628 471563

OB1 20050725 235785 707348

OB1 20050726 282948 990296

OB1 20050727 330106 1320402

OB1 20050728 377272 1697674

OB2 20050718

OB2 20050719

OB2 20050720

OB2 20050721 47161 47161

OB2 20050722 94322 141483

OB2 20050723 141486 282969

OB2 20050724 188648 471617

OB2 20050725 235815 707432

OB2 20050726 282978 990410

OB2 20050727 330148 1320558

OB2 20050728 377312 1697870

OB3 20050718

OB3 20050719

OB3 20050720

OB3 20050721 47166 47166

OB3 20050722 94334 141500

OB3 20050723 141501 283001

OB3 20050724 188672 471673

OB3 20050725 235840 707513

OB3 20050726 283014 990527

OB3 20050727 330183 1320710

OB3 20050728 377360 1698070

XN1 20050718

XN1 20050719

XN1 20050720

[email protected] 40

Page 41: O10g app support_11

http://www.ggola.com 장 경 상

XN1 20050721 47172 47172

XN1 20050722 94344 141516

XN1 20050723 141519 283035

XN1 20050724 188692 471727

XN1 20050725 235870 707597

XN1 20050726 283044 990641

XN1 20050727 330225 1320866

XN1 20050728 377400 1698266

55 rows selected.

이제 report가 보다 더 명확해 졌다. 어느 스포츠가 어느 날짜에 data가 없는지

나타나는 것이다. 특히나 위 data는 앞서 보이지 않았던 ‘SQUASH’ 항목(IB1)도

나타남으로 fact table 전체 data를 기준으로 report를 해준다. 이를 응용하여 다른

형태로 표현해 보자. 우선 time dimension에 “IB1”이 가지고 있는 날짜 하나(sysdate

-1)를 추가한 후 최종 사용자가 data를 날짜 별, 스포츠 이름 별, 판매량과 판매량의

날짜 별 누적 값을 “20050725”일까지만 top-down형식으로 그리고 매출이 없는

날짜는 0으로 보여달라는 요구가 있다고 가정해 보자. 그렇다면 다음과 같은 형식이

가능할 것이다.

SCOTT> insert into ptj1 values (to_char(sysdate -1, 'YYYYMMDD'));

1 row created.

SCOTT> commit;

Commit complete.

SCOTT> col sp_name for a10

SCOTT> select ss.sale_date, sp.sp_name, ss.volumn, ss.sumvol

2 from sports sp,

3 (select sale_date, sp_id, nvl(sp_volumn, 0) "VOLUMN", sum(sp_volumn)

4 over(partition by sp_id order by sale_date) "SUMVOL"

5 from sports_sold partition by(sp_id)

6 right outer join ptj1 using(sale_date)

7 where sale_date <= '20050725') ss

[email protected] 41

Page 42: O10g app support_11

http://www.ggola.com 장 경 상

8 where sp.sp_code = ss.sp_id

9 order by 1, sp.sp_code;

SALE_DAT SP_NAME VOLUMN SUMVOL

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

20050717 SQUASH 19 19

20050717 BASEBALL 20 20

20050717 TENNIS 20 20

20050717 SOCKER 21 21

20050717 SWIMMING 21 21

20050718 SQUASH 0 19

20050718 BASEBALL 0 20

20050718 TENNIS 0 20

20050718 SOCKER 0 21

20050718 SWIMMING 0 21

20050719 SQUASH 0 19

20050719 BASEBALL 0 20

20050719 TENNIS 0 20

20050719 SOCKER 0 21

20050719 SWIMMING 0 21

20050720 SQUASH 0 19

20050720 BASEBALL 0 20

20050720 TENNIS 0 20

20050720 SOCKER 0 21

20050720 SWIMMING 0 21

20050721 SQUASH 0 19

20050721 BASEBALL 47155 47175

20050721 TENNIS 47161 47181

20050721 SOCKER 47166 47187

20050721 SWIMMING 47172 47193

20050722 SQUASH 0 19

20050722 BASEBALL 94312 141487

20050722 TENNIS 94322 141503

20050722 SOCKER 94334 141521

20050722 SWIMMING 94344 141537

[email protected] 42

Page 43: O10g app support_11

http://www.ggola.com 장 경 상

20050723 SQUASH 0 19

20050723 BASEBALL 141468 282955

20050723 TENNIS 141486 282989

20050723 SOCKER 141501 283022

20050723 SWIMMING 141519 283056

20050724 SQUASH 0 19

20050724 BASEBALL 188628 471583

20050724 TENNIS 188648 471637

20050724 SOCKER 188672 471694

20050724 SWIMMING 188692 471748

20050725 SQUASH 0 19

20050725 BASEBALL 235785 707368

20050725 TENNIS 235815 707452

20050725 SOCKER 235840 707534

20050725 SWIMMING 235870 707618

45 rows selected.

이 결과는 17일에 data를 포함하고 있으며 18일부터 20일까지는 판매량이 0 따라서

누적 값은 17일과 동일하다는 것을 보여준다. 물론, 21일부터는 판매량 증가에 따른

누적치의 변화도 나타나며 다만 스포츠 “SQUASH”의 경우 17일 외에는 판매량이

없음으로 17일의 판매량이 날짜 별 누계와 동일하다는 것을 보여주고 있다.

[email protected] 43

Page 44: O10g app support_11

http://www.ggola.com 장 경 상

참조

==============================================

=================

fact : o8i 27p

dimension : o8i 33p

[email protected] 44

Page 45: O10g app support_11

http://www.ggola.com 장 경 상

4.4. SQL Model Clause

4.4.1.Overview

SQL Model 절은 기본적으로 spread-sheet 형식의 data 처리를 가능케 하도록 data

를 만들어 주는 oracle10g의 강력하고 유연한 SQL 계산 능력이다.

예를 들면, 여러 개의 union과 SQL을 통해 만들어져야 하는 다양한 row 단위의 data

로 계산된 표현 형식을 향후에 설명하는 model 절과 rule 기준을 가지고 자연스럽게

표현할 수 있는 기능이다.

사실 이 부분은 data warehousing의 기법으로 model절만 자세히 설명해도 상당한

분량이 될 수 있다. 그러나 oracle10g의 일반적인 new features를 소개하는

문서들에는 별로 친절한 설명이 나오지 않는다. 그것은 이 기법의 대부분 이 책의

범위를 넘어서는 DW측면의 것이기 때문이지만 필자가 보기에 이 기능은 DW와

상관없이 사용자의 이해와 능력에 따라 매우 유용할 수 있다고 생각되어 대체적인

사용방식 만은 비교적 자세히 다루고 예제를 통해 실습을 하면서 익혀보고자 한다. 가장

일반적인 SQL Model clause은 다음과 같은 형식을 취한다.

select p_col, d_col, m_value

from s_model where d_col in ('VAL1', 'VAL2')

group by p_col, d_col

model

[return all|updated rows]

[reference ref_name on (select x, y from sub_model)

dimension by (x) measures (y)]

[main main_model_name]

[partition by(p_col)]

dimension by(d_col)

measures(sum(m_val) as m_value)

[ignore nav|keep nav]

[unique dimension|unique single reference]

rules [upsert|update] [sequential order | automatic order]

[iterative (n)] [until <condition>]

(m_value['VAL1-VAL2'] = m_value['VAL1'] - m_value['VAL2']);

4.4.2.Three Groups for Model

Model clause은 세 개의 group column으로 다차원 array형식을 만들어 내는데 이는

partition, dimension, measure columns으로 구성되며 rules로 표현되는 형식에

[email protected] 45

Page 46: O10g app support_11

http://www.ggola.com 장 경 상

따라 이들 3개의 groups을 기준으로 다양한 data를 구성할 수 있다.

1. partition by (columns) : SQL Model의 결과값을 구성하는 기준이 되는 logical

block을 정의한다. 보통 논리적으로 볼 때 결과 값의 맨 왼쪽 column으로 인식된다.

2. dimension by (columns) : 말 그대로 차원을 정의한다. 보통 partition value에

따른 세부적인 구분 기준을 표시하게 된다.

3. measures (columns) : 일반적으로 숫자로 표시되는 즉, fact table의 실 data를

정의한다. 따라서 partition value에 따른 dimension value의 조합으로 match되는

data를 표현한다.

4. rules : dimension으로 정의된 다차원 구성에 의해 만들어지는 measures의 값은

rules에 의해 다양한 형식의 계산을 할 수 있고 그 값은 모두 각각의 개별 partition

value에 적용된다. 그리고 그 결과값이 최종적으로 row단위로 표현된다.

CF. rules는 매우 다양한 표현 형식을 가지고 있다. 어찌 보면 이 rules에서 얼마나

원하는 data를 잘 표현하는가가 SQL Model의 훌륭한 결과값을 받는가 아닌가의

관건이 될 것이다.

4.4.3.Rules

앞서 설명을 했듯이 이 rules을 구성하는 것이 관건이 된다. 이 rules을 표현하는

방식은 매우 다양하지만 역시 dimension columns을 비교대상으로 하여 measures

value를 표현하는 규칙은 항상 동일하다.

1. dimension columns과 rules이 match : 만일 SQL Model의 대상이 되는 table(or

view)의 dimension이 “CITY”, “YEAR”로 되어 있고 표시되는 measures data는

인구수인 “PPLT’이고 2001년까지만 data가 있다고 가정하자. 그렇다면 dimension

by (CITY, YEAR)와 같은 형식으로 SQL Model을 구현할 것이다. 물론, rules는 이

값을 기준으로 정의가 될 터이다. 이때에 rules는 다음과 같은 형식으로 match 시킨다.

pplt[city = ‘SEOUL’, year = 1980]

또는 아래와 같이 dimension by의 순서에 따라 column을 지정하지 않고

pplt[‘PUSAN’, year = 2001]

2. rules의 표현은 보통 series 형식으로 하게 된다. 즉, 위의 예에서 2000년도와

2001년의 인구수를 기준으로 2002년의 인구수를 예측한다면 아래와 같은 형식을

취할 수 있을 것이다.(물론, forecasting(예측)의 기준은 각자 다를 수 있다)

pplt[‘SEOUL’, 2002]=trunc((MAX(pplt)[‘SEOUL’, year between 2000 and

2001]) * 1.2)

3. rules option : rules을 지정할 때는 두 가지 option을 사용할 수 있다. 위에서처럼

아무것도 지정하지 않으면 default인 자동으로 upsert가 지정되며 이는 참조하는

data(cell)가 있으면 update를 하고 없으면 insert를 한다. 만일, update option을

[email protected] 46

Page 47: O10g app support_11

http://www.ggola.com 장 경 상

사용하면 참조하는 data(cell)가 있으면 update를 하고 없으면 insert하지 않는다.

그리고 이 option은 global 또는 run level로 나누어 지는데 global level에서

지정하면 모든 rules에 적용이 되고 각 rule에 따로 지정하면(run level) 각각 따로

option이 적용된다. 물론, 둘 다 지정을 한 경우엔 run level이 우선한다.

rules update

( pplt[‘SEOUL’, 2001] = pplt[‘SEOUL’, 2000] * 1.2,

upsert pplt[‘SEOUL’, 2002]=trunc((MAX(pplt)[‘SEOUL’, year between 2000

and 2001]) * 1.2)

위의 예는 global level에서 update를 지정하였기 때문에 첫 번째 rules는 update를

진행하고 두 번째 rules는 run level에서 upsert를 지정하였기 때문에 2002년 data가

있으면 update 없으면 insert를 진행한다.

4. wildcard : rules는 다양한 표현을 위해 “ANY” wildcard를 사용할 수 있다. 이는

모든 것을 치환하는 역할을 한다. 아래의 예는 2000년의 서울지역 인구수의 1.2배를

모든 도시의 2001년도 data로 지정하는 것이다.

pplt[ANY, 2001] = pplt[‘SEOUL’, 2000] * 1.2

위 형식은 아래와 같이 바꾸어 사용할 수도 있다.

pplt[city is ANY, 2001] = pplt[‘SEOUL’, 2000] * 1.2

5. functions : rules를 표현하는 여러 기능 중 표현을 단순화 시키기 위해 CV

function을 사용할 수 있다. 예를 들어 다음과 같은 rules이 있다면.

pplt[‘SEOUL’, 2002]=trunc((MAX(pplt)[‘SEOUL’, year between 2000 and 2001]) *

1.2),

pplt[‘PUSAN’, 2002]=trunc((MAX(pplt)[‘PUSAN’, year between 2000 and 2001]) *

1.2),

pplt[‘DAEGU’, 2002]=trunc((MAX(pplt)[‘DAEGU’, year between 2000 and 2001]) *

1.2),

pplt[‘JUNJOO’, 2002]=trunc((MAX(pplt)[‘JUNJOO’, year between 2000 and 2001]) *

1.2)

이를 아래와 같이 한 라인으로 바꿀 수 있다.

pplt[city in (‘SEOUL’, ‘PUSAN’, ‘DAEGU’, ‘JUNJOO’), 2002] = trunc((MAX(pplt)

[CV(city), year between 2000 and 2001]) * 1.2)

또한 NVL과 같은 표현도 지원하는데 PRESENTV, PRESENTNNV가 있다. 표현 식은 둘

[email protected] 47

Page 48: O10g app support_11

http://www.ggola.com 장 경 상

다 다음과 같다.

PRESENT(NN)V (pplt[‘SEOUL’, 2004], pplt[‘DAEGU’, 2004], 1.1 *

pplt[‘SEOUL’, 2003])

PRESENTV : 서울지역 2004년도 자료가 있으면 대구지역 2004년도 인구수를 return

하고 없다면 전년도 서울지역 인구수의 1.1배를 return한다.

PRESENTNNV : 서울지역 2004년도 자료가 있거나 not null이면 대구지역 2004년도

인구수를 아니면 전년도 서울지역 인구수의 1.1배를 return한다.

6. 일반적으로 rules로 표현되는 data는 dimension values의 order에 따라

나타나는데 필요할 경우 사용자가 rules에 order by를 지정함으로써 특정 순서를

강제할 수 있다. 다음은 이런 기법을 이용하여 특정 순서를 지정하는 방식이다.

pplt[city in (‘SEOUL’, ‘PUSAN’, ‘DAEGU’, ‘JUNJOO’, 2002] order by year =

trunc((MAX(pplt) [CV(city), year between 2000 and 2001]) * 1.2)

7. rules order : 앞서의 예들은 모두 default rules order인 “SEQUENTIAL ORDER”

가 사용되었다. 그러나 사용자의 요구에 따라 “AUTOMATIC ORDER”를 지정하게 되면

rules에 있는 계산들은 그 dependency에 따라 처리 순서가 바뀌게 된다. 아래의 예를

보자.

rules automatic order

(pplt[ANY, 2001] = pplt[‘SEOUL’, 2000] * 1.2,

pplt[‘SEOUL’, 2000] = 10000000)

위의 예는 automatic order를 지정했기 때문에 첫 번째 rules인 2000년도의 서울

인구수를 모든 도시에 적용하기에 앞서서 마지막에 있는 2000년도 서울 인구수를 1

천만으로 update하는 과정이 먼저 수행이 된다. 즉, 최종 결과는 모든 도시의 2001

년도 인구수는 1천2백만이 되는 것이다. 만일 위 순서를 sequential order로 했다면

(또는 default임으로 아무 지정도 하지 않았다면) 최종 결과는 모든 도시의 2001년도

인구수는 서울의 2000년도 인구수의 1.2배로 나타나고 서울의 인구수는 1천만으로

표시될 것이다.

8. iterative rule : 이 방식은 rules을 수행할 때 동일한 규칙을 특정 조건을 만날 때

까지 반복적으로 수행하라는 의미이다. 다음의 예를 보자.

rules iterate(10) [until]

(pplt[‘SEOUL’, 2000] = pplt[‘SEOUL’, 2000]/1000)

위 예는 rules를 최대 10회 반복적으로 수행하도록 하는 예이다. 만일, until 조건에

특정한 조건을 생성했다면 이 rules은 10회 반복 이전에 until 조건이 만족되어 중단될

[email protected] 48

Page 49: O10g app support_11

http://www.ggola.com 장 경 상

수도 있다.

CF. 이 기법은 앞서 소개한 “AUTOMATIC ORDER”와 함께 사용할 수 없다.

4.4.4.Other Options

1. reference model : SQL Model은 기본적으로 지정해서 사용하는 다차원 model

외에 참조 model을 만들어 다른 차원의 model data를 활용할 수 있다. 이를

reference model이라 하며 이에 비해 현재까지 설명해온 기본 모델을 main model

이라 부른다. 설명이 더 어렵다. 쉽게 말하면 main model에서 reference model의

값을 참조하여 필요한 data를 가공할 수 있다는 뜻이다. 아래의 예는 reference

model로부터 예측 인구 증가비율을 reference하여 참조하는 것이다.

model

reference pplt_rate on (select city, ratio from population_rate)

dimension by(city) measures(ratio)

main pplt_forecast

dimension by(city, year) measures(pplt)

rules

(pplt[‘SEOUL’, 2001] = pplt[‘SEOUL’, 2000] * pplt_rate.ratio[‘SEOUL’],

pplt[‘PUSAN’, 2001] = pplt[‘PUSAN’, 2000] * pplt_rate.ratio[‘PUSAN’],

pplt[‘DAEGU’, 2001] = pplt[‘DAEGUE’, 2000] * 1.2)

위 마지막 rules의 3개중 처음 2개는 reference model로부터 인구 예측 증가율을

가지고 와서 계산을 하였고 마지막 1개는 전년도를 기준으로 계산을 하였다.

2. ignore nav|keep nav : 이 option은 measure value의 숫자 값이 null이나

비어있을 때에 null로 유지할 것인가 아니면 0으로 변환할 것인가를 지정한다.

기본적으로 “keep nav”가 적용되어 0으로 변환하지 않는 것이 default이며 “ignore

nav”를 지정하면 0으로 변환한다. 이 option은 SQL Model의 모든 measures 즉,

모든 reference clause에 다 사용될 수 있다.

CF. 물론, 서두에 global level에서 사용될 수 있지만 항상 reference model에 직접

지정한 값이 우선한다.

3. unique dimension | unique single reference : 이 option은 해당 model이

partition by 와 dimension by에 의해 unique한가 그렇지 않은가를 의미한다.

기본적으로 unique함이 원칙이며 이 값이 default인 “unique dimension”이다.

그러나 경우에 따라 이 option(default)은 필요하다면 run time시에 배타적으로

unique함을 check함으로 성능에 부담이 있을 수 있다. 만일, “unique single

[email protected] 49

Page 50: O10g app support_11

http://www.ggola.com 장 경 상

reference”를 지정하게 되면 전체 query result set을 가지고 unique를 check하지

않고 rules에서 지정한 오른쪽 부분만을 check함으로 이런 overhead를 피할 수 있다.

CF. 이 option도 모든 reference clause에 적용할 수 있다.

4. return all | updated rows : 이 options은 query 결과 모두를 return할 것인가를

지정한다. 지정하지 않으면 default ‘return all rows”가 지정되어 모든 rows가

return되지만 만일 “return updated rows”가 지정되면 rules에 의해 update된 rows

만 return된다.

4.4.5.Model Example

이제 앞서 설명한 방식들을 이용하여 몇 가지 실례를 통해 작업을 해보자.

4.4.5.1. Test Data 환경 구성먼저 원활한 작업환경을 만들기 위하여 fact table “sports_sold”에 앞서 만들어

테스트를 진행한 time dimension “ptj1”외에 방위 즉, 동서남북을 표현하는

“compass” table을 만들고 이를 sports_sold에 column을 추가하여 random하게

data를 생성해 보자. 이는 방위 별, 상품 별, 날짜 별 스포츠 용품 매출액을 계산한다는

시나리오를 만들어 준다.

SCOTT> !pwd

/app/oracle/temp

SCOTT> conn scott/tiger

Connected.

SCOTT> create table compass (dir_name varchar2(10), dir_code

varchar2(1));

Table created.

SCOTT> insert into compass values ('NORTH', 'N');

1 row created.

SCOTT> insert into compass values ('SOUTH', 'S');

1 row created.

[email protected] 50

Page 51: O10g app support_11

http://www.ggola.com 장 경 상

SCOTT> insert into compass values ('EAST', 'E');

1 row created.

SCOTT> insert into compass values ('WEST', 'W');

1 row created.

SCOTT> alter table sports_sold add cp_code varchar2(1);

Table altered.

SCOTT> !more ptj_env3.sql

declare

vs_dir varchar2(1);

cursor cur_item is

select sp_id, sale_date from sports_sold

order by sale_date, substr(to_char(sp_volumn*0.5), 2, 2);

-- 위 order by는 날짜 별 순서에 상품에 대한 random 순서를

-- 가져오기 위한 방식일 뿐 다른 의미는 없다.

begin

vs_dir := 'N';

for vs_item in cur_item loop

update sports_sold set cp_code = vs_dir

where sp_id = vs_item.sp_id and sale_date = vs_item.sale_date;

if vs_dir = 'N' then

vs_dir := 'S';

elsif vs_dir = 'S' then

vs_dir := 'E';

elsif vs_dir = 'E' then

vs_dir := 'W';

else

vs_dir := 'N';

end if;

end loop;

[email protected] 51

Page 52: O10g app support_11

http://www.ggola.com 장 경 상

commit;

end;

/

SCOTT> @ptj_env3.sql

PL/SQL procedure successfully completed.

SCOTT> desc compass

Name Null? Type

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

DIR_NAME VARCHAR2(10)

DIR_CODE VARCHAR2(1)

SCOTT> desc ptj1

Name Null? Type

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

SALE_DATE VARCHAR2(8)

SCOTT> desc sports

Name Null? Type

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

SP_CODE VARCHAR2(3)

SP_NAME VARCHAR2(30)

SP_STAT VARCHAR2(1)

SCOTT> desc sports_sold

Name Null? Type

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

SP_ID VARCHAR2(3)

SALE_DATE VARCHAR2(8)

SP_VOLUMN NUMBER

CP_CODE VARCHAR2(1)

이제 테스트를 위한 table 환경 즉, fact table “sports_sold”, 시간 표시 “ptj1”, 방위

[email protected] 52

Page 53: O10g app support_11

http://www.ggola.com 장 경 상

표시 “compass”, sports code table “sports”이 준비 되었다.

다음은 앞서 만들어진 ptj1에 현재 sports_sold에 있고 ptj1에 없는 날짜들을(매출

날짜로 사용하기 위해) 생성함과 동시에 날짜에 대한 상태코드를 추가한다. 이 상태

코드는 나중에 테스트를 위한 것이니 그냥 따라 하기 바란다. 그리고 마지막으로 SQL

Model에 사용할 view를 방위 별, 상품 별, 연도 별, 월 별 판매량과 판매일 수의 합을

갖도록 생성한다.

SCOTT> select min(sale_date) from ptj1;

MIN(SALE

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

20050717

SCOTT> insert into ptj1

2 select distinct(sale_date) from sports_sold

3 where sale_date < '20050717';

1000 rows created.

SCOTT> select count(*) from ptj1;

COUNT(*)

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

1012

SCOTT> select count(distinct(sale_date)) from ptj1;

COUNT(DISTINCT(SALE_DATE))

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

1012

SCOTT> alter table ptj1 add time_stat varchar2(1) default 'Y';

Table altered.

SCOTT> desc ptj1

[email protected] 53

Page 54: O10g app support_11

http://www.ggola.com 장 경 상

Name Null? Type

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

SALE_DATE VARCHAR2(8)

TIME_STAT VARCHAR2(1)

SCOTT> create or replace view sp_model

2 (dir_name, sp_name, sale_year, sale_month, sale_sum, sale_cnt) as

3 select cp.dir_name, sp.sp_name,

4 substr(td.sale_date, 1, 4), substr(td.sale_date, 5, 2),

5 sum(sp_volumn), count(*)

6 from sports sp, compass cp, ptj1 td, sports_sold ss

7 where sp.sp_code = ss.sp_id and cp.dir_code = ss.cp_code and

8 td.sale_date = ss.sale_date and td.time_stat = 'Y'

9 group by cp.dir_name, sp.sp_name,

10 substr(td.sale_date, 1, 4), substr(td.sale_date, 5, 2);

View created.

SCOTT> select count(*) from sp_model;

COUNT(*)

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

676

위와 같이 SQL Model을 수행할 data구성을 하였다. 먼저 가장 간단한 SQL Model을

생각해 보자. 다음의 SQL은 지금 만들어진 view를 바탕으로 방위를 partition으로

하고 특정 스포츠 항목을 dimension으로 하는 매출액과 상품간 차액을 출력한다.

이를 업무적으로 표현하면 [특정 스포츠 “스쿼시”와 “수영”에 대하여 방위 별 매출

총액을 표시하되 다만, 두 항목간의 방위 별 전체 총액 차이를 추가적으로 표시하여

방위 별 두 항목간 스포츠용품 구매규모와 그 차이를 확인해 보자] 라고 할 수 있다.

SCOTT> select dir_name, sp_name, sales

2 from sp_model where sp_name in ('SQUASH', 'SWIMMING')

3 group by dir_name, sp_name

4 model

[email protected] 54

Page 55: O10g app support_11

http://www.ggola.com 장 경 상

5 partition by(dir_name) dimension by(sp_name)

6 measures(sum(sale_sum) as sales)

7 rules

8 (sales['SQUASH-SWIMMING'] = sales['SQUASH'] - sales['SWIMMING'])

9 order by 1, 2;

from sp_model where sp_name in ('SQUASH', 'SWIMMING')

*

ERROR at line 2:

ORA-37002: Oracle OLAP failed to initialize. Please contact Oracle OLAP

technical support.

ORA-33262: Analytic workspace EXPRESS does not exist.

안타깝게도 위와 같은 error가 발생하였다. 지금 위와 동일한 error를 본 사람은 아마도

필자와 마찬가지로 oracle9i에서 oracle10g로 upgrade를 진행한 경우일 것이다. 위

error 메시지를 통해 SQL Model clause이 oracle9i에서 소개 되었던 workspace

개념과 oracle olap 기능을 함께 사용하고 있다고 유추할 수 있다.

4.4.5.2. OLAP error 발생 시 처리이 부분은 아무런 error가 나오지 않은 사람은 skip해도 좋다. 다만, 위와 같은 오류가

생기는 경우엔 SQL Model을 사용할 수 없음으로 다음과 같은 처리절차가 필요하다.

먼저 다음 SQL을 통해 현재 database의 components를 확인해 보자.

SCOTT> conn system/manager

Connected.

SYSTEM> col comp_name for a40

SYSTEM> set pagesize 100

SYSTEM> select comp_name, status from dba_server_registry;

COMP_NAME STATUS

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

Oracle Enterprise Manager VALID

Oracle XML Database VALID

Oracle Ultra Search VALID

Oracle Text VALID

Spatial VALID

Oracle interMedia VALID

Oracle Workspace Manager VALID

[email protected] 55

Page 56: O10g app support_11

http://www.ggola.com 장 경 상

Oracle Database Catalog Views VALID

Oracle Database Java Packages VALID

Oracle Database Packages and Types VALID

JServer JAVA Virtual Machine VALID

Oracle XDK VALID

12 rows selected.

Oracle OLAP components가 나타나지 않는다. 이제 sysdba로 login하여 OLAP

component를 설치해보자.

SYSTEM> conn

Enter user-name: /as sysdba

Connected.

SYS> @$ORACLE_HOME/olap/admin/olap.sql SYSAUX TEMP;

PL/SQL procedure successfully completed.

…………………………….

…………………………….

…………………………….

Call completed.

Call completed.

Call completed.

Call completed.

[email protected] 56

Page 57: O10g app support_11

http://www.ggola.com 장 경 상

Function created.

Grant succeeded.

SYS> select comp_name, status from dba_server_registry;

COMP_NAME STATUS

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

OLAP Catalog VALID

Oracle Enterprise Manager VALID

Oracle XML Database VALID

Oracle Ultra Search VALID

Oracle Text VALID

Spatial VALID

Oracle interMedia VALID

Oracle Workspace Manager VALID

OLAP Analytic Workspace VALID

Oracle Database Catalog Views VALID

Oracle Database Java Packages VALID

Oracle Database Packages and Types VALID

JServer JAVA Virtual Machine VALID

Oracle XDK VALID

Oracle OLAP API VALID

15 rows selected.

OLAP 설치가 완료되면서 총 3개의 components “OLAP Catalog”, “OLAP

Workspace Manager”, “Oracle OLAP API”이 load되었다.

4.4.5.3. SQL Model Test

이제 앞서 문제가 되었던 SQL을 다시 수행해보자.

SCOTT> select dir_name, sp_name, sales

2 from sp_model where sp_name in ('SQUASH', 'SWIMMING')

3 group by dir_name, sp_name

4 model

[email protected] 57

Page 58: O10g app support_11

http://www.ggola.com 장 경 상

5 partition by(dir_name) dimension by(sp_name)

6 measures(sum(sale_sum) as sales)

7 rules

8 (sales['SQUASH-SWIMMING'] = sales['SQUASH'] - sales['SWIMMING'])

9 order by 1, 2;

DIR_NAME SP_NAME SALES

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

EAST SQUASH 2482390

EAST SQUASH-SWIMMING -1172768

EAST SWIMMING 3655158

NORTH SQUASH 2491449

NORTH SQUASH-SWIMMING -406578

NORTH SWIMMING 2898027

SOUTH SQUASH 2594240

SOUTH SQUASH-SWIMMING -184461

SOUTH SWIMMING 2778701

WEST SQUASH 2454560

WEST SQUASH-SWIMMING -823071

WEST SWIMMING 3277631

12 rows selected.

다음은 표현 형식을 바꾸어 스포츠 항목별 매출 규모의 차이를 보다 보기 쉽게 해보자.

SCOTT> select dir_name, sp_name, sales

2 from sp_model where sp_name in ('SQUASH', 'SWIMMING')

3 group by dir_name, sp_name

4 model

5 partition by(dir_name) dimension by(sp_name)

6 measures(sum(sale_sum) as sales)

7 rules

8 (sales['SQUASH-SWIMMING'] = sales['SQUASH'] - sales['SWIMMING'])

9 order by 1, length(sp_name);

DIR_NAME SP_NAME SALES

[email protected] 58

Page 59: O10g app support_11

http://www.ggola.com 장 경 상

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

EAST SQUASH 2482390

EAST SWIMMING 3655158

EAST SQUASH-SWIMMING -1172768

NORTH SQUASH 2491449

NORTH SWIMMING 2898027

NORTH SQUASH-SWIMMING -406578

SOUTH SQUASH 2594240

SOUTH SWIMMING 2778701

SOUTH SQUASH-SWIMMING -184461

WEST SQUASH 2454560

WEST SWIMMING 3277631

WEST SQUASH-SWIMMING -823071

12 rows selected.

전반적으로 수영이 특히나 동쪽 방향에서 훨씬 매출액 크다는 것을 알 수 있다.

마지막으로 좀 더 복잡한 예를 통해 다양한 기법을 확인해 보자.

SCOTT> select sp_name, sale_year, sales

2 from sp_model where sp_name in ('BASEBALL', 'TENNIS', 'SQUASH') and

3 sale_year > 2003 group by sp_name, sale_year

4 model

5 dimension by(sp_name, sale_year) measures(sum(sale_sum) as sales)

6 rules (

7 sales[ANY, 2005] =

8 PRESENTV(sales[CV(), CV()], round(sales[CV(), CV() - 1] * 1.5),

9 round(sales[CV(), CV() - 2] * 1.2)),

10 sales[for sp_name in ('BASEBALL', 'TENNIS', 'SQUASH'),

11 for sale_year in (2006, 2007, 2008)] = round(sales[CV(), CV() - 2] *

1.2),

12 sales[for (sp_name, sale_year) in (('BASEBALL', 2009), ('TENNIS', 2009),

13 ('SQUASH', 2009), ('BASEBALL', 2010), ('TENNIS', 2010), ('SQUASH',

2010))]

14 = round(sales[CV(), CV() - 2] * 1.2),

15 sales['BASEBALL', for sale_year from 2011 to 2013 increment 1]

[email protected] 59

Page 60: O10g app support_11

http://www.ggola.com 장 경 상

16 = round(sales[CV(), CV() - 2] * 1.2),

17 sales['YEAR_SUM', for sale_year from 2004 to 2013 increment 1]

18 = sum(sales)[ANY, CV()]

19 )

20 order by sale_year, sp_name;

SP_NAME SALE_YEA SALES

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

BASEBALL 2004 2792580

SQUASH 2004 2792580

TENNIS 2004 2865590

YEAR_SUM 2004 8450750

BASEBALL 2005 4188870

SQUASH 2005 4188870

TENNIS 2005 4298385

YEAR_SUM 2005 12676125

BASEBALL 2006 3351096

SQUASH 2006 3351096

TENNIS 2006 3438708

YEAR_SUM 2006 10140900

BASEBALL 2007 5026644

SQUASH 2007 5026644

TENNIS 2007 5158062

YEAR_SUM 2007 15211350

BASEBALL 2008 4021315

SQUASH 2008 4021315

TENNIS 2008 4126450

YEAR_SUM 2008 12169080

BASEBALL 2009 6031973

SQUASH 2009 6031973

TENNIS 2009 6189674

YEAR_SUM 2009 18253620

BASEBALL 2010 4825578

SQUASH 2010 4825578

TENNIS 2010 4951740

[email protected] 60

Page 61: O10g app support_11

http://www.ggola.com 장 경 상

YEAR_SUM 2010 14602896

BASEBALL 2011 7238368

YEAR_SUM 2011 7238368

BASEBALL 2012 5790694

YEAR_SUM 2012 5790694

BASEBALL 2013 8686042

YEAR_SUM 2013 8686042

34 rows selected.

위 예는 다음과 같은 사용자 요구를 수용하기 위하여 1 SQL 문으로 작성한다고 가정한

것이다.

1. 상품별, 연도별 매출액을 출력하되 2003년 이후의 야구, 테니스, 스쿼시종목에

한정하여 매출액을 계산한다.

2. 2005년도 매출액 산정은 모든 종목에(위 3가지 종목) 대하여 2005년도 매출이

있으면 2004년도 매출의 1.5배를 없으면 2003년도 매출의 1.2배를 적용한다.

3. for loop를 사용하여 2006년부터 2008년까지 매출액 예상을 하되 각각 2년도 전의

매출액에 1.2배를 산정한다.

4. 다른 형식 for loop를 사용하여 2009년, 2010년의 매출액을 각 2년도 전 매출액의

1.2배로 산정한다.

5. 야구에 대해서만 2011년부터 1013년까지 2년도 전 매출액의 1.2배를 산정하되

“for .. from .. to .. in(de)crement ..”를 사용한다.

6. 2004년부터 2013년까지 모든 스포츠의 항목별 매출액 및 매출예상 금액을

연도별로 합산하여 계산한다.

[email protected] 61

Page 62: O10g app support_11

http://www.ggola.com 장 경 상

참조

==============================================

=================

workspace : o9i 408p

olap : o8i 36p

[email protected] 62

Page 63: O10g app support_11

http://www.ggola.com 장 경 상

4.5. Materialized View

벌써 이 Materialized View(이하 MView)features가 소개된 지도 꽤 오래 되었다.

처음 oracle8i에서 이 기법을 사용하면서 정말 유용하다는 생각을 하면서도 대다수의

사람들이 그냥 무시하고 지나칠 때마다 참 안타까웠다. 그러다 실무에서 이 기능을 직접

구현해서 담당자에게 소개해 주었을 때 무척 만족스러워했던 모습을 아직 기억하고

있다. 필자는 거의 이런 이야기를 하지 않지만 실제로 이 기능의 존재를 제대로

인식하지 못해 사용하지 않는 사람들이 많아서 좀 길고 불필요할 것 같아도 이렇게

시작한다.

필자의 홈페이지에서 배포하는 oracle8i new features 문서를 보면 매우 쉽게 이

MView를 만들어 테스트를 해볼 수 있다. 먼저 MView하나를 만들어 놓고서

oracle10g에서 소개되는 내용들을 확인해 보자.

4.5.1.기본 Materialized View 생성먼저 시나리오 구성을 위해 table을 하나 생성하고 무작위로 data를 만든다. 현재

상황은 table “emp_fact”를 만들어 5개의 columns으로 구성을 하되 주로 사용하는

2개의 columns의 data는 다음과 같이 구성하였다.

SCOTT> desc emp_fact;

Name Null? Type

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

EMP_ID VARCHAR2(10)

EMP_NAME VARCHAR2(10)

SAL_MON VARCHAR2(6)

DEPT_NAME VARCHAR2(20)

SALARY NUMBER

Key가 되는 emp_id는 ‘A01’, ‘B01’, ‘C01’, ‘D01’, ‘E01’까지고 dept_name은

‘DATABASE’, ‘MIDDLEWARE’, ‘APPLICATION’, ‘SYSTEM’, ‘NETWORK’으로 총 5

가지로 구분이 되어 있다. 나머지는 무작위로 만들어서 계속적으로 상당한 양이 될

때까지 아래 문장을 반복 수행한 것이다.

SCOTT> insert into emp_fact select * from emp_fact;

현재 data상황은 다음과 같다.

SCOTT> set timing on

SCOTT> select count(*) from emp_fact;

[email protected] 63

Page 64: O10g app support_11

http://www.ggola.com 장 경 상

COUNT(*)

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

9437184

Elapsed: 00:05:13.02

SCOTT> select emp_id, dept_name, sum(salary)

2 from emp_fact group by emp_id, dept_name;

EMP_ID DEPT_NAME SUM(SALARY)

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

A01 NETWORK 1.2805E+10

A01 DATABASE 2886912000

A01 MIDDLEWARE 8279472000

B01 SYSTEM 1.0055E+10

B01 DATABASE 2270080000

B01 MIDDLEWARE 6495478400

C01 DATABASE 2525486400

C01 MIDDLEWARE 7227372800

C01 APPLICATION 1.1219E+10

D01 SYSTEM 9742939200

D01 NETWORK 1.2389E+10

D01 DATABASE 8800299200

D01 MIDDLEWARE 2197750400

D01 APPLICATION 1.0866E+10

E01 SYSTEM 3887499200

E01 NETWORK 4955904000

E01 DATABASE 8803337600

E01 APPLICATION 4354590400

18 rows selected.

Elapsed: 00:00:44.23

[email protected] 64

Page 65: O10g app support_11

http://www.ggola.com 장 경 상

이제 위 SQL을 근간으로 하는 MView를 하나 만들어 보자.

SCOTT> create materialized view mv_emp_sales

2 build immediate refresh complete

3 as select emp_id, dept_name, sum(salary)

4 from emp_fact

5 group by emp_id, dept_name;

from emp_fact

*

ERROR at line 4:

ORA-01031: insufficient privileges

MView는 MView를 만들 수 있는 권한이 따로 있음으로 error가 return되었다. MView

생성을 위한 권한과 충분한 space 사용을 위해 sys 계정으로 필요한 권한을 부여한 후

다시 만들어 보자.

SCOTT> conn sys/manager

Connected.

SYS> grant unlimited tablespace to scott;

Grant succeeded.

Elapsed: 00:00:00.46

SYS> grant create materialized view to scott;

Grant succeeded.

Elapsed: 00:00:00.10

SYS> conn scott/tiger

Connected.

SCOTT> create materialized view mv_emp_sales

2 build immediate refresh complete

3 as select emp_id, dept_name, sum(salary)

4 from emp_fact

5 group by emp_id, dept_name;

Materialized view created.

[email protected] 65

Page 66: O10g app support_11

http://www.ggola.com 장 경 상

Elapsed: 00:07:20.86

SCOTT> select * from mv_emp_sales;

EMP_ID DEPT_NAME SUM(SALARY)

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

A01 NETWORK 1.2805E+10

A01 DATABASE 2886912000

A01 MIDDLEWARE 8279472000

B01 SYSTEM 1.0055E+10

B01 DATABASE 2270080000

B01 MIDDLEWARE 6495478400

C01 DATABASE 2525486400

C01 MIDDLEWARE 7227372800

C01 APPLICATION 1.1219E+10

D01 SYSTEM 9742939200

D01 NETWORK 1.2389E+10

D01 DATABASE 8800299200

D01 MIDDLEWARE 2197750400

D01 APPLICATION 1.0866E+10

E01 SYSTEM 3887499200

E01 NETWORK 4955904000

E01 DATABASE 8803337600

E01 APPLICATION 4354590400

18 rows selected.

Elapsed: 00:00:00.18

MView를 사용하는 효과는 명백히 나타난다. 하지만 진정한 application level의

효과는 query rewrite의 효과적인 사용에 있을 수 있다.

4.5.2.Query Rewrite

앞서 기본 MView 테스트를 진행하면서 설명했지만 효과적인 MView의 생성은

application의 성능향상을 기대해 볼 수 있다. 즉, 특정한 SQL이 많은 I/O를

수반하면서 여러 application에서 자주 사용되는 경우나 또는 많은 application은

[email protected] 66

Page 67: O10g app support_11

http://www.ggola.com 장 경 상

아니지만 일부 application에서 사용되고 있고 해당 application을 MView를

사용하도록 고칠 수 없는 경우가 있다면 이 query rewrite는 매우 유용할 수 있는

것이다. 그러나 이런 query rewrite가 제대로 사용이 되는지 혹은 문제가 있는지를

hint를 통해 SQL의 시작과 함께 사전에 점검하거나 procedure를 통해 미리 문제점을

확인하는 것도 매우 중요하다. 이제 oracle10g의 새로운 hint사용법과 보다 상세해진

rewrite_table에 대하여 알아보자.

먼저 query rewrite의 환경조건을 구성한 후 새로운 hint “rewrite_or_error”를

사용해 보자.

CF. parameter “query_rewrite_enabled”를 “true”로 하고 cost based optimizer

의 설정이 필요하다.

SCOTT> conn scott/tiger

Connected.

SCOTT> set timing off

SCOTT> alter session set query_rewrite_enabled = true;

Session altered.

SCOTT> alter session set optimizer_mode = 'all_rows';

Session altered.

SCOTT> select /*+ rewrite_or_error */ emp_id, dept_name, sum(salary)

2 from emp_fact group by emp_id, dept_name;

from emp_fact group by emp_id, dept_name

*

ERROR at line 2:

ORA-30393: a query block in the statement did not rewrite

앞서 MView를 사용할 때 작성한 SQL을 hint와 함께 사용하자 error가 return되었다.

이는 rewrite가 불가하며 그래서 SQL을 수행하지 않는다는 것이지만 구체적으로

무엇이 문제인가를 나타내지는 않는다.

CF. 주의할 점은 query rewrite parameter를 true로 하지 않고 위 SQL을 사용하면

[email protected] 67

Page 68: O10g app support_11

http://www.ggola.com 장 경 상

query rewrite가 안되기 때문에 원래의 SQL을 수행하게 된다는 것이다. 따라서

적어도 application에서 이 hint와 함께 SQL을 사용하려면 query rewrite

parameter가 true이어야 할 것이다. 그렇지 않다면 error가 아니라 원래의 SQL 즉,

대부분이 long running query로 수행될 것이기 때문이다.

CF. 물론, 효율적인 사용을 위해서는 refresh 설정을 잘해야 하지만 이는 여기서 다루는

부분은 아니다. 필자의 oracle8i new features 문서를 활용하라.

위에서 hint로 나타난 error는 현상만 나오기 때문에 원인을 알기 위해서는 procedure

를 사용해야 하며 또한 관련 table을 만들어야 한다. 마치 explain plan을 사용하기

위한 plan_table처럼 말이다. 다음은 그 과정을 수행하여 구체적으로 무엇이

문제인지를 확인하는 과정이다.

SCOTT> desc rewrite_table

ERROR:

ORA-04043: object rewrite_tablee does not exist

SCOTT> @$ORACLE_HOME/rdbms/admin/utlxrw.sql

Table created.

SCOTT> desc rewrite_table

Name Null? Type

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

STATEMENT_ID VARCHAR2(30)

MV_OWNER VARCHAR2(30)

MV_NAME VARCHAR2(30)

SEQUENCE NUMBER(38)

QUERY VARCHAR2(2000)

MESSAGE VARCHAR2(512)

PASS VARCHAR2(3)

MV_IN_MSG VARCHAR2(30)

MEASURE_IN_MSG VARCHAR2(30)

JOIN_BACK_TBL VARCHAR2(30)

JOIN_BACK_COL VARCHAR2(30)

ORIGINAL_COST NUMBER(38)

[email protected] 68

Page 69: O10g app support_11

http://www.ggola.com 장 경 상

REWRITTEN_COST NUMBER(38)

FLAGS NUMBER(38)

RESERVED1 NUMBER(38)

RESERVED2 VARCHAR2(10)

CF. 위 rewrite_table의 마지막 두 columns은(reserved1, 2) 무시해도 좋다. 아직

실제적으로 사용하고 있는 columns은 아니다.

이제 위에서 생성된 table을 이용하여 앞서 query rewrite error의 원인을 확인해

보자. 사용하는 procedure의 argument로 SQL문을 넣어야 하기 때문에 1라인에 다

넣을 수가 없어서 readability를 위해 라인을 넘기는 “-“을 사용하였다.

SCOTT> exec dbms_mview.explain_rewrite(-

> 'select emp_id, dept_name, sum(salary) from emp_fact-

> group by emp_id, dept_name','mv_emp_sales')

PL/SQL procedure successfully completed.

SCOTT> select mv_name, query, message from rewrite_table;

MV_NAME

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

QUERY

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

MESSAGE

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

MV_EMP_SALES

select emp_id, dept_name, sum(salary) from emp_fact group by emp_id,

dept_name

QSM-01026: query rewrite is disabled for, MV_EMP_SALES

이제 query rewrite가 안되었던 이유가 해당 MView의 query rewrite가 disable

상태였기 때문이라는 것을 알 수 있다. 이를 조정하여 다시 확인을 하고 수행을 해보자.

CF. rewrite_table은 마치 plan_table과 같아서 data가 생성이 되면 사라지자 않는다.

즉, 더 이상 해당 정보가 필요 없다면 rollback을 하거나 delete를 해서 지워주어야

[email protected] 69

Page 70: O10g app support_11

http://www.ggola.com 장 경 상

한다.

SCOTT> rollback;

Rollback complete.

SCOTT> alter materialized view mv_emp_sales

2 enable query rewrite;

Materialized view altered.

SCOTT> exec dbms_mview.explain_rewrite(-

> 'select emp_id, dept_name, sum(salary) from emp_fact-

> group by emp_id, dept_name','mv_emp_sales');

PL/SQL procedure successfully completed.

SCOTT> select mv_name, query, message from rewrite_table;

MV_NAME

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

QUERY

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

MESSAGE

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

MV_EMP_SALES

select emp_id, dept_name, sum(salary) from emp_fact group by emp_id,

dept_name

QSM-01009: materialized view, MV_EMP_SALES, matched query text

위 메시지는 query text가 일치함으로 rewrite를 할 수 있다는 의미이다.

SCOTT> set timing on

SCOTT> select /*+ rewrite_or_error */ emp_id, dept_name, sum(salary)

2 from emp_fact group by emp_id, dept_name;

EMP_ID DEPT_NAME SUM(SALARY)

[email protected] 70

Page 71: O10g app support_11

http://www.ggola.com 장 경 상

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

A01 NETWORK 1.2805E+10

A01 DATABASE 2886912000

A01 MIDDLEWARE 8279472000

B01 SYSTEM 1.0055E+10

B01 DATABASE 2270080000

B01 MIDDLEWARE 6495478400

C01 DATABASE 2525486400

C01 MIDDLEWARE 7227372800

C01 APPLICATION 1.1219E+10

D01 SYSTEM 9742939200

D01 NETWORK 1.2389E+10

D01 DATABASE 8800299200

D01 MIDDLEWARE 2197750400

D01 APPLICATION 1.0866E+10

E01 SYSTEM 3887499200

E01 NETWORK 4955904000

E01 DATABASE 8803337600

E01 APPLICATION 4354590400

18 rows selected.

Elapsed: 00:00:00.00

수행시간만 보아도 rewrite가 제대로 이루어 졌음을 알 수 있다.

MView를 join을 사용하여 복잡하게 만든 경우에 rewrite오류가 있다면 역시 그 오류도

message column에 설명이 될 것이다. 그리고 그 때 join과 관련한 message내역은

다른 column join_back_tbl, join_back_col에 나타나게 된다. 물론, 오류가 발생한

message내의 MView이름도 mv_in_msg column에 나타난다. 여기서 한가지 유용한

것 중 하나가 바로 cost의 비교 값이다. 사용한 SQL의 rewrite 여부에 따른 SQL cost

를 미리 알 수 있는데 column original_cost와 rewritten_cost가 그것이다. 앞서

만들어진 data를 가지고 이를 확인해 보자.

SCOTT> select original_cost, rewritten_cost

2 from rewrite_table;

[email protected] 71

Page 72: O10g app support_11

http://www.ggola.com 장 경 상

ORIGINAL_COST REWRITTEN_COST

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

49725 6

Elapsed: 00:00:00.06

SCOTT> rollback;

Rollback complete.

Elapsed: 00:00:00.00

그렇다면 query rewrite를 하기 위해선 항상 같은 SQL만 가능할까. 만일 꼭 그런

제한이 있다면 MView를 통한 query rewrite의 사용은 매우 한정된 기능이 될 것이며

application의 성능 향상에도 한정된 역할을 하게 될 것이다. 다음의 예제를 수행해

보자.

SCOTT> select /*+ rewrite_or_error */

2 emp_id, sum(salary) from emp_fact group by emp_id;

EMP_ID SUM(SALARY)

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

A01 2.3971E+10

B01 1.8820E+10

C01 2.0972E+10

D01 4.3996E+10

E01 2.2001E+10

Elapsed: 00:00:00.02

작업수행 시간을 보면 분명 query rewrite가 발생했음을 유추할 수 있다. 물론, hint로

인하여 error가 return되지 않은 것으로도 query rewrite가 제대로 되었음을 알 수

있지만. 적어도 이런 정도는 rewrite가 되어야 하며 이는 application의 종류에 따라서

매우 유용할 수가 있다.

다시 말하면, MView를 이루는 기본 SQL의 범위가 어느 정도는 확장성이 있도록

[email protected] 72

Page 73: O10g app support_11

http://www.ggola.com 장 경 상

구현이 되면 그 MView를 기반으로 하여 다양한 SQL이 rewrite가 가능하다는 것이다.

위의 예는 emp_id별, dept_name별 summary를 가진 MView가 만들어졌지만 이

data는 emp_id별 summary를 만드는데도 아무런 지장이 없기 때문에 SQL문은

틀려도 query rewrite가 가능하다는 것을 보여준다.

CF. 지역, 상품, 년도, 분기, 월별 매출액으로 만들어진 MView가 있다고 가정해 보자.

이를 활용하면 얼마나 다양한 query rewrite가 가능하겠는가.

4.5.3.Materialized View Management

MView를 만들고 이를 잘 운용하려면 base table을 기본으로 하는 SQL의 적절성,

refresh 시점의 유효성, query rewrite의 확장성 등 고려할 것이 많아서 MView를

만들다가 오류가 생기는 경우도 자주 접할 수 있다. Oracle10g에서는 이런 문제들에

도움을 주고자 MView의 tuning과 관련된 procedure를 제공하고 있다.

4.5.3.1. Check Fast Refreshable MView

Oracle9i부터 현재 만들어진 MView가 fast refresh가 가능한지 및 각종 속성에 대한

정보를 알아보기 위하여 다음과 같은 방식을 사용해 왔다. 먼저 procedure

“dbms_mview.explain_mview” 의 결과를 담을 table을 생성하고 작업을 수행해

보자.

CF. 이 table도 plan_table이나 앞서 소개했던 rewrite_table과 마찬가지로 table

owenr가 필요할 때 정리를 해주거나 procedure 수행 및 결과를 확인한 후 rollback을

해주어야 한다.

SCOTT> desc mv_capabilities_tablee

ERROR:

ORA-04043: object mv_capabilities_tablee does not exist

SCOTT> @$ORACLE_HOME/rdbms/admin/utlxmv.sql

Table created.

SCOTT> desc mv_capabilities_table

Name Null? Type

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

STATEMENT_ID VARCHAR2(30)

MVOWNER VARCHAR2(30)

[email protected] 73

Page 74: O10g app support_11

http://www.ggola.com 장 경 상

MVNAME VARCHAR2(30)

CAPABILITY_NAME VARCHAR2(30)

POSSIBLE CHAR(1)

RELATED_TEXT VARCHAR2(2000)

RELATED_NUM NUMBER

MSGNO NUMBER(38)

MSGTXT VARCHAR2(2000)

SEQ NUMBER

SCOTT> exec dbms_mview.explain_mview('mv_emp_sales');

PL/SQL procedure successfully completed.

위 결과는 매우 다양한 정보를 제공해 주는데 그 중에서 간단히 두 가지만 조회를 통해

살펴보자.

SCOTT> col possible for a1

SCOTT> col msgtxt for a35

SCOTT> select capability_name, possible, msgtxt

2 from mv_capabilities_table

3 where capability_name in ('REWRITE', 'REFRESH_FAST',

'REFRESH_FAST_AFTER_INSERT');

CAPABILITY_NAME P MSGTXT

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

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

REFRESH_FAST N

REWRITE Y

REFRESH_FAST_AFTER_INSERT N the detail table does not have a materialized view

log

SCOTT> rollback;

Rollback complete.

위 결과는 MView “mv_emp_sales”가 query rewrite가 가능하며 현재 fast refresh

[email protected] 74

Page 75: O10g app support_11

http://www.ggola.com 장 경 상

는 되어있지 않다는 것을 그리고 fast refresh를 하려면 base table의 materialized

view log table이 필요하다는 정보를 제공하고 있다.

4.5.3.2. Tuning Procedure

이제 oracle10g에서 강조하는 MView를 제대로 생성하는데 도움을 주는 procedure

를 통해 그 효율성을 확인해 보자. MView Tuning을 위한 procedure를 수행하기

위해서는 sys 계정으로부터 특별한 권한인 advisor를 받아야 한다. 적절한 권한을

부여한 후 직접 수행해 보자.

SCOTT> conn sys/manager

Connected.

SYS> grant advisor to scott;

Grant succeeded.

SCOTT> conn scott/tiger

Connected.

SCOTT> var task varchar2

SCOTT> exec dbms_advisor.tune_mview(:task,-

> 'create materialized view mv_emp_fast build immediate -

> refresh fast as select emp_id, dept_name, -

> sum(salary) from emp_fact group by emp_id, dept_name');

BEGIN dbms_advisor.tune_mview(:task, 'create materialized view

mv_emp_fast build immediate refresh fast as select emp_id, dept_name,

sum(salary) from emp_fact group by emp_id, dept_name'); END;

*

ERROR at line 1:

ORA-22998: CLOB or NCLOB in multibyte character set not supported

ORA-06512: at "SYS.PRVT_TUNE_MVIEW", line 943

ORA-06512: at "SYS.DBMS_ADVISOR", line 757

ORA-06512: at line 1

제대로 수행하였으나 oracle error 22998을 return하고 비정상 종료되었다. 이 error

는 oracle bug “3478459”로서 oracle10g 10.1.0.4(Server Patch Set)과 향후

oracle10g release2에서 해결이 된다고 한다. 그러나 현재 10.1.0.4 database에서

[email protected] 75

Page 76: O10g app support_11

http://www.ggola.com 장 경 상

동일한 error가 나타난 것이 이상하여 자료를 살펴보면 이와 관련된 oracle

rdbms/admin의 scripts가 10.1.0.4 version과 그 이하 version에서 변경되지 않은

것으로 나타났다.

그래서 다른 자료를 확인해 본 결과 또 다른 oracle bug “4479674”에 따르면 이 bug

가 실제로는 10.1.0.4 patch set에서도 제대로 fix가 되지 않았다는 것을 알 수

있었다. 아직은 해결책이 없음으로 더 이상 tuning 절차를 수행할 수가 없다. 향후

release2에서 그 결과를 다시 확인해 보도록 하자.

CF. 만일 error없이 정상적으로 작업이 이루어지면 여러분은 “USER_TUNE_MVIEW”

를 통해 해당 MView를 만들기 위해 어떤 tuning의 요소들이 있는지 확인할 수 있다.

이 VIEW의 “STATEMENT” column에는 필요한 scripts까지 만들어진다.

CF. 보다 편한 방법을 사용하기 위해 “dbms_advisor.create_file”을 이 procedure

에서 지정하는 directory를 만들어 놓은 후 사용하면 아예 tuning된 SQL문들이

지정된 file로 생성되어 해당 file만 수행하면 되도록 할 수 있다. 사용법은 다음과 같다.

SQL> exec dbms_advisor.create_file

(dbms_advisor.get_task_script('task_name’), 'directory_name', 'file_name');

4.5.4.Partition Change Tracking (PCT)

4.5.4.1. PCT Enhancement

Partition Change Tracking(PCT)이란 MView를 이루는 partitioned tables의 data

들이 어떤 특정 partitions에 있는지를 식별하는 능력을 뜻하는 것으로 refresh time을

최소화하고 query rewrite의 확장을 최대화 할 수 있는 능력이다.

1. 이전 버전인 oracle9i에서는 range, range-hash partition에 한해서만 이 기능이

지원이 되었지만 oracle10g에서는 oracle9i에서 처음 소개된 list partitioned table

에 대해서도 PCT를 지원하게 되었다.

2. join-dependent expression도 PCT를 지원할 수 있게 되었다. 이 말은 join으로

구성된 MView의 base table중 partitioned table의 partition key가 equijoin에

사용되는 SQL문을 말한다. 이 때 partition table에 equijoin되는 table을 join-

dependent table이라 하고 이러한 SQL 표현을 join-dependent expression이라

하며 이런 경우에도 MView를 이루는 해당 detail partitioned table의 변경사항에

대하여 PCT refresh가 가능하게 되었다.

4.5.4.2. PCT Truncate

이전 버전의 oracle은 PCT refresh를 할 때 MView의 rows를 없애기 위해서 delete

문을 사용해 왔다. 물론, 이런 방식 보다는 MView에 대한 truncate partition이 훨씬

[email protected] 76

Page 77: O10g app support_11

http://www.ggola.com 장 경 상

더 효과적일 것이다. Oracle10g는 다음과 같은 조건들을 만족할 때에 이 방식을

제공한다.

1. detail table과 MView의 partition이 모두 range이고 서로 동일한 partition

value의 범위를 가질 때

2. MView가 하나의 PCT key로 partition되었을 때

3. detail table과 MView의 partitions이 1:1 관계를 가질 때

4. truncate partition이 DDL임으로 단일 transaction에 의해 refresh가 수행되는

것을 원치 않을 때

4.5.4.3. PCT Refresh

MView refresh를 manually 하기 위하여 제공되는 dbms_mview.refresh에 PCT

refresh를 위한 option으로 “P”가 추가되었다. 다음과 같은 형식을 사용한다.

SQL> exec dbms_mview.refresh(‘mview_name’, method => ‘p’);

이 procedure의 method는 여러 가지 값을 가지고 있음으로 간단히 이를 살펴보자.

Method Meaning

f fast refresh

? force refresh

C or c complete refresh

A or a A 와 C는 같다.

P or p PCT refresh

m1m2 기술된 MView와 차례로 match시킨다.

위 method value와 관련하여 다음의 예가 의미하는 바를 이해하도록 하자.

SQL> exec dbms_mview.refresh (‘mv1, mv2, mv3’ , method => ‘fc’);

위 의미는 mv1은 fast refresh를 mv2는 complete refresh를 mv3은 default를

적용하여 문장 하나로 MView 3개를 refresh하는 방법이다.

CF. refresh는 특정 SQL가 같이 사용자가 만드는 것이 아니라 MView refresh 내부

처리에 있는 것이다. 어떤 특정 SQL을 통해 새로운 MView를 만드는 것이 아니라는

것을 인식하자.

4.5.5.Other Operation

4.5.5.1. Partition Operation

Oracle10g부터는 partitioned MView를 위한 partition operation이 가능해 졌다.

[email protected] 77

표 4-3

Mview Refresh Method

Page 78: O10g app support_11

http://www.ggola.com 장 경 상

다음과 같은(사실은 alter table과 같은) operation이 가능하다.

SQL> alter materialized view mv_name [truncate|drop] partition pt_name;

SQL> alter materialized view mv_name exchange partition pt_name with

table tab_name;

4.5.5.2. Execution Plan

Oracle10g에서는 MView를 사용하는 execution plan의 설명이 보다 구체화 되었다.

작업한 SQL이 rewrite가 되었는가를 구체적으로 표현해 준다. 다음의 두 SQL간의

차이를 보자.

SCOTT> set timing off

SCOTT> !more plan.sql

set linesize 100

column operation format a50

select substr(lpad(' ',2*(level-1))||operation||' '||options ||' '||

object_name,1,200)

"OPERATION", COST "COST", cpu_cost "CPU-", io_cost "IO-", temp_space

"TMP"

from plan_table

start with id=0 and statement_id = 'test'

connect by prior id = parent_id and STATEMENT_ID='test'

order by id;

delete from plan_table where statement_id='test' ;

--||' '||object_name,1,200) || '-- COST : ' || COST "Plan View"

commit ;

SCOTT> explain plan set statement_id = 'test' for

2 select * from mv_emp_sales;

Explained.

SCOTT> @plan

OPERATION COST CPU- IO- TMP

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

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

SELECT STATEMENT 7 37947 7

[email protected] 78

Page 79: O10g app support_11

http://www.ggola.com 장 경 상

MAT_VIEW ACCESS FULL MV_EMP_SALES 7 37947 7

2 rows deleted.

Commit complete.

SCOTT> explain plan set statement_id = 'test' for

2 select emp_id, sum(salary) from emp_fact group by emp_id;

Explained.

SCOTT> @plan

OPERATION COST CPU- IO- TMP

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

-----------

SELECT STATEMENT 8 1951849 7

SORT GROUP BY 8 1951849 7

MAT_VIEW REWRITE ACCESS FULL MV_EMP_SALES 7 37947 7

3 rows deleted.

Commit complete.

첫 번째 SQL은 그냥 MView를 사용했음을 두 번째 SQL은 어떤 MView를 가지고

query rewrite가 수행되었는지를 구체적으로 표현해 주고 있다.

4.5.5.3. Trusted Option

MView를 생성할 때 oracle10g부터는 option을 통해 refresh 속성을 지정할 수 있다.

다음과 같은 형식으로 지정하며 지정하지 않는 경우에는 default로 “ENFORCED”가

적용된다.

create materialized view mv_name

[email protected] 79

Page 80: O10g app support_11

http://www.ggola.com 장 경 상

... refresh [using enforced|trusted constraints] fast as

select ....

만일, “TRUSTED” option을 적용하면 unenforced constraints(예를 들어

dimension relationship이나 rely constraints같은)를 사용할 수 있다. 예를 들면,

생성되는 MView의 underlying tables이 가지고 있는 constraints가 실제 database

에서는 validate하지 않더라도 DBA가 rely flag를 통해 valid한 것으로 지정하면 해당

constraints를 refresh를 위해 사용할 수 있도록 허용한다는 것이다. 즉, 이 option은

MView의 refresh동안 사용될 수 있는 constraints의 속성을 지정하는 option이다.

[email protected] 80

Page 81: O10g app support_11

http://www.ggola.com 장 경 상

OCP point

==============================================

=================

1. query rewrite와 관련한 hint “rewrite_or_error”에 대한 이해

2. dbms_mview.explain_rewrite를 통한 오류검증 방법

참조

==============================================

=================

materialized view : o8i 22p, o9i 564p

query rewrite : o8i 28p

dbms_mview.explain_mview : o9i 565p

TRUSTED : o8i 31p

[email protected] 81

Page 82: O10g app support_11

http://www.ggola.com 장 경 상

4.6. Indexes

4.6.1.IOT(Index Organized Table) Partition

IOT가 oracle8에서 소개되면서 index segment로 table 형태의 관리를 지원할 수

있는 효과적인 방안을 제시했었다. Oracle10g는 보다 효율적인 IOT사용을 위해

다음과 같은 기능을 지원한다.

1. oracle9i에서 소개된 list partition을 local partitioned IOT에 적용할 수 있다.

(oracle9i는 heap table만을 지원했었다)

2. 이전 버전에서는 partition관리 작업이 수행된 후 global index의 성능에 문제가

있었다. 이는 drop, truncate, exchange와 같은 operation은 global index를

unusable로 만들지만 move, split, merge와 같은 operation은 global index를

unusable이 아닌 상태로 유지함으로 해당 index를 access하는데 나타난 것이다. 즉,

IOT의 특성상 global index에 대한 guess-DBA(row의 유효성 검증)를 하는 과정에서

global index 상태는 usable이더라도 partition operation으로 인해 index rows가

이미 invalidate 되었기 때문에 발생한 성능상의 문제점들이 oracle10g에서

해결되었다는 뜻이다. (global index가 usable이지만 index rows가 invalidate

되었던 문제들이 해소되었다)

3. bitmap mapping table의 의미가 확장되면서 local partitioned IOT에 대해서도

bitmap index를 생성할 수 있게 되었다.

4. 모든 형태의 partitioned IOT에서 LOB columns을 사용할 수 있다.

간단하게 list partition을 만들어 보자.

SCOTT> create table iot_list (id number, addr varchar2(2), amount number,

2 hiredata date, constraint pk_iotlist primary key(id))

3 organization index

4 partition by list(addr)

5 (partition sale_east values ('ea', 'eb'),

6 partition sale_west values ('wa', 'wb'),

7 partition sale_south values ('sa', 'sb') tablespace tools,

8 partition sale_north values ('na', 'nb') tablespace tools);

partition by list(addr)

*

ERROR at line 4:

ORA-25199: partitioning key of a index-organized table must be a subset of

the

[email protected] 82

Page 83: O10g app support_11

http://www.ggola.com 장 경 상

primary key

SCOTT> create table iot_list (id number, addr varchar2(2), amount number,

2 hiredata date, constraint pk_iotlist primary key(id, addr))

3 organization index

4 partition by list(addr)

5 (partition sale_east values ('ea', 'eb'),

6 partition sale_west values ('wa', 'wb'),

7 partition sale_south values ('sa', 'sb') tablespace tools,

8 partition sale_north values ('na', 'nb') tablespace tools);

Table created.

SCOTT> insert into iot_list values (10, 'ea', 100, sysdate -1);

1 row created.

SCOTT> insert into iot_list values (20, 'wb', 200, sysdate -2);

1 row created.

SCOTT> insert into iot_list values (30, 'sa', 300, sysdate -3);

1 row created.

SCOTT> insert into iot_list values (40, 'nb', 400, sysdate -4);

1 row created.

SCOTT> commit;

Commit complete.

첫 번째 SQL에서 partition key가 primary key의 subset이 아니어서 error가

[email protected] 83

Page 84: O10g app support_11

http://www.ggola.com 장 경 상

발생했지만 primary key를 수정하자 정상적으로 작업이 성공했다.

이제 테스트 진행을 위해 global index를 만들어보자.

SCOTT> create index iotlist_gi on iot_list(amount)

2 global partition by range (amount)

3 (partition p1 values less than (201),

4 partition p2 values less than (maxvalue));

Index created.

SCOTT> select index_name, partition_name, status

2 from user_ind_partitions

3 where index_name = 'IOTLIST_GI';

INDEX_NAME PARTITION_NAME STATUS

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

IOTLIST_GI P1 USABLE

IOTLIST_GI P2 USABLE

다음은 partition operation을 통해 local partitioned IOT의 global index 상태가

어떤 변화를 일으키는지 확인해보는 과정이다.

SCOTT> alter table iot_list move partition sale_east

2 tablespace user_default update global indexes;

alter table iot_list move partition sale_east

*

ERROR at line 1:

ORA-25182: feature not currently available for index-organized tables

SCOTT> alter table iot_list move partition sale_east

2 tablespace user_default;

Table altered.

SCOTT> select index_name, partition_name, status

2 from user_ind_partitions

3 where index_name = 'IOTLIST_GI';

[email protected] 84

Page 85: O10g app support_11

http://www.ggola.com 장 경 상

INDEX_NAME PARTITION_NAME STATUS

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

IOTLIST_GI P1 USABLE

IOTLIST_GI P2 USABLE

IOT에 대한 update global indexes option은 error를 return하였지만 move

partition을 통해 global index가 그대로 유지되고 있음이 확인된다. 바로 이런

상태에서 이전 버전에서는 guess-DBA를 할 수가 없어서 성능의 문제가 있을 수

있었지만 oracle10g는 이 문제들을 해결했다는 것이다.

SCOTT> alter table iot_list truncate partition sale_west;

Table truncated.

SCOTT> select index_name, partition_name, status

2 from user_ind_partitions

3 where index_name = 'IOTLIST_GI';

INDEX_NAME PARTITION_NAME STATUS

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

IOTLIST_GI P1 UNUSABLE

IOTLIST_GI P2 UNUSABLE

여전히 truncate partition은 global index를 unusable로 만들고 있다.

위 예들을 통해서 IOT의 특성상 다른 일반 partition table처럼 partition DDL과

update global index를 통해 global index를 정상적으로 update할 수는 없지만

move, split, merge partition은 local partitioned IOT에서 자동으로 global index

를 관리해준다는 것을 살펴보았다.

4.6.2.Bitmap Index

Oracle10g는 DML operation의 발전으로 index관련 성능상의 문제들이 좋아졌다. 그

것은 bitmap index에도 마찬가지여서 특정 DML로 인해 성능이 떨어지는 bitmap

index의 문제들이 다소 해소되었다. 따라서 bitmap index의 성능도 더욱 좋아졌고

fragment도 전보다 덜하다.

다음은 이런 향상된 features가 제대로 적용할 수 있는 환경이다.

[email protected] 85

Page 86: O10g app support_11

http://www.ggola.com 장 경 상

1. database compatible 10.0.0.0 이후에 생성된 bitmap index

2. compatible 10.0.0.0 이전에 만들어진 bitmap index의 경우 compatible을

10.0.0.0 이상으로 올린 후 해당 bitmap index 에 첫 번째 DML이 일어날 때

부분적으로 효과가 있다.

3. 어느 경우이든 compatible 10.0.0.0 이전에 만들어진 bitmap index는

compatible 10.0.0.0 이상으로 database가 start된 후 rebuilding을 해주는 것이

완전한 효과를 볼 수 있는 길이다.

4.6.3.Update Local Partitioned Index

Partition DDL로 인해 영향을 받는 local index partition이 새로운 segment로

만들어져야 할 때는 default tablespace나 table과 같은 tablespace에 위치하는

것이 일반적인 형태였다. Oracle10g는 option을 통해 index partition에 대한 조정을

partition DDL과 동시에 할 수 있도록 하였으며 unusable 상태로 변하게 되는 local

partitioned index도 동시에 처리할 수 있는 방법을 제공한다.

테스트를 위해 앞서 IOT와 유사하게 만들고 local index를 추가하자.

SCOTT> create table pt_sales

2 (id number, addr varchar2(2), amount number,hiredata date)

3 partition by list(addr)

4 (partition sale_east values ('ea', 'eb'),

5 partition sale_west values ('wa', 'wb'),

6 partition sale_south values ('sa', 'sb') tablespace tools,

7 partition sale_north values ('na', 'nb') tablespace tools);

Table created.

SCOTT> insert into pt_sales values (10, 'ea', 100, sysdate -1);

1 row created.

SCOTT> insert into pt_sales values (20, 'wb', 200, sysdate -2);

1 row created.

SCOTT> insert into pt_sales values (30, 'sa', 300, sysdate -3);

[email protected] 86

Page 87: O10g app support_11

http://www.ggola.com 장 경 상

1 row created.

SCOTT> insert into pt_sales values (40, 'nb', 400, sysdate -4);

1 row created.

SCOTT> commit;

Commit complete.

SCOTT> create index pt_sales_li1 on pt_sales(hiredata) local;

Index created.

SCOTT> select partition_name, tablespace_name, status

2 from user_ind_partitions

3 where index_name = 'PT_SALES_LI1';

PARTITION_NAME TABLESPACE_NAME STATUS

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

SALE_EAST USER_DEFAULT USABLE

SALE_NORTH TOOLS USABLE

SALE_SOUTH TOOLS USABLE

SALE_WEST USER_DEFAULT USABLE

Local index의 tablespace는 table partition과 동일함을 확인했다. Partition DDL

과 index partition의 변화를 partition DDL option의 사용 전후에 따라 비교해 보자.

#1 without option (previous oracle10g)

SCOTT> alter table pt_sales move partition sale_north

2 tablespace bigdata;

Table altered.

SCOTT> select partition_name, tablespace_name, status

2 from user_ind_partitions

[email protected] 87

Page 88: O10g app support_11

http://www.ggola.com 장 경 상

3 where index_name = 'PT_SALES_LI1';

PARTITION_NAME TABLESPACE_NAME STATUS

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

SALE_EAST USER_DEFAULT USABLE

SALE_NORTH TOOLS UNUSABLE

SALE_SOUTH TOOLS USABLE

SALE_WEST USER_DEFAULT USABLE

Table partition sale_north의 move command로 인하여 연결된 index partition

sale_north가 unusable 상태로 바뀌었다.

#2 with new oracle10g options

SCOTT> alter table pt_sales move partition sale_south

2 tablespace bigdata update indexes

3 (pt_sales_li1 (partition sale_south tablespace bigdata));

Table altered.

SCOTT> select partition_name, tablespace_name, status

2 from user_ind_partitions

3 where index_name = 'PT_SALES_LI1';

PARTITION_NAME TABLESPACE_NAME STATUS

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

SALE_EAST USER_DEFAULT USABLE

SALE_NORTH TOOLS UNUSABLE

SALE_SOUTH BIGDATA USABLE

SALE_WEST USER_DEFAULT USABLE

새로운 option “update indexes”와 index절을 통한 index partition 지정을 통해

table partition sale_south와 연결된 index partition sale_south를 bigdata

tablespace로 옮김과 동시에 index update를 통해 그 상태도 usable로 유지할 수

있도록 하였다.

4.6.4.Unusable Index

대용량 database를 위한 partition table의 사용이 필수적이라면 그에 따른

[email protected] 88

Page 89: O10g app support_11

http://www.ggola.com 장 경 상

반대급부도 있기 마련이다.

특정 data를 조절하기 위해 전체 table이 아닌 partition operation을 적절히

사용하는 것은 매우 좋은 선택이다. 그러나 이것이 성능상의 많은 이점은 줄 수 있으나

그에 따른 관련 indexes의 unusable 상태문제들이 있기 때문에 이를 해결하기 위하여

oracle version마다 새로운 options도 추가되고 있다. 그 예로 oracle9i의 update

global indexes나 위에서 언급된 update indexes와 같은 것도 그 한 예이다.

지금 설명하려는 것도 그 중 하나이다. 일부 data에 대한 처리를 위해 partition

operation을 수행하고 그로 인해 해당 partition과 연결된 index partition이

unusable이 되면 해당 partition에 data를 입력하는 일은 불가능하다. Oracle8i

에서는 session level에서 “alter session set skip_unusable_indexes = true”를

SQL이 parsing이 되기 전에 사용하여 SQL parsing단계에서 unusable index를

ignore함으로써 data작업이 가능하도록 하는 방법을 제시했다. 이는 일단 data의

입력은 처리하고 후에 index를 처리할 수 있는 기법이다. (사실은 SQL parsing에

영향을 주는 기법이지만) 하지만 이러한 방식은 동시 다발적으로 발생하는 multi-

session의 concurrent 작업에 있어서 좋은 솔루션이 될 수는 없었다.

이제 oracle10g는 이 parameter를 dynamic initial parameter로 적용할 수 있도록

하여 그 기능을 확장하였다. 즉, session과 상관없이 database level에서 모든

session에게 영향을 주는 것이다. 기본적으로 true를 default value로 사용하기

때문에 SQL parsing을 할 때 unusable indexes를 무시하도록 설정이 된다. (원치

않으면 false로 바꾸라)

그러나 사용자의 입장에서는 error가 나타나지 않기 때문에 관련 SQL이 사실은 해당

indexes를 무시하고 suboptimal execution plan을 사용한다는 것을 모르게 된다.

그래서 oracle10g는 이런 indexes의 unusable상태가 발생하면 alert.log에 어떤

index가 언제 unusable로 mark되었다는 message를 출력하게 된다.

4.6.5.Hash Partitioned Global Index

Oracle8i에서 소개된 hash partition은 global index에 적용될 수 없다는 문제가

있었다. 이제 oracle10g는 global index에도 hash partition을 적용할 수 있게

되었다.

따라서 hash partitioned global index에 대한 add 및 coalesce partition

command가 지원됨은 물론이다. 그렇다면 이런 기법들이 실제로 어떤 효과가 있을까?

[email protected] 89

Page 90: O10g app support_11

http://www.ggola.com 장 경 상

가장 손쉽게 생각할 수 있는 것이 oracle이 generate하는 sequence다. 예를 들어

multi-user concurrent access가 일어나는 OLTP 환경에서 increment by를 1로(

대부분 1로 설정하니까) 설정한 sequence를 사용하는 경우 index block에 대한

contention이 충분히 예측될 수 있다. 게다가 그 환경이 RAC 환경이고 node에 구분이

없이 application의 접속이 이루어진다면 updated index block에 대한 node간

전송까지 발생할 것임으로 경우에 따라서는 block contention의 정도가 심할 수도

있을 것이다.

이런 이유로 oracle8부터 reverse key index를 소개하면서 index block에 대한

contention을 어느 정도 해소할 수 있는 방안을 제시한 바 있다. 그러나 reverse key

index는 하나의 index tree구조에만 이점이 있지 이를 여러 partition으로 분산하는

효과는 없다. 설사 이런 column에 대한 index를 range partition으로 구성하더라도

partition key의 range 속성상 마찬가지다.

이제 oracle10g에서 이런 유형의 column에 hash partitioned global index를

지원하게 되어 index tree를 분산할 수 있고 게다가 reverse key까지 적용한다면 더욱

더 큰 분산효과까지 얻을 수 있게 되었다.

아래 표는 위에서 설명한 유형의 column에 대하여 partition과 index type간의

차이를 보여주는 개념도이다.

다음은 hash partitioned global index를 만들기 위한 환경구성이다. 먼저 table을

만들어 무작위로 연속된 data를 insert한다.

SCOTT> conn system/manager

Connected.

SYSTEM> grant create sequence to scott;

[email protected] 90

그림 4-1

Hash partitioned global index 이점

Page 91: O10g app support_11

http://www.ggola.com 장 경 상

Grant succeeded.

SYSTEM> conn scott/tiger

Connected.

SCOTT> create table g_tab_hash (id number, gid number)

2 tablespace bigdata;

Table created.

SCOTT> create sequence g_tabhash_seq

2 minvalue 1 maxvalue 999999999

3 increment by 1;

Sequence created.

SCOTT> begin

2 for i in 1..100000 loop

3 insert into g_tab_hash values (g_tabhash_seq.nextval,

g_tabhash_seq.currval);

4 end loop;

5 end;

6 /

PL/SQL procedure successfully completed.

SCOTT> commit;

Commit complete.

SCOTT> select count(*) from g_tab_hash;

COUNT(*)

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

100000

[email protected] 91

Page 92: O10g app support_11

http://www.ggola.com 장 경 상

SCOTT> select max(id), max(gid) from g_tab_hash;

MAX(ID) MAX(GID)

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

100000 100000

SCOTT> select min(id), min(gid) from g_tab_hash;

MIN(ID) MIN(GID)

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

1 1

모두 1부터 100000까지 값을 갖는 data를 sequence를 통해 만들었다. 이 table을

기준으로 두 가지 방식의 hash partitioned global index를 만들어 보자.

SCOTT> create index g_tabhash_i1 on g_tab_hash(id)

2 global partition by hash(id) (

3 partition hp1 tablespace tools,

4 partition hp2 tablespace bigdata,

5 partition hp3, partition hp4);

Index created.

SCOTT> create index g_tabhash_i2 on g_tab_hash(gid)

2 global partition by hash (gid)

3 partitions 6

4 store in (tools, bigdata, user_default)

5 reverse;

Index created.

첫 번째 SQL은 4개의 hash partition으로 구성하되 그 이름을 지정하였고 3, 4번째

partition의 경우 저장될 tablespace name을 지정하지 않은 경우이고 두 번째 SQL은

6개의 partition 수를 지정하고 저장될 tablespace list를 3개만 설정하면서 reverse

key index로 구성한 형태이다. 그러면 실제로 어떻게 저장이 되었는지 확인해 보자.

SCOTT> select index_name, partition_name, tablespace_name

[email protected] 92

Page 93: O10g app support_11

http://www.ggola.com 장 경 상

2 from user_ind_partitions

3 where index_name in ('G_TABHASH_I1', 'G_TABHASH_I2');

INDEX_NAME PARTITION_ TABLESPACE_NAME

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

G_TABHASH_I1 HP1 TOOLS

G_TABHASH_I1 HP2 BIGDATA

G_TABHASH_I1 HP3 USER_DEFAULT

G_TABHASH_I1 HP4 USER_DEFAULT

G_TABHASH_I2 SYS_P221 TOOLS

G_TABHASH_I2 SYS_P222 BIGDATA

G_TABHASH_I2 SYS_P223 USER_DEFAULT

G_TABHASH_I2 SYS_P224 TOOLS

G_TABHASH_I2 SYS_P225 BIGDATA

G_TABHASH_I2 SYS_P226 USER_DEFAULT

10 rows selected.

첫 번째 index는 tablespace를 지정한 두 partition을 제외한 나머지 partitions을

해당 user의 default tablespace를 사용했다. 두 번째 index는 oracle이 generate한

이름으로 partition이름을 생성하면서 지정된 3개의 tablespace를 round-robin

방식으로 사용했음을 알 수 있다.

CF. tablespace가 지정되지 않은 hash partition의 storage는 다음과 같은 순서에

의해 결정된다. (store in tablespace list user default tablespace system

default tablespace)

Hash partition operation에 주로 사용되는 add, coalesce command를 테스트

해보자.

SCOTT> alter index g_tabhash_i1 add partition hp5

2 tablespace bigdata;

Index altered.

SCOTT> delete from g_tab_hash where rownum < 50001;

[email protected] 93

Page 94: O10g app support_11

http://www.ggola.com 장 경 상

50000 rows deleted.

SCOTT> alter index g_tabhash_i2 coalesce partition;

Index altered.

SCOTT> select index_name, partition_name, tablespace_name

2 from user_ind_partitions

3 where index_name in ('G_TABHASH_I1', 'G_TABHASH_I2');

INDEX_NAME PARTITION_ TABLESPACE_NAME

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

G_TABHASH_I1 HP1 TOOLS

G_TABHASH_I1 HP2 BIGDATA

G_TABHASH_I1 HP3 USER_DEFAULT

G_TABHASH_I1 HP4 USER_DEFAULT

G_TABHASH_I1 HP5 BIGDATA

G_TABHASH_I2 SYS_P227 TOOLS

G_TABHASH_I2 SYS_P228 BIGDATA

G_TABHASH_I2 SYS_P229 USER_DEFAULT

G_TABHASH_I2 SYS_P230 TOOLS

G_TABHASH_I2 SYS_P231 BIGDATA

10 rows selected.

첫 번째 index에 새로운 hash partition hp5를 추가하였고 table data의 50%를

삭제한 후 두 번째 index에 대하여 coalesce를 진행하였다. 그 결과로 첫 번째 index

는 partition이 하나 늘어났고 두 번째 index는 partition이 1개 줄어들면서 partition

data들이 재 분배가 되었다. 따라서 대량의 DML이 발생하면 주기적인 coalesce와

같은 작업들을 진행하는 것이 성능에 도움이 될 것이다.

CF. hash partitioned global index에는 split partition command를 사용할 수

없으며 modify partition command의 경우 unusable option만을, modify default

attributes command의 경우 tablespace만을 지정할 수 있다.

[email protected] 94

Page 95: O10g app support_11

http://www.ggola.com 장 경 상

CF. range partitioned index의 경우 parallel access를 할 때 access하는 partition

의 수 만큼만 parallel수를 지정할 수 있다. 즉, partition pruning을 통해 필요한

partition만 각각의 slave process가 작업을 담당을 하는 것이다. 하지만 hash

partitioned global index의 경우 partition pruning을 하지 않고 모든 partition이

parallel로 access될 수 있다. (parallel fast full scan) 사실 이 말은 range partition

과 hash partition의 수가 같고 분포가 같으면 의미가 없다. 진정한 의미는 hash

partitioned global index가 보다 많은 partition에 data 분산을 가져올 수 있기

때문에 더 많은 parallel processes를 사용할 가능성이 range partitioned index보다

훨씬 높다고 이해를 해야 할 것이다.

CF. hash partition의 이름을 지정하지 않고 숫자만 지정하는 경우 과거 oracle8i

에서는 그 이름을 oracle이 만드는 SYS_Pnn의 형식을 가졌지만 oracle10g는

확장성이 고려되었는지 SYS_Pnnn으로 숫자가 하나 더 늘었다.

CF. 물론 여전히 reverse key index의 속성상 이 index에 대한 access는 equal 또는

in-list 조건만을 허용한다.(“=”, “in (….)” 만 지원하며 index range scan은

불가하다. 따라서 reverse key index column에 범위를 조건으로 주는 query의

경우엔 index full scan 내지는 table scan이 발생할 것이다)

[email protected] 95

Page 96: O10g app support_11

http://www.ggola.com 장 경 상

OCP point

==============================================

=================

1. partition 변화에 따른 local partition index의 상태를 usable로 유지하는 option

2. skip_unusable_indexes parameter의 설정과 이에 따른 SQL error message

처리와 alert log와의 관계

참조

==============================================

=================

IOT : o8 68p, o8i 44p

list partition : o9i 174p

guess-DBA : o8i 45p

bitmap index : ob 34p, o8i 38p, o9i 216p

update global indexes : o9i 168p

hash partition : o8i 46p

reverse key : o8 67p, o8i 39p

[email protected] 96

Page 97: O10g app support_11

http://www.ggola.com 장 경 상

4.7. Scheduling

4.7.1.Advanced Scheduling

전통적으로 oracle은 job이라는 기능을 통해 scheduling을 지원해 왔다. Oracle10g

에서 말하는 advanced scheduling이란 바로 새로운 package인 dbms_scheduler

를 사용함으로써 과거 dbms_job으로 지원하던 기능보다 훨씬 더 많은 기능들을

제공할 수 있게 되었다는 것이다. 이 package는 DBA나 개발자들로 하여금 다양하고

복잡한 많은 작업들을 scheduling할 수 있도록 해준다.

CF. 예를 들어 일일 backup, 주 단위 MView refresh, 정기 batch job, 월 단위

통계수집 등 무궁무진한 작업을 설정할 수 있는데도 (oracle10g는 이제 OS scripts도

지원한다) 불구하고 대부분의 사이트들에서 이런 oracle이 제공하는 job scheduling

을 잘 사용하지 않고 있다는 점은 매우 안타까운 일이다.

4.7.2.Scheduler Components

Oracle10g의 scheduler는 3개의 기본 components로 구성이 되어있다.

1. job : 무엇을 또는 언제 할 것인가를 지정한다. 이 job은 곧 현재 존재하는 program

이나 schedule이 될 수도 있다. (what, when program, schedule)

2. schedule : 언제 몇 회에 걸쳐서 job이 수행될 것인가를 지정한다.

3. program : 실제로 수행될 program을 지정한다. (scripts, procedure 등의

metadata)

CF. scheduler components는 database schema의 하부구조로 구성되며 일반적인

database naming rule을 따르게 된다. 따라서 각 components의 이름들은 모두

SQL namespace에서 unique해야 한다. 즉, 각각은 독립된 objects로 database에

저장이 된다.

CF. 위에서 필자가 program은 schedule에 따라 수행할 metadata의 모음이고

schedule은 언제, 얼마나 자주 수행할 것인가를 정의하여 독립적으로 저장된다고

했다. 이는 곧 program과 schedule은 여러 개의 jobs이 하나의 program과

schedule을 같이 사용할 수 있는 재사용이(reuse) 가능한 objects라는 의미가 된다.

즉, parameter의 형태를 바꾸어 overriding을 통해 하나의 program을 여러 jobs이

공유할 수도 있고 또 각각의 jobs이 하나의 schedule에 의해 통제될 수도 있다는

뜻이다.

위 3가지 기본 components는 scheduling을 위한 가장 기초적인 개념이다. 다음은

[email protected] 97

Page 98: O10g app support_11

http://www.ggola.com 장 경 상

이런 scheduling에 필요한 또는 연결되는 다른 components에 대한 설명이다. 이 중

resource와 관련한 것은 oracle8i에서 소개된 개념임으로 여기서는 schedule과

관련하여서만 설명을 한다.

1. job class : 이는 job의 모임 즉, 공통의 resource를 사용하는 job들을 하나로 묶는

group을 의미한다. 따라서 하나의 job은 반드시 하나의 job class에만 속할 수 있다.

2. resource consumer group : 하나 이상의 job classes와 연결되며 job class에

할당되는 resource를 결정한다. 따라서 하나의 job class는 하나의 resource

consumer group에 속할 수 있다.

3. resource plan : resource에 대한 우선순위를 결정한다.

4. window : time interval을 설정하며 세부적인 설정을 통해 시간대별 resource

plan을 다르게 활성화 시킬 수도 있다. 즉, resource plan의 선택과 설정을 지원한다.

5. window group : 위 window들의 모임 즉, group을 의미한다. 여러 유형의 time

interval들을 묶어서 group으로 관리할 수 있다.

CF. job, schedule, program은 current user의 schema로 만들어지지만 job class,

window, window group은 sys schema로 만들어진다.

다음은 scheduling을 제대로 사용하기 위한 권한들에 대하여 알아보자.

1. creation : grant (any) create job (job, program, scheduler 생성권한)

2. management : grant manage scheduler (job class, window, window group

의 create, drop, alter 권한과 stop any job(force option사용가능) 권한, 실제

설정된 시간보다 앞서서 start and stop window 권한)

3. scheduler components execution : grant execute on (any) program/class

4.: scheduler_admin role : scheduling에 필요한 모든 system 권한의 모음

CF. scheduler_admin : create job, create any job, execute any program,

execute any class, manage scheduler

4.7.3.Basic Scheduler Component 생성Job을 만들기 위해서는 앞서 설명한 3가지 기본 components를 지정해야 한다. 그

components는 job을 생성하면서 직접 지정을 하든 아니면 원래 있던 것을 사용하든

상간은 없다. 따라서 그 개념에 비추어 본다면 job을 만드는 방식은 다음과 같은 것들이

있을 것이다.

1. job을 생성하면서 실제 사용할 program과 schedule을 직접 입력하거나 기존에

저장된 program, schedule을 직접 지정한다.

2. 실제 program을 직접 입력하고 기존의 schedule을 지정한다.

[email protected] 98

Page 99: O10g app support_11

http://www.ggola.com 장 경 상

3. schedule은 직접 입력하고 기존에 저장이 되어있던 program을 지정한다.

CF. 사실 위 유형은 package와 직접 연관이 있다. 1번의 두 가지 형태와 2, 3번을 합쳐

총 4가지 방식을 지원하는 dbms_scheduler.create_job procedure가 override되어

4개가 존재한다는 의미이다.

어떤 형태로 job을 생성하든 해당 job이 수행하는 실제 program은 3가지 type을 가질

수 있게 되는데 이는 job_type으로 대표된다. 이렇게 설정한 job_type은 job_action

으로 지정되는 값을 어떻게 실행하는가를 표현하게 되며 job_action의 값은 job_type

에 따라 수행할 procedure 이름이 될 수도 있고 script이름이나 OS command가 될

수도 있으며 anonymous PL/SQL code block이 될 수도 있다.

JOB_TYPE JOB_ACTION

PLSQL_BLOCK anonymous PL/SQL code block

STORED_PROCEDURE named stored procedure, Java, external procedure

EXECUTABLE named script 또는 OS command

CF. job_type을 stored_procedure로 설정하는 경우에는 inout 또는 out parameter

를 가진 stored procedure는 사용할 수 없으며 return value가 필요한 function도

사용할 수 없다. 또한 plsql_block을 사용하는 경우에는 반드시 semi colon(;)으로

끝나는 완전한 문장을 구사해야 한다.

다음은 과거부터 가장 흔하게 사용되던 named procedure를 통한 job의 수행을

oracle10g scheduler를 통해 구현한 예이다. 현재 테스트를 위해 사용하는 계정

scott에 대한 통계수집을 매일 한차례 수행하는 scheduling job을 만들어 보자.

SCOTT> conn system/manager

Connected.

SYSTEM> exec dbms_scheduler.create_job( -

> job_name => 'STAT_SCOTT_10', -

> job_type => 'STORED_PROCEDURE', -

> job_action => 'sys.dbms_stats.gather_schema_stats(''SCOTT'', estimate_percent

=> 10)', -

> start_date => trunc(sysdate) + 25/24, -

> repeat_interval => 'trunc(sysdate+1) + 25/24', -

> end_date => trunc(sysdate+7) + 25/24, -

> enabled => true, -

[email protected] 99

표 4-4

Job Type과 Job Action

Page 100: O10g app support_11

http://www.ggola.com 장 경 상

> comments => 'Gathering the stats of scott 1 time per day!');

BEGIN dbms_scheduler.create_job( job_name => 'STAT_SCOTT_10',

job_type => 'STORED_PROCEDURE', job_action =>

'sys.dbms_stats.gather_schema_stats(''SCOTT'', estimate_percent => 10)',

start_date => trunc(sysdate) + 25/24, repeat_interval =>

'trunc(sysdate+1)

+ 25/24', end_date => trunc(sysdate+7) + 25/24, enabled => true,

comments => 'Gathering the stats of scott 1 time per day!');

END;

*

ERROR at line 1:

ORA-27452: sys.dbms_stats.gather_schema_stats('SCOTT', estimate_percent

=> 10)

is an invalid name for a database object.

ORA-06512: at "SYS.DBMS_ISCHED", line 99

ORA-06512: at "SYS.DBMS_SCHEDULER", line 262

ORA-06512: at line 1

정상적으로 한 것 같지만 error가 return되었다. 아래처럼 job_type을 바꾸어 다시

해보자.

SYSTEM> exec dbms_scheduler.create_job( -

> job_name => 'STAT_SCOTT_10', -

> job_type => 'PLSQL_BLOCK', -

> job_action => 'sys.dbms_stats.gather_schema_stats(''SCOTT'', estimate_percent

=> 10);', -

> start_date => trunc(sysdate) + 25/24, -

> repeat_interval => 'trunc(sysdate+1) + 25/24', -

> end_date => trunc(sysdate+7) + 25/24, -

> enabled => true, -

> comments => 'Gathering the stats of scott 1 time per day!');

PL/SQL procedure successfully completed.

SYSTEM>

[email protected] 100

Page 101: O10g app support_11

http://www.ggola.com 장 경 상

작업이 정상적으로 수행되었다. 위 내용의 의미는 다음과 같다. Oracle의 scheduling

job을 사용하기 위해 job의 이름은 “STAT_SCOTT_10”으로 하는 “PLSQL_BLOCK”

type으로 dbms_stats package를 이용하여 통계를 gathering하는 작업을

등록하였다. 이 작업은 익일 01시에 시작되며 24시간 단위로 재 구동된다. 그리고 7일

후에는 작업을 더 이상 수행하지 않도록 end_data를 설정하였다. 또한 작업 등록과

동시에 활성화 되도록 enabled를 true로 설정하였고(default는 false임으로 등록과

동시에 작업을 활성화 하기 위해선 이 parameter를 지정해야 한다) 작업에 대한

간단한 설명을 comments로 등록 하였다.

CF. 사실 위의 간단한 예제는 이전 버전의 dbms_job으로 등록하는 작업과 큰 차이는

없다. 보다 복잡하고 유연한 scheduling을 구사해야 oracle10g의 강점을 살릴 수

있을 것이다.

CF. 몇 차례의 테스트 결과 job_type을 STORED_PROCEDURE로 하는 경우에

parameter가 있으면 모두 error가 return 되었다.

4.7.4.Calendaring Expressions

앞서 생성한 job은 repeat_interval을 datetime을 형태로 표현한 것이었다.

전통적으로 사용해온 날짜와 function을 이용한 계산법으로 주로 sysdate에서 “+”, “-

“등의 계산 수식을 통해 만들어내는 것이었다. 그러나 아시다시피 이 방법은 입맛에

맞는 적절한 날짜를 구하기 위해선 매우 복잡한 형태의 logic이 필요할 수 있고 보는

사람도 이해를 하기가 어려운 측면이 분명히 있다. 이제 oracle10g가 소개하는

calendaring 표현을 이해하면 훨씬 수월할 것이다. 아래 표현은 무엇을 의미 하는가.

repeat_interval => ‘FREQ=HOURLY; INTERVAL 8; BYDAY=MON’

이 말은 반복은 시간단위로 주기는 8이라는 뜻이며 매 월요일에 수행한다는 뜻이다.

반복을 시간단위로 설정하였으니 주기는 8시간이라는 의미가 된다. 따라서 “매 월요일

8시간마다”라는 의미의 schedule이 되는 것이다. 충분히 이해할 수 있는 표현이다.

물론, INTERVAL이나 BYDAY가 없다면 매 “시간마다” 라는 의미가 되니 뒤에 있는 것은

option으로 설정이 가능하다고 보면 되겠다. 정리해 보면.

FREQuency INTERVAL BYxxx

YEARLY 1 ~ 999 BYMONTH

MONTHLY BYWEEKNO

WEEKLY BYYEARDAY

DAILY BYMONTHDAY

[email protected] 101

표 4-5

Calendar

Expression

Page 102: O10g app support_11

http://www.ggola.com 장 경 상

HOURLY BYDAY

MINUTELY BYHOUR

SECONDLY BYMINUTE

BYSECOND

앞서 테스트한 “매일 새벽 1시”를 좀 더 세밀하게 “매일 새벽 1시 30분”으로 하여

calendaring 표현으로 바꾸면 “FREQ=DAILY, BYHOUR=1; BYMINUTE=30” 이렇게

할 수 있다. 또한 BYDAY=2MON 혹은 BYDAY=-1SAT처럼 2번째 월요일 혹은 끝에서

첫 번째(즉, 마지막) 토요일과 같은 세밀한 표현이 가능하다.

보다 복잡한 형태를 알아보자. 통계작업을 수행하는데 업무시간과 배치 작업시간,

그리고 마감시점 등을 계산한 결과 “매달 중순 새벽 3시 20분에 1회 수행”이 좋다고

판단되면 다음과 같이 하면 된다.

repeat_interval => ‘FREQ=MONTHLY; BYMONTHDAY=15; BYHOUR=3;

BYMINUTE=20’

다음은 “BY”로 표현되는 절들의 list를 정리한 내역이다.

bymonth_clause = "BYMONTH" "=" monthlist

monthlist = monthday ( "," monthday) *

month = numeric_month | char_month

numeric_month = 1 | 2 | 3 ... 12

char_month = "JAN" | "FEB" | "MAR" | "APR" | "MAY" | "JUN" |"JUL" |

"AUG" | "SEP" | "OCT" | "NOV" | "DEC"

byweekno_clause = "BYWEEKNO" "=" weeknumber_list

weeknumber_list = weekday ( "," weeknumber)*

week = [minus] weekno

minus = "-"

weekno = 1 through 53

byyearday_clause = "BYYEARDAY" "=" yearday_list

yearday_list = yearday ( "," yearday)*

yearday = [minus] yeardaynum

yeardaynum = 1 through 366

bymonthday_clause = "BYMONTHDAY" "=" monthday_list

monthday_list = monthday ( "," monthday) *

monthday = [minus] monthdaynum

[email protected] 102

Page 103: O10g app support_11

http://www.ggola.com 장 경 상

monthdaynum = 1 through 31

byday_clause = "BYDAY" "=" byday_list

byday_list = byday ( "," byday)*

byday = [weekdaynum] day

weekdaynum = [minus] daynum

daynum = 1 through 53 /* if frequency is yearly */

daynum = 1 through 5 /* if frequency is monthly */

day = "MON" | "TUE" | "WED" | "THU" | "FRI" | "SAT" | "SUN"

byhour_clause = "BYHOUR" "=" hour_list

hour_list = hour ( "," hour)*

hour = 0 through 23

byminute_clause = "BYMINUTE" "=" minute_list

minute_list = minute ( "," minute)*

minute = 0 through 59

bysecond_clause = "BYSECOND" "=" second_list

second_list = second ( "," second)*

second = 0 through 59

4.7.5.Job Using Program & Schedule

이제 일반 계정에 권한을 부여하고 program과 schedule을 만들어서 job을 생성해

보자. 먼저 권한을 부여하고 작업할 procedure를 하나 생성한다. 이 procedure는

통계치를 전체 data의 80% 수준을 가지고 계산하여 거의 정확한 값을 구해내는

것이다.

SYSTEM> grant create job to scott;

Grant succeeded.

SYSTEM> conn scott/tiger

Connected.

SCOTT> create or replace procedure gather_me is

2 begin

3 dbms_stats.gather_schema_stats('SCOTT', estimate_percent => 80);

4 end;

5 /

Procedure created.

[email protected] 103

Page 104: O10g app support_11

http://www.ggola.com 장 경 상

다음 단계로 프로그램을 만들자. 위 procedure를 “STORED_PROCEDURE” type으로

하여 생성한다.

SCOTT> exec dbms_scheduler.create_program(-

> program_name => 'SCOTT_FULL',-

> program_action => 'gather_me',-

> program_type => 'STORED_PROCEDURE',-

> enabled => true);

PL/SQL procedure successfully completed.

현재 enabled의 값을 true로 지정하였고 이 뜻은 program생성이 되면서 지정된

program의 validity를 확인한다는 의미이다. Default는 false이며 이 경우엔 아직

program이 생성되지 않는다. 추후 enable procedure를 통해 exclusively 생성할 수

있다. (gather_me가 scott이 만든 것이 아니라면 ownership을 명시해서 표현해

주어야 한다. 예를 들어 gather_me가 “xman”이라는 계정의 것이면

“xman.gather_me”라고 직접 지정한다)

CF. 앞서 job_type이 “STORED_PROCEDURE”일 때 error가 있었지만 이번엔

parameter가 없기 때문에 별 문제가 안 된다.

다음으로 schedule을 만들어 보자. 이 schedule은 매달 마지막 토요일 새벽 1시에

valid한 것으로 1년에 12회 수행되는 작업들 중 주로 업무시간이 아닌 시간대에 작업이

필요할 경우 사용할 수 있는 것이다.

SCOTT> exec dbms_scheduler.create_schedule(-

> schedule_name => 'scd_every_month',-

> start_date => sysdate,-

> repeat_interval => 'FREQ=MONTHLY; BYDAY=-1SAT; BYHOUR=1',-

> comments => 'every last saturday am 1');

PL/SQL procedure successfully completed.

이 스케쥴은 매달 뒤에서 첫 번째 토요일(즉 마지막 토요일) 새벽 1시에 수행하는

스케쥴로서 start_date는 이 schedule이 valid되는 시점(현재는 즉시)을 의미하며

end_date가 지정되지 않았기 때문에 만료시점이 없이 계속 유효하다.

[email protected] 104

Page 105: O10g app support_11

http://www.ggola.com 장 경 상

CF. 다양한 job interval 표현이 가능함을 알 수 있다. 관련하여 “2MON”은 두 번째

월요일을 의미한다. 즉, “-“표현은 끝에서 몇 번째를 지시하는 표현임을 알아두자.

CF. 이런 interval들의 표현을 검증할 필요가 있다면 아래의 예처럼 확인할 수 있다.

다음의 예는 향후 1년간의 job schedule을 표현해 준다.

SCOTT> set serveroutput on

SCOTT> declare

2 ltd_start TIMESTAMP;

3 ltd_next TIMESTAMP;

4 ltd_return TIMESTAMP;

5 begin

6 ltd_start := trunc(SYSTIMESTAMP);

7 ltd_return := ltd_start;

8 for cnt in 1..12 loop

9 dbms_scheduler.evaluate_calendar_string(

10 'FREQ=MONTHLY; BYDAY=-1SAT; BYHOUR=1',

11 ltd_start, ltd_return, ltd_next);

12 dbms_output.put_line('Next date: '||

13 to_char(ltd_next,'YYYYMMDD HH24:MI:SS'));

14 ltd_return := ltd_next;

15 end loop;

16 end;

17 /

Next date: 20050827 01:00:00

Next date: 20050924 01:00:00

Next date: 20051029 01:00:00

Next date: 20051126 01:00:00

Next date: 20051231 01:00:00

Next date: 20060128 01:00:00

Next date: 20060225 01:00:00

Next date: 20060325 01:00:00

Next date: 20060429 01:00:00

Next date: 20060527 01:00:00

Next date: 20060624 01:00:00

[email protected] 105

Page 106: O10g app support_11

http://www.ggola.com 장 경 상

Next date: 20060729 01:00:00

PL/SQL procedure successfully completed.

이제 이미 만들어진 program과 schedule을 이용하여 scheduling job을 생성해 보자.

SCOTT> exec dbms_scheduler.create_job(-

> job_name => 'gather_full_stat_month_scott',-

> program_name => 'SCOTT_FULL',-

> schedule_name => 'SCD_EVERY_MONTH',-

> enabled => true);

PL/SQL procedure successfully completed.

이제 매달 마지막 토요일 새벽 1시에 scott의 통계치 gathering이 수행될 것이다.

4.7.6.Advanced Components

Job에 대한 management는 “SYS” schema에 속하기 때문에 작업을 하기 위해선

“management scheduler” privilege가 필요하다. 이 권한을 가진 계정이 어떤

advanced components작업을 할 수 있는지 확인해 보자.

4.7.6.1. Job Class

앞서 설명한 데로 resource 정책을 수립한 후 job의 속성을 정의하는 job들의 group인

job class를 만들어 보자. 사실 job을 생성할 때 job class를 정의하지 않으면 이는

“DEFAULT_JOB_CLASS”에 자동으로 속하게 되며 이 job class는 system의 default

resource consumer group인 “DEFAULT_CONSUMER_GROUP”을 사용한다.

SYSTEM> grant manage scheduler to scott;

Grant succeeded.

SYSTEM> conn scott/tiger

Connected.

SCOTT> exec dbms_resource_manager.create_pending_area;

PL/SQL procedure successfully completed.

SCOTT> exec dbms_resource_manager.create_plan('BATCHJOB', 'BATCH BETWEEN

[email protected] 106

Page 107: O10g app support_11

http://www.ggola.com 장 경 상

24 TO 3');

PL/SQL procedure successfully completed.

SCOTT> exec dbms_resource_manager.create_consumer_group('BATCH', 'NOT

OLTP');

PL/SQL procedure successfully completed.

SCOTT> exec dbms_resource_manager.create_plan_directive(-

> 'BATCHJOB', 'BATCH', 'RuleForBatch', cpu_p1 => 50, parallel_degree_limit_p1 =>

10);

PL/SQL procedure successfully completed.

SCOTT> exec dbms_resource_manager.create_plan_directive(-

> 'BATCHJOB', 'OTHER_GROUPS', 'OtherUserForBatch', cpu_p1 => 30,

parallel_degree_limit_p1 => 0);

PL/SQL procedure successfully completed.

SCOTT> exec dbms_resource_manager.validate_pending_area;

PL/SQL procedure successfully completed.

SCOTT> exec dbms_resource_manager.submit_pending_area;

PL/SQL procedure successfully completed.

SCOTT> select plan, group_or_subplan from dba_rsrc_plan_directives

2 where plan = 'BATCHJOB';

PLAN GROUP_OR_SUBPLAN

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

BATCHJOB OTHER_GROUPS

[email protected] 107

Page 108: O10g app support_11

http://www.ggola.com 장 경 상

BATCHJOB BATCH

SCOTT> exec dbms_scheduler.create_job_class(-

> job_class_name => 'LARGE_JOB',-

> logging_level => DBMS_SCHEDULER.LOGGING_FULL,-

> log_history => 60,-

> resource_consumer_group => 'BATCH',-

> comments => 'LARGE JOB POLICY');

PL/SQL procedure successfully completed.

BATCHJOB plan을 만들어 batch resource consumer group을 생성하였다. 그리고

이 resource consumer group “BATCH”를 job class 생성에서 명시하여 할당하는

과정을 진행하였다. 마지막 job class 생성시 지정한 log_history는 log를 기록한

view “DBA_SCHEDULER_JOB_LOG”에 보관되는 시간을(단위:day) 의미하며

지정하지 않으면 default는 “30”일 이다. 또한 logging level은 log의 수준을

의미하는데 현재는 “FULL”로서 모든 활동을 기록하도록 설정하였다.

CF. 그 밖의 log의 수준을 의미하는 방법은 logging을 하지 않는

“DBMS_SCHEDULER.LOGGING_OFF”와 job의 실행과 관련한 모든 log를 기록하는

“DBMS_SCHEDULER.LOGGING_RUNS”이 있다.

CF. consumer group을 지정하지 않으면 역시 default resource consumer group

을 사용하며 나중에 “set_attribute” procedure를 통해 이를 다시 지정할 수 있다.

CF. parameter service를 지정하지 않으면 cluster로 묶여 있는 database중 아무

곳이나 한 곳에서 job이 수행된다. 따라서 RAC환경에서 이를 잘 활용하면

performance 측면에서 세밀한 scheduling이 가능할 것이다.

CF. job log는 기본적으로 매일 30일 이상이 지난 data를 삭제하도록 되어 있는데 이를

manually 삭제하기 위해 procedure “dbms_scheduler.purge_log”를 call할 수

있다. 사용할 수 있는 parameter는 3가지로 “보관 날짜”, “log type (job_log,

window_log, job_and_window_log) “, “job (class) name(job1, job2)”을 사용할

수 있는데 예를 들어, 10일 이상이 지난 job “jobx”와 job class “jobc_admin”의 job

log를 한번에 삭제하길 원한다면 다음과 같이 할 수 있다. (이 사항은 window log에도

[email protected] 108

Page 109: O10g app support_11

http://www.ggola.com 장 경 상

동일하게 적용된다)

SQL> exec dbms_scheduler.purge_log(10, ‘job_log’, ‘jobx’, ‘jobc_admin’);

4.7.6.2. Window

다음은 resource plan의 선택과 설정을 지원하는 window에 대해 알아보자. 예를 들어

scheduling job이 실행되는 특정 시간대에 resource의 효율적 관리를 위해 사용되는

plan을 선택할 수 있다면 전체적인 system 성능향상에 도움을 줄 수 있을 것이다.

이럴 때 사용하는 것이 바로 window이며 이들 window들을 묶어 하나의 이름으로

만들어 사용하는 것이 window group이다.

Window를 만들기 위한 procedure의 parameter를 확인해 보자. 기존에 만들어진

schedule을 이용하는 방법과 직접 입력하는 방법이 있다.

SQL> dbms_scheduler.create_window (

window_name in varchar2, resource_plan in varchar2, schedule_name in

varchar2,

duration in interval day to second, window_priority in varchar2 default

'LOW',

comments in varchar2 default null);

SQL> dbms_scheduler.create_window (

window_name in varchar2, resource_plan in varchar2,

start_date in timestamp with time zone default null, repeat_interval in

varchar2,

end_date in timestamp with time zone default null,

duration in interval day to second, window_priority in varchar2 default

'LOW',

comments in varchar2 default null);

1. window_name : 만들고자 하는 window의 이름

2. resource_plan : start_date에 활성화 되는 plan의 이름

3. schedule_name : 이미 만들어 놓은 schedule의 이름

4. start_date : 지정한 plan이 활성화되는 시점(time zone을 가진 timestamp)

5. repeat_interval : 만들어지는 window가 반복되는 주기

6. duration : 활성화된 window가 open되어 있는 시간 (일, 시, 분, 초 단위의

interval)

7. end_date : window가 close되는 즉, 더 이상 open되지 않는 시점(time zone을

가진 timestamp)

[email protected] 109

Page 110: O10g app support_11

http://www.ggola.com 장 경 상

8. window_priority : window는 한번에 하나만 유효함으로 두 window가 동시에

open될 대 priority가 낮은 window는 open이 되지 않도록 한다. 지정이 가능한 값은

‘HIGH’, ‘LOW’ 두 가지이다. (default는 LOW다)

9. comments : window 설명

위 parameter에서 보듯 duration의 datatype이 interval day to second라는 점을

잘 생각해야 한다. 그리고 이번에는 start, end date를 sysdate로부터 계산하는 것이

아니라 원문에 충실하기 위해 timestamp with time zone으로 표현할 것이다. 먼저

이들 data type의 형태를 검증해 보자.

CF. Oracle9i에서 소개된 timestamp with time zone과 interval datatype에 대한

기억을 되살려 아래 예를 이해해 보자.

SCOTT> alter session set nls_timestamp_tz_format = 'YYYY-MM-DD HH.MI.SSXFF AM

TZR';

Session altered.

SCOTT> select timestamp '2005-09-01 22:00:00 +9:00' from dual;

TIMESTAMP'2005-09-0122:00:00

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

2005-09-01 10.00.00.000000000 PM +09:00

SCOTT> select interval '0 06:00:00' day to second from dual;

INTERVAL'006:00:00'DAYTOSECON

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

+00 06:00:00.000000

한국은 GMT보다 9시간 빠름으로 이를 이용하여 window creation에 사용할 시간과

duration을 검증해 보았다. 이제 앞서 만들었던 plan “BATCHJOB”을 이용하여

window를 생성해 보자.

SCOTT> exec dbms_scheduler.create_window(window_name =>

'PLAN_2005_LARGE',-

> resource_plan => 'BATCHJOB',-

> start_date => '2005-09-01 10:00:00 PM +9:00',-

[email protected] 110

Page 111: O10g app support_11

http://www.ggola.com 장 경 상

> repeat_interval => 'FREQ=DAILY; BYHOUR=23',-

> duration => '0 06:00:00',-

> end_date => '2005-12-31 04:00:00 AM +9:00',-

> comments => 'Active Plan for 2005 22H');

PL/SQL procedure successfully completed.

이 window는 한국시각 2005년 9월 1일 오후 10시에 open이 되어 “BATCHJOB”

plan이 적용된다. 또한 이 window는 매일 저녁 11시에 반복적으로 open이 되며 6

시간 동안 지속된다. 그리고 2005년 12월 31일 오전 4시를 기준으로 disable되어 더

이상 open되지 않는다.

CF. window들을 모아 group으로 만드는 window group은

dbms_scheduler.create_window_group(group_name, window_list,

comments)로 만들 수 있으며 추후에

dbms_scheduler.add_window_group_member(group_name, window_list)

를 통해 추가할 수도 있다. 따라서 job schedule로 window group을 할당하게 되면

해당 group내의 window들이 함께(combined) 적용된다. 즉, 할당된 window group

에 평일과 주말을 대표하는 두 개의 window를 가지고 있다면 이 들이 combined되어

적용된다는 뜻이다.

4.7.7.Scheduler Management

4.7.7.1. Disable & Enable Components

이미 만들어진 components를(program, job, window, window group)

dbms_scheduler의 procedure를 이용하여 활성화 또는 비 활성화 시킬 수 있다.

SQL> exec dbms_scheduler.enable(‘name’);

SQL> exec dbms_scheduler.disable(‘name’);

CF. disable의 경우 두 번째 parameter로 force option을 사용할 수 있는데 이는

disable시 dependencies를 무시할 것인가를 결정한다. (default false)

CF. component objects 여러 개를 한번에 지정하고 싶을 때에는 “,”로 분리하여 list

를 적으면 된다. < EX. dbms_scheduler.enable(‘name1’, ‘name2’, ‘name3’) >

4.7.7.2. Running Job

기본적은 job은 scheduling을 위해 설정을 하지만 경우에 따라 scheduling과

[email protected] 111

Page 112: O10g app support_11

http://www.ggola.com 장 경 상

상관없이 직접 실행을 할 필요도 있고 필요하다면 manually stop 또는 drop을 할

필요도 있다.

Run Job :

SQL> exec dbms_scheduler.run_job(‘job_name’, use_current_session);

이 procedure는 job을 manually 수행하는 것으로 use_current_session을 true로

할 경우엔 현재 session이 job이 직접 수행되고 false이면 job coordinator와 slave에

의해 regular job처럼 수행된다. (default는 true로 현재 session에서 직접 수행한다)

Stop Job :

SQL> exec dbms_scheduler.stop_job(‘job_name’, force);

사용자가 직접 job을 중단시키는 것으로 force를 true로 하면 현재 수행중인 job이

있어도 이를 kill하고 stop을 진행한다. (force : default false)

Drop Job

SQL> exec dbms_scheduler.drop_job(‘job_name’, force);

사용자가 직접 job을 drop하고자 할 때 사용하며 force를 true로 하면 현재 수행중인

job이 있어도 이를 stop하고 drop을 진행한다. (force : default false)

CF. drop하고자 하는 job의 이름을 지정할 때 “,”로 구분된 list로 여러 개의 jobs을

지정할 수 있다.

4.7.7.3. Program & Schedule

Drop Program :

기 생성된 program component를 drop하고자 할 때는 다음과 같이 하면 된다.

SQL> exec dbms_scheduler.drop_program(‘pgm’, force);

만일 force의 값을 true로 설정하면 program을 drop하기 전에 지정된 program을

reference하는 모든 job을 disable된다. 하지만 false로 설정하게 되면 어떤 job

에서도 지정된 program을 reference하고 있지 않아야만 drop이 성공할 수 있다.

(force : default false)

Drop Schedule :

기 생성된 schedule component를 drop하고자 할 때는 다음과 같이 하면 된다.

SQL> exec dbms_scheduler.drop_schedule(‘scd’, force);

[email protected] 112

Page 113: O10g app support_11

http://www.ggola.com 장 경 상

만일 force의 값을 true로 설정하게 되면 schedule이 drop되기 전에 이 schedule을

reference하는 모든 jobs과 windows는 disable된다. 따라서 false로 지정이 되고 이

schedule을 reference하는 job 또는 window가 존재하면 drop은 실패한다. (force :

default false)

CF. program이나 schedule 모두 “,”로 구분된 list로 여러 개의 objects를 지정할 수

있다.

CF. program이나 schedule을 drop하는데 있어서 지정하는 object가 자신의 소유가

아니라면 해당 object에 대한 alter privilege 또는 create any job privilege가

있어야만 drop을 할 수 있다.

4.7.7.4. Window

Job이 수행될 때 plan을 제어하는 window는 한 시점에 오직 하나의 window만 open

이 될 수 있고 지정된 시간에 자동으로 open된다. 그러나 사용자가 원한다면 직접

window를 open할 수 있고 마찬가지로 close 또는 drop도 할 수 있다.

Open Window :

SQL> exec dbms_scheduler.open_window(‘window’, duration, force);

위 procedure는 이미 open된 window가 있다면 이를 close하고 지정된 window를

open하게 되는데 만일 이미 open된 window와 동일한 window를 또 open하는

경우에는 force option이 false이면 error가 return된다. (force : default false)

CF. duration이 지정되지 않으면 해당 window가 가지고 있는 regular duration이

적용된다. 따라서 현재 open된 window를 또 다시 open하고 duration을 지정한다면

해당 duration후 window가 close되겠지만 duration을 지정하지 않았다면 지정된

window가 가지고 있는 duration만큼 다시 window의 open시간이 확장된다.

Close Window :

SQL> exec dbms_scheduler.close_window(‘window’);

말 그대로 지정된 window를 close하며 별다른 option도 없다. 그러나 close window

는 나름대로의 속성을 가지고 있다.

1. window를 close해도 현재 running job은 멈추지 않는다.

2. 위 1에서 해당 job의 stop_on_window_close 속성이 true로 설정이 되어 있었다면

해당 job은 stop을 진행한다.

[email protected] 113

Page 114: O10g app support_11

http://www.ggola.com 장 경 상

3. 위 2에도 불구하고 해당 job이 window group을 가지고 있어서 지정된 window가

close되고 동 group내의 다른 window가 active되면 이 job은 멈추지 않는다.

Drop Window :

SQL> exec dbms_scheduler.drop_window(‘window’, force);

단순하게 window를 drop하는 procedure이지만 역시 몇 가지 속성을 갖는다.

1. force가 false이면 해당 window는 open되어 있지 않거나 어떤 job에 의해서도

reference되지 않아야 한다. (force : default false)

2. force가 true이면 window는 drop되고 관련 jobs은 disable된다.

3. 위 2에 해당하나 해당 jobs이 drop되는 window가 속한 동일 window group을

가지고 있다면 그 jobs은 disable되지 않는다.

4. window를 close하는 시점에 해당 window를 사용하여 수행중인 job은 계속

수행이 되고 작업이 끝나고 disable된다. 단, 이 job의 stop_on_window_close

속성이 ㅅtrue이면 해당 job은 중단된다.

5. 지정된 window를 포함하는 window group내의 member도 자동으로 삭제된다.

CF. drop하고자 하는 window의 이름을 지정할 때 “,”로 구분된 list로 여러 개의

window를 지정할 수 있다.

CF. window를 open, close, disable, drop하는 것은 manage scheduler privilege

가 필요하다.

4.7.7.5. Control Component Attributes

현재 설정이 완료된 job 또는 기존에 만들어진 scheduler components를 바꾸고 싶을

때 이를 매번 다시 만들 수는 없다. 따라서 각 components의 조절이 필요할 때

dbms_scheduler.set_attribute procedure를 이용하면 원하는 작업이 가능하다.

SQL> exec dbms_scheduler.set_attribute(‘name’, ‘attribute’, value);

첫 번째 parameter name은 object의 이름을 의미하며 attribute은 지정된 object의

속성을 value는 변경할 값을 의미한다. 따라서 object와 attribute에 따라 value는

다양한 datatype을 가질 수 있다. 다음은 변경이 가능한 object와 그 속성들이다.

Components Attributes

Job logging_level, restartable,

max_failures, max_runs, job_weight,

instance_stickiness,

[email protected] 114

표 4-6

Scheduler

Object와 속성

Page 115: O10g app support_11

http://www.ggola.com 장 경 상

stop_on_window_close, job_priority,

schedule_limit, program_name,

job_action, job_type,

number_of_arguments,

schedule_name, repeat_interval,

start_date, end_date, job_class,

comments, auto_drop

Programprogram_action, program_type,

number_of_arguments, comments

Schedulerepeat_interval, comments,

end_date, start_date

Job Classresource_consumer_group, service,

logging_level, log_history, comments

Window

resource_plan, window_priority,

duration, schedule_name,

repeat_interval, start_date,

end_date, comments

Window Group comments

위와 반대로 설정된 속성을 제거할 때에는 다음과 같은 procedure를 사용한다.

SQL> exec dbms_scheduler.set_attribute_null(‘name’, ‘attribute’);

CF. 위 두 procedure를 window, window group 또는 job class에 적용할 때에는

object의 owner이거나 해당 object에 대한 alter privilege 혹은 create any job

privilege가 있어야 한다. 물론, manage scheduler privilege가 있으면 상관없다.

CF. 위에서 설정하는 값이나 기존에 설정된 값을 보고 싶다면

dbms_scheduler.get_attribute(‘name’, ‘attribute’, value)을 call하여 사용할

있다. 여기서 value는 out variable이다.

위 속성을 변경하는 procedure외에 한번에 모든 scheduler components를 바꿀 수

있는 global level의 procedure가 있다. 이 procedure에서 설정한 값은 즉시 영향을

미치지만 그 결과값은 즉시가 아닐 수도 있다. 다음과 같이 사용하며 총 3개의 속성을

지시할 수 있다.

SQL> exec dbms_scheduler.set_scheduler_attribute(‘attribute’, ‘value’);

[email protected] 115

Page 116: O10g app support_11

http://www.ggola.com 장 경 상

현재 사용 가능한 attributes는 다음과 같다.

1. default_timezone : repeating job의 경우 repeat_interval을 갖게 될 것이다.

그러나 만일 start_date가 지정되지 않았다면 이 time zone을 기준으로 계산이 된다.

2. max_job_slave_processes : maximum slave processes의 수를 지정한다. 1

부터 999까지 지정이 가능하며 scheduler가 자동으로 결정한 slave의 수가 이 값보다

많더라도 이 값이 우선하여 적용된다. 즉, 여기서 20이라고 정하면 scheduler가

결정하여 25개가 필요하다 하더라도 slave의 수는 20개를 넘을 수 없다.

3. log_history : scheduler가 logging하는 양을 조절한다. (보관하는 log의 날짜를

지정하며 역시 default는 30이다)

CF. 설정된 값을 확인하기 위해서

dbms_scheduler.get_scheduler_attribute(‘attribute’, value)를 call할 수 있다.

여기서 value는 out variable이다.

4.7.7.6. Priority

Job Priority :

Job이 수행될 때 여러 개의 job이 동시에 경합을 벌일 수 있다. 이 경우 어떤 job이

우선순위를 갖는가에 따라 높은 순위의 job을 먼저 수행시킬 수 있다.

예를 들어 job class “JOB_ADMIN”에 속한 job “job_a”, “job_b”가 동시에

수행된다면 각 job의 priority를 기준으로 우선순위가 결정된다. 일반적으로 각 job의

priority는 default 3을 갖게 되고 1부터 5까지 5단계로 설정할 수 있다.

그러나 job class간의 경합은 job class가 가진 resource plan에 따라 처리가 되기

때문에 동시에 발생하는 job class간의 경합은 job priority로 결정되지 않는다. 따라서

job class “A”와 “B”에 각각 속한 job “job_a1”, “job_b1”이 각각 priority “1”, “5”

로 “job_a1”이 높다 하더라도 “job_a1”이 “job_b1”보다 항상 우선한다는 보장은 없다.

Job priority의 조정은 다음과 같이 하면 된다.

SQL> exec dbms_scheduler.set_attribute(‘job_a1’, ‘job_priority’, 2);

Window Priority :

앞서 dbms_scheduler.create_window procedure에서 잠깐 살펴 보았듯 window

priority는 “HIGH”와 “LOW”의 두 가지 값만을 갖는다. 물론, 한 시점에 open될 수

있는 window도 역시 하나 밖에 유효하지 않다. 따라서 설정된 window의 활성화

시간에 따라 window가 겹치는 시간(overlap)은 얼마든지 존재할 수 있으며 이 때에 이

window priority가 판단의 기준이 된다.

1. window가 overlap될 때 동일한 priority값을 가지고 있으며 open되어 있는

[email protected] 116

Page 117: O10g app support_11

http://www.ggola.com 장 경 상

window가 여전히 active하다.

2. 위 1에서 high priority가 overlap되면 low window는 close되고 high window가

open된다.

3. window가 끝나는 시점에 여러 개의 window가 설정이 되어 있다면 남아있는

시간이 가장 긴 window가 open된다.

4. drop되는 window는 자동으로 close된다.

4.7.7.7. Dictionary View

지금 소개한 scheduler는 oracle10g의 순수한 new feature이기 때문에 관련된 view

들도 많이 나타나고 있다.

New Data Dictionary Description

[DBA|ALL|USER]_SCHEDULER_JOBS Scheduler jobs 정보

[DBA|ALL|USER]_SCHEDULER_JOB_ARGS Scheduler jobs의 argument 정보

[DBA|ALL|USER]_SCHEDULER_RUNNING_JOBS Running scheduler jobs 정보

[DBA|ALL|USER]_SCHEDULER_JOB_LOG Scheduler jobs log 정보

[DBA|ALL|USER]_SCHEDULER_JOB_RUN_DETAILS Scheduler jobs detail log 정보

[DBA|ALL|USER]_SCHEDULER_PROGRAMS Scheduler programs 정보

[DBA|ALL|USER]_SCHEDULER_PROGRAM_ARGS Scheduler programs의 argument 정보

[DBA|ALL|USER]_SCHEDULER_SCHEDULES Scheduler schedules 정보

[DBA|ALL]_SCHEDULER_JOB_CLASSES Scheduler job classes 정보

[DBA|ALL]_SCHEDULER_WINDOWS Scheduler windows 정보

[DBA|ALL]_SCHEDULER_WINDOW_DETAILS Scheduler windows detail log 정보

[DBA|ALL]_SCHEDULER_WINDOW_LOG Scheduler windows log 정보

[DBA|ALL]_SCHEDULER_WINDOW_GROUPS Scheduler window groups 정보

[DBA|ALL]_SCHEDULER_WINGROUP_MEMBERS Scheduler window group members 정보

CF. 위 view를 살펴보면 job class및 window object는 sys schema 소유이기 때문에

“user_”로 시작하는 view가 없다는 것을 인식하자.

4.7.8.Using em

위에서 설명한 schedule 작업은 em에서도 가능하다. 사실 job을 만드는 과정에

parameters가 많기 때문에 em이 더 편할 수 있다. 다음 화면에서 그 내용들을 확인할

수 있다.

CF. 만일 여러분의 database에서 아직 em database control을 start하지 않았다면

chapter 7의 “em Start”부분을 먼저 확인하여 em을 start한 후 다음의 내용을

[email protected] 117

표 4-7

Views of Schedul

er

Page 118: O10g app support_11

http://www.ggola.com 장 경 상

확인하기 바란다.

1. 먼저 “em 관리”를 선택한 화면이다.

2. 우측 중앙에서 “작업”을 선택해 보자.

앞서 생성한 job이 보이고 있다. 우측의 각 버튼을 통해 편집, 보기, 삭제, 수행 등

다양한 작업이 가능하다는 것을 알 수 있다. 물론, 우측 상단의 “생성”버튼을 통해 새로

만들 수도 있다.

3. 위에서 “생성” 버튼을 click하면 다음과 같은 화면이 나타난다.

[email protected] 118

그림 4-2

em의 관리tab 화면

그림 4-3

em의 job(작업) 화면

Page 119: O10g app support_11

http://www.ggola.com 장 경 상

이렇게 바로 생성할 수도 있지만 “SQL 표시”를 눌러서 그 script를 확인할 수도 있다.

다음은 SQL로 표시한 것이다.

앞서 수행했던 package를 그대로 사용이 가능하도록 보여주고 있다. 매우 편리한

[email protected] 119

그림 4-4

em의 job(작업) 생성화면

그림 4-5

em의 job(작업) 생성 script 화면

Page 120: O10g app support_11

http://www.ggola.com 장 경 상

기능이 아닐 수 없다.

[email protected] 120

Page 121: O10g app support_11

http://www.ggola.com 장 경 상

OCP point

==============================================

=================

1. reuse가 가능한 scheduler object 2가지

2. calendaring expression에 대한 완전한 이해

3. 지난 job log를 삭제하는 procedure dbms_scheduler.purge_log의 사용법

4. job component의 속성을 변경하는 procedure의 이름 두 가지

참조

==============================================

=================

job : o9i 576p

resource, consumer group, plan : o8i 105p, o9i 112p

[email protected] 121

Page 122: O10g app support_11

http://www.ggola.com 장 경 상

4.8. Special Sort

4.8.1.Case, Accent를 무시하는 Sort

4.8.1.1. 개요Oracle10g는 case와 accent를 무시하는 sort를 제공한다. 달리 표현하면 대, 소문자

구분을 하지 않도록 하는 기능과 동일한 data에 accent만 다른 경우를 무시할 수 있는

기능을 제공한다는 것이다. 이 기능을 알기 위해서는 관련 parameter를 알아야 한다.

4.8.1.2. Parameters

“nls_sort“ parameter가 있다. 이는 어떤 sort를 할 것인가를 설정한다. 이 값은

“v$nls_valid_values”의 parameter 값이 “sort”인 항목의 “value”를 사용하는데

case-insensitive를 지정하기 위해서는 “sortvalue_CI”를 accent-insensitive를

지정하기 위해서는 “sortvalue_AI”를 설정하여 linguistic sort의 case와 accent를

조절하게 된다. 따라서 다른 언어 data를 가진 table의 data를 handling하는데

유용하게 사용될 수 있다. 몇 가지만 추출해 보면 다음과 같다.

SYS> select value from v$nls_valid_values

2 where parameter = 'SORT' and rownum < 10;

VALUE

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

BINARY

WEST_EUROPEAN

XWEST_EUROPEAN

GERMAN

XGERMAN

DANISH

XDANISH

SPANISH

XSPANISH

9 rows selected.

또한 where절이나 PL/SQL block에서 값을 비교하기 위해서는 또 다른 parameter

nls_comp를 설정할 필요가 있는데 이 부분은 아래의 example을 통해서 확인하도록

한다.

[email protected] 122

Page 123: O10g app support_11

http://www.ggola.com 장 경 상

4.8.2.Example

4.8.2.1. NLS_SORT

다음은 특정한 언어를 사용하지는 않았지만 일반적인 sort를 위와 같은 경우라고

가정하고 대, 소문자를 무시하는 한 예이다.

SCOTT> create table x_sort (x varchar2(1));

Table created.

SCOTT> insert into x_sort values ('a');

1 row created.

SCOTT> insert into x_sort values ('B');

1 row created.

SCOTT> insert into x_sort values ('A');

1 row created.

SCOTT> insert into x_sort values ('b');

1 row created.

SCOTT> commit;

Commit complete.

현재 상태에서 sort를 진행하면 다음과 같다.

SCOTT> select x from x_sort order by x;

X

--

A

B

[email protected] 123

Page 124: O10g app support_11

http://www.ggola.com 장 경 상

a

b

대, 소문자를 무시하도록 nls_sort의 값을 변경한 후 해보자.

SCOTT> alter session set nls_sort=binary_ci;

Session altered.

SCOTT> select x from x_sort order by x;

X

--

a

A

B

b

분명히 값의 변화가 나타난다. 여기서 data가 accent를 표현하고 있는 특정한 나라

예를 들어 “GERMAN”이고 이를 accent를 무시한 sort(비교 등)를 하고자 했다면

다음과 같이 했을 것이다.

SQL> alter session set nls_sort=german_ai;

4.8.2.2. NLS_COMP

위에서 예시한 내용을 order by가 아닌 비교의 “값”으로 사용하려면 어떻게 해야 할까.

다시 말하면 앞에서 설정한 방식은 order by에서는 사용할 수 있어도 비교의 값으로는

그냥 사용할 수가 없다.

위에서 설정한 nls_sort를 기준으로 where절이나 PL/SQL block에서 값을 비교하기

위해서는 또 다른 parameter nls_comp의 값을 “ansi”로 설정하여야 한다. 최초

nls_comp는 binary이기 때문에 nls_sort의 값을 반영하기 위해서는 이 값이 ansi로

바뀌어야 한다. 앞서의 nls_sort 환경을 그대로 유지하여 다음의 예를 통해 확인해 보자.

SCOTT> select x from x_sort where x = 'a';

X

--

a

[email protected] 124

Page 125: O10g app support_11

http://www.ggola.com 장 경 상

SCOTT> alter session set nls_comp=ansi;

Session altered.

SCOTT> select x from x_sort where x = 'a';

X

--

a

A

CF. 이들 parameter에 설정된 값은 SQL문의 여러 부분들을 지원하는데 “where,

order by, start with, having, in/not in, between, case-when”등이 그것이다.

4.8.2.3. NLSSORT

위 과정을 session level에서 조정하지 않고 다음과 같이 새롭게 확장된 nlssort

function을 사용해도 된다. 다음은 새로 connection을 생성하여 nls 환경을 초기화 한

후 진행한 것이다.

SCOTT> conn scott/tiger

Connected.

SCOTT> select x from x_sort order by x;

X

--

A

B

a

b

SCOTT> select x from x_sort where x = 'a';

X

--

a

[email protected] 125

Page 126: O10g app support_11

http://www.ggola.com 장 경 상

SCOTT> select x from x_sort order by nlssort(x, 'nls_sort=binary_ci');

X

--

a

A

B

B

SCOTT> select x from x_sort where nlssort(x, 'nls_sort=binary_ci')

2 = nlssort('a', 'nls_sort=binary_ci');

X

--

a

A

[email protected] 126