Page 1
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
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
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
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
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
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
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
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
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
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
http://www.ggola.com 장 경 상
보여주고 있다.
[email protected] 11
Page 12
http://www.ggola.com 장 경 상
참조
==============================================
=================
internal connection : o9i 37p
[email protected] 12
Page 13
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
http://www.ggola.com 장 경 상
OCP point
==============================================
=================
1. merge문의 on절과 matched(update), unmatched(insert)문장 사용법 이해
2. q operator 사용법
참조
==============================================
=================
merge : o9i 367p
[email protected] 31
Page 32
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
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
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
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
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
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
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
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
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
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
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
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
http://www.ggola.com 장 경 상
참조
==============================================
=================
fact : o8i 27p
dimension : o8i 33p
[email protected] 44
Page 45
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
http://www.ggola.com 장 경 상
참조
==============================================
=================
workspace : o9i 408p
olap : o8i 36p
[email protected] 62
Page 63
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
http://www.ggola.com 장 경 상
확인하기 바란다.
1. 먼저 “em 관리”를 선택한 화면이다.
2. 우측 중앙에서 “작업”을 선택해 보자.
앞서 생성한 job이 보이고 있다. 우측의 각 버튼을 통해 편집, 보기, 삭제, 수행 등
다양한 작업이 가능하다는 것을 알 수 있다. 물론, 우측 상단의 “생성”버튼을 통해 새로
만들 수도 있다.
3. 위에서 “생성” 버튼을 click하면 다음과 같은 화면이 나타난다.
[email protected] 118
그림 4-2
em의 관리tab 화면
그림 4-3
em의 job(작업) 화면
Page 119
http://www.ggola.com 장 경 상
이렇게 바로 생성할 수도 있지만 “SQL 표시”를 눌러서 그 script를 확인할 수도 있다.
다음은 SQL로 표시한 것이다.
앞서 수행했던 package를 그대로 사용이 가능하도록 보여주고 있다. 매우 편리한
[email protected] 119
그림 4-4
em의 job(작업) 생성화면
그림 4-5
em의 job(작업) 생성 script 화면
Page 120
http://www.ggola.com 장 경 상
기능이 아닐 수 없다.
[email protected] 120
Page 121
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
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
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
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
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
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