Your SlideShare is downloading. ×
O10g app support_11
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

O10g app support_11

165
views

Published on


0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
165
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
1
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. http://www.ggola.com 장 경 상 4. Application Support..............................................................................................................4 4. SQL*Plus Enhancement.................................................................................................5 5. Internal Connection................................................................................................5 6. 쉬운 prompt의 변경...............................................................................................6 7. File Handling..........................................................................................................9 8. SQL Statement..............................................................................................................12 9. 확장된 Merge Statement......................................................................................12 10. DUAL table의 성능.............................................................................................18 11. Regular Expression.............................................................................................19 12. REGEXP_LIKE (Like 확장)........................................................................19 13. REGEXP_INSTR(Instr 확장)......................................................................21 14. REGEXP_REPLACE(Replace 확장)..........................................................24 15. REGEXP_SUBSTR(Substr 확장)................................................................25 16. 다양한 Format.............................................................................................26 17. q Operator (quotation의 다른 표현).................................................................27 18. Partitioned Outer Join...............................................................................................31 19. Dense Data..........................................................................................................31 20. Outer Join using Partition By............................................................................31 21. Example...............................................................................................................32 22. SQL Model Clause.....................................................................................................44 23. Overview.............................................................................................................44 24. Three Groups for Model....................................................................................44 25. Rules.....................................................................................................................45 26. Other Options.....................................................................................................47 27. Model Example...................................................................................................49 28. Test Data 환경 구성.....................................................................................49 29. OLAP error 발생 시 처리...........................................................................54 30. SQL Model Test...........................................................................................56 31. Materialized View......................................................................................................62 32. 기본 Materialized View 생성............................................................................62 33. Query Rewrite.....................................................................................................65 34. Materialized View Management......................................................................72 35. Check Fast Refreshable MView.................................................................72 36. Tuning Procedure.......................................................................................73 37. Partition Change Tracking (PCT).....................................................................75 38. PCT Enhancement......................................................................................75 JKSPARK@HANAFOS.COM 1
  • 2. http://www.ggola.com 장 경 상 39. PCT Truncate...............................................................................................75 40. PCT Refresh.................................................................................................75 41. Other Operation..................................................................................................76 42. Partition Operation.....................................................................................76 43. Execution Plan.............................................................................................76 44. Trusted Option............................................................................................78 45. Indexes........................................................................................................................80 46. IOT(Index Organized Table) Partition.............................................................80 47. Bitmap Index.......................................................................................................83 48. Update Local Partitioned Index........................................................................84 49. Unusable Index...................................................................................................86 50. Hash Partitioned Global Index.........................................................................87 51. Scheduling..................................................................................................................95 52. Advanced Scheduling........................................................................................95 53. Scheduler Components......................................................................................95 54. Basic Scheduler Component 생성.....................................................................96 55. Calendaring Expressions...................................................................................99 56. Job Using Program & Schedule......................................................................101 57. Advanced Components...................................................................................104 58. Job Class.....................................................................................................104 59. Window......................................................................................................106 60. Scheduler Management...................................................................................109 61. Disable & Enable Components................................................................109 62. Running Job...............................................................................................109 63. Program & Schedule.................................................................................110 64. Window......................................................................................................110 65. Control Component Attributes...............................................................112 66. Priority.......................................................................................................113 67. Dictionary View........................................................................................114 68. Using em............................................................................................................115 69. Special Sort................................................................................................................119 70. Case, Accent를 무시하는 Sort.........................................................................119 71. 개요.............................................................................................................119 72. Parameters.................................................................................................119 73. Example.............................................................................................................120 74. NLS_SORT.................................................................................................120 JKSPARK@HANAFOS.COM 2
  • 3. http://www.ggola.com 장 경 상 75. NLS_COMP...............................................................................................121 76. NLSSORT...................................................................................................122 JKSPARK@HANAFOS.COM 3
  • 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를 몰라서 못쓰면 되겠는가. 알아도 “필요하지 않아서 사용하지 않 는다”면 모르지만. JKSPARK@HANAFOS.COM 4
  • 5. http://www.ggola.com 장 경 상 4. SQL*Plus Enhancement 이번에 oracle10g에서 많은 사용자들에게 익숙한(특히나 DBA라면 더더욱) SQL*Plus의 기능을 확장하여 사용자 편의성을 증대 시켰다. CF. 주의할 점은 oracle10g임으로 oracle10g client의 sqlplus 실행 file을 가지고 이 기능 을 활용해야 한다. Oracle9i의 sqlplus 실행 file을 가지고 oracle10g database에 접속을 하 면 새로운 기능들을 사용할 수 없다는 말이다. 5. 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. JKSPARK@HANAFOS.COM 5
  • 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” 사이에 반드시 공란이 있어야 한다는 것이다. 6. 쉬운 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자까지 가능하다는 것을 알 수 있다. 그래서 날 JKSPARK@HANAFOS.COM 6
  • 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 ------------------------ JKSPARK@HANAFOS.COM 7
  • 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. JKSPARK@HANAFOS.COM 8
  • 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로 표시가 되었다. 7. 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 JKSPARK@HANAFOS.COM 9
  • 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는 항상 최신 정보를 보여주고 있다. JKSPARK@HANAFOS.COM 10
  • 11. http://www.ggola.com 장 경 상 참조 =============================================================== internal connection : o9i 37p JKSPARK@HANAFOS.COM 11
  • 12. http://www.ggola.com 장 경 상 8. SQL Statement 9. 확장된 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. JKSPARK@HANAFOS.COM 12
  • 13. 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 JKSPARK@HANAFOS.COM 13
  • 14. 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. SCOTT> select * from sports_inform; JKSPARK@HANAFOS.COM 14
  • 15. http://www.ggola.com 장 경 상 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'; 1 row updated. SCOTT> merge into sports_inform si JKSPARK@HANAFOS.COM 15
  • 16. http://www.ggola.com 장 경 상 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진행 중에 삭제를 하는 기능이다. 두 번째 시나리오는 다 음과 같다. 첫 번째와 거의 유사하지만 update를 진행하면서 where 조건에 해당하는 data를 삭제하 고 insert를 할 때는 where절에 상관이 없이 unmatched data 전체를 insert하는 것이다. 즉, sports에서 상태가 ‘N’인데 sports_inform에 있는 data는 delete하고 sports_inform에 JKSPARK@HANAFOS.COM 16
  • 17. http://www.ggola.com 장 경 상 없고 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 6 rows selected. SCOTT> select * from sports; SP_ SP_NAME S JKSPARK@HANAFOS.COM 17
  • 18. http://www.ggola.com 장 경 상 ----- --------------------------- - 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을 모두 사용해야 했을 것이다. 10. 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 ------- ---------------------------------------------------------- 0 SELECT STATEMENT GOAL: CHOOSE 1 TABLE ACCESS (FULL) OF 'DUAL' Oracle10g부터는 아래와 같이 변경이 되었다. Rows Execution Plan JKSPARK@HANAFOS.COM 18
  • 19. http://www.ggola.com 장 경 상 ------- -------------------------------------------------------------- 0 SELECT STATEMENT MODE: ALL_ROWS 0 FAST DUAL 11. 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이다. 12. 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 “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 JKSPARK@HANAFOS.COM 19
  • 20. http://www.ggola.com 장 경 상 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 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 JKSPARK@HANAFOS.COM 20
  • 21. http://www.ggola.com 장 경 상 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]'); SP_ ----- OB1 OB2 13. 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) JKSPARK@HANAFOS.COM 21
  • 22. http://www.ggola.com 장 경 상 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을 지정 해야 했다. 그렇지 않으면 ‘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; JKSPARK@HANAFOS.COM 22
  • 23. http://www.ggola.com 장 경 상 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, ----------------------------------------------------- 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]로 하면 동일한 결과를 얻을 수 있 JKSPARK@HANAFOS.COM 23
  • 24. http://www.ggola.com 장 경 상 을 것이다. 14. 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 ------------------------------------------------- (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] JKSPARK@HANAFOS.COM 24
  • 25. http://www.ggola.com 장 경 상 15. 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, --------------------------------------------------------- ,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/ JKSPARK@HANAFOS.COM 25
  • 26. http://www.ggola.com 장 경 상 16. 다양한 Format 지금까지 살펴본 regular expression의 생명은 얼마나 다양한 pattern을 생각해 내고 사 용하느냐에 있다. 다음은 그 다양한 형식을 설명한 내역이다. 이 부분은 한글화로 할 경우 너무 오해의 소지가 많기 때문에 원래 설명 그대로를 싣는다. Operation 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 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 JKSPARK@HANAFOS.COM 26 표 4-1 Regexp Format 표 4-2 Regexp 중괄호 용 법
  • 27. http://www.ggola.com 장 경 상 [: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 17. q Operator (quotation의 다른 표현) 현재 여러분이 사용하고 있는 문자열에 quotation을 사용하는 경우 그것이 문자열이냐 아니면 인용부호이냐를 구분하기 위해 두 개의 quotation을 연달아 사용하여 이를 구분 하곤 했다. 이는 한 column에 여러 개의 quotation을 사용하고 싶은 경우 매우 불편하여 사용상의 어려움이 많았다. 실수할 여지도 많았고. Oracle10g가 제공하는 새로운 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; JKSPARK@HANAFOS.COM 27
  • 28. http://www.ggola.com 장 경 상 '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; '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 * JKSPARK@HANAFOS.COM 28
  • 29. http://www.ggola.com 장 경 상 ERROR at line 1: ORA-00923: FROM keyword not found where expected 위에서 보듯 pair만 맞추어 주면 원하는 결과를 얻을 수 있지만 마지막 SQL문의 error에 서처럼 문자열의 시작 character와 q operator의 문자가 같은 경우는 error가 return되었 다. JKSPARK@HANAFOS.COM 29
  • 30. http://www.ggola.com 장 경 상 OCP point =============================================================== 1. merge문의 on절과 matched(update), unmatched(insert)문장 사용법 이해 2. q operator 사용법 참조 =============================================================== merge : o9i 367p JKSPARK@HANAFOS.COM 30
  • 31. http://www.ggola.com 장 경 상 18. Partitioned Outer Join 19. 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를 넣는다 는 것은 사용자가 매번 작업을 해야 하며 작업 시점에 따라 틀린 결과를 가져올 수도 있는 문제가 있다. 20. 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로 출력할 수 있다. CF. 간단히 정의하면 partitioned outer join은 outer join을 partition by로 지정한 column JKSPARK@HANAFOS.COM 31
  • 32. http://www.ggola.com 장 경 상 별로 각각 수행하는 역할을 해준다고 하겠다. CF. 이 기능은 ANSI, ISO SQL Standard(표준) 이다. 21. 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 / PL/SQL procedure successfully completed. JKSPARK@HANAFOS.COM 32
  • 33. http://www.ggola.com 장 경 상 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 declare JKSPARK@HANAFOS.COM 33
  • 34. http://www.ggola.com 장 경 상 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 insert into sports_sold values JKSPARK@HANAFOS.COM 34
  • 35. http://www.ggola.com 장 경 상 (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 OB1 20050726 282948 990296 JKSPARK@HANAFOS.COM 35
  • 36. http://www.ggola.com 장 경 상 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가 된다. 자 이제 사용자가 time dimension의 모든 날짜를 기준으로 data를 보고자 한다. 그렇다면 JKSPARK@HANAFOS.COM 36
  • 37. http://www.ggola.com 장 경 상 보통은 다음과 같이 작업을 진행할 것이다. 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 XN1 20050723 141519 283035 JKSPARK@HANAFOS.COM 37
  • 38. http://www.ggola.com 장 경 상 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 OB1 20050719 JKSPARK@HANAFOS.COM 38
  • 39. http://www.ggola.com 장 경 상 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 XN1 20050721 47172 47172 JKSPARK@HANAFOS.COM 39
  • 40. http://www.ggola.com 장 경 상 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 8 where sp.sp_code = ss.sp_id 9 order by 1, sp.sp_code; JKSPARK@HANAFOS.COM 40
  • 41. http://www.ggola.com 장 경 상 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 20050723 SQUASH 0 19 20050723 BASEBALL 141468 282955 JKSPARK@HANAFOS.COM 41
  • 42. http://www.ggola.com 장 경 상 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일 의 판매량이 날짜 별 누계와 동일하다는 것을 보여주고 있다. JKSPARK@HANAFOS.COM 42
  • 43. http://www.ggola.com 장 경 상 참조 =============================================================== fact : o8i 27p dimension : o8i 33p JKSPARK@HANAFOS.COM 43
  • 44. http://www.ggola.com 장 경 상 22. SQL Model Clause 23. 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']); 24. Three Groups for Model Model clause은 세 개의 group column으로 다차원 array형식을 만들어 내는데 이는 partition, dimension, measure columns으로 구성되며 rules로 표현되는 형식에 따라 이 JKSPARK@HANAFOS.COM 44
  • 45. 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의 훌륭한 결과값을 받는가 아닌가의 관건이 될 것이 다. 25. 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을 사용하면 참조하는 data(cell)가 있으면 update를 하고 없으면 insert하지 않는다. 그리고 이 option은 global JKSPARK@HANAFOS.COM 45
  • 46. http://www.ggola.com 장 경 상 또는 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가 있다. 표현 식은 둘 다 다음과 같다. PRESENT(NN)V (pplt[‘SEOUL’, 2004], pplt[‘DAEGU’, 2004], 1.1 * pplt[‘SEOUL’, 2003]) PRESENTV : 서울지역 2004년도 자료가 있으면 대구지역 2004년도 인구수를 return하고 없다면 전년도 서울지역 인구수의 1.1배를 return한다. JKSPARK@HANAFOS.COM 46
  • 47. http://www.ggola.com 장 경 상 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 조건이 만족되어 중단될 수도 있 다. CF. 이 기법은 앞서 소개한 “AUTOMATIC ORDER”와 함께 사용할 수 없다. 26. Other Options 1. reference model : SQL Model은 기본적으로 지정해서 사용하는 다차원 model외에 참 조 model을 만들어 다른 차원의 model data를 활용할 수 있다. 이를 reference model이 라 하며 이에 비해 현재까지 설명해온 기본 모델을 main model 이라 부른다. 설명이 더 어렵다. 쉽게 말하면 main model에서 reference model의 값을 참조하여 필요한 data를 JKSPARK@HANAFOS.COM 47
  • 48. http://www.ggola.com 장 경 상 가공할 수 있다는 뜻이다. 아래의 예는 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 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된다. JKSPARK@HANAFOS.COM 48
  • 49. http://www.ggola.com 장 경 상 27. Model Example 이제 앞서 설명한 방식들을 이용하여 몇 가지 실례를 통해 작업을 해보자. 28. 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. 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. JKSPARK@HANAFOS.COM 49
  • 50. http://www.ggola.com 장 경 상 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; commit; end; / SCOTT> @ptj_env3.sql PL/SQL procedure successfully completed. SCOTT> desc compass Name Null? Type ----------------------------------------- -------- ---------------------------- JKSPARK@HANAFOS.COM 50
  • 51. http://www.ggola.com 장 경 상 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”, 방위 표시 “compass”, sports code table “sports”이 준비 되었다. 다음은 앞서 만들어진 ptj1에 현재 sports_sold에 있고 ptj1에 없는 날짜들을(매출 날짜로 사용하기 위해) 생성함과 동시에 날짜에 대한 상태코드를 추가한다. 이 상태 코드는 나중 에 테스트를 위한 것이니 그냥 따라 하기 바란다. 그리고 마지막으로 SQL Model에 사용 할 view를 방위 별, 상품 별, 연도 별, 월 별 판매량과 판매일 수의 합을 갖도록 생성한다. SCOTT> select min(sale_date) from ptj1; MIN(SALE --------------- 20050717 JKSPARK@HANAFOS.COM 51
  • 52. http://www.ggola.com 장 경 상 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 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 JKSPARK@HANAFOS.COM 52
  • 53. http://www.ggola.com 장 경 상 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 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. JKSPARK@HANAFOS.COM 53
  • 54. http://www.ggola.com 장 경 상 안타깝게도 위와 같은 error가 발생하였다. 지금 위와 동일한 error를 본 사람은 아마도 필자와 마찬가지로 oracle9i에서 oracle10g로 upgrade를 진행한 경우일 것이다. 위 error 메시지를 통해 SQL Model clause이 oracle9i에서 소개 되었던 workspace 개념과 oracle olap 기능을 함께 사용하고 있다고 유추할 수 있다. 29. 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 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 JKSPARK@HANAFOS.COM 54
  • 55. http://www.ggola.com 장 경 상 Connected. SYS> @$ORACLE_HOME/olap/admin/olap.sql SYSAUX TEMP; PL/SQL procedure successfully completed. ……………………………. ……………………………. ……………………………. Call completed. Call completed. Call completed. Call completed. 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 JKSPARK@HANAFOS.COM 55
  • 56. http://www.ggola.com 장 경 상 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되었다. 30. 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 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 JKSPARK@HANAFOS.COM 56
  • 57. http://www.ggola.com 장 경 상 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 ----------------- --------------------------------- ------------ 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 JKSPARK@HANAFOS.COM 57
  • 58. http://www.ggola.com 장 경 상 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] 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 JKSPARK@HANAFOS.COM 58
  • 59. http://www.ggola.com 장 경 상 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 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년 이후의 야구, 테니스, 스쿼시종목에 한정하 여 매출액을 계산한다. JKSPARK@HANAFOS.COM 59
  • 60. http://www.ggola.com 장 경 상 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년까지 모든 스포츠의 항목별 매출액 및 매출예상 금액을 연도별로 합 산하여 계산한다. JKSPARK@HANAFOS.COM 60
  • 61. http://www.ggola.com 장 경 상 참조 =============================================================== workspace : o9i 408p olap : o8i 36p JKSPARK@HANAFOS.COM 61
  • 62. http://www.ggola.com 장 경 상 31. Materialized View 벌써 이 Materialized View(이하 MView)features가 소개된 지도 꽤 오래 되었다. 처음 oracle8i에서 이 기법을 사용하면서 정말 유용하다는 생각을 하면서도 대다수의 사람들 이 그냥 무시하고 지나칠 때마다 참 안타까웠다. 그러다 실무에서 이 기능을 직접 구현해 서 담당자에게 소개해 주었을 때 무척 만족스러워했던 모습을 아직 기억하고 있다. 필자 는 거의 이런 이야기를 하지 않지만 실제로 이 기능의 존재를 제대로 인식하지 못해 사용 하지 않는 사람들이 많아서 좀 길고 불필요할 것 같아도 이렇게 시작한다. 필자의 홈페이지에서 배포하는 oracle8i new features 문서를 보면 매우 쉽게 이 MView 를 만들어 테스트를 해볼 수 있다. 먼저 MView하나를 만들어 놓고서 oracle10g에서 소개 되는 내용들을 확인해 보자. 32. 기본 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; JKSPARK@HANAFOS.COM 62
  • 63. 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 이제 위 SQL을 근간으로 하는 MView를 하나 만들어 보자. JKSPARK@HANAFOS.COM 63
  • 64. http://www.ggola.com 장 경 상 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. JKSPARK@HANAFOS.COM 64
  • 65. 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의 효과적인 사용에 있을 수 있다. 33. Query Rewrite 앞서 기본 MView 테스트를 진행하면서 설명했지만 효과적인 MView의 생성은 application의 성능향상을 기대해 볼 수 있다. 즉, 특정한 SQL이 많은 I/O를 수반하면서 여러 application에서 자주 사용되는 경우나 또는 많은 application은 아니지만 일부 application에서 사용되고 있고 해당 application을 MView를 사용하도록 고칠 수 없는 JKSPARK@HANAFOS.COM 65
  • 66. http://www.ggola.com 장 경 상 경우가 있다면 이 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을 사용하면 query rewrite가 안되기 때문에 원래의 SQL을 수행하게 된다는 것이다. 따라서 적어도 application에서 이 hint와 함께 SQL을 사용하려면 query rewrite parameter가 true이어 JKSPARK@HANAFOS.COM 66
  • 67. http://www.ggola.com 장 경 상 야 할 것이다. 그렇지 않다면 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) REWRITTEN_COST NUMBER(38) FLAGS NUMBER(38) JKSPARK@HANAFOS.COM 67
  • 68. http://www.ggola.com 장 경 상 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를 해서 지워주어야 한다. SCOTT> rollback; Rollback complete. JKSPARK@HANAFOS.COM 68
  • 69. http://www.ggola.com 장 경 상 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) ------------ -------------------- ----------------------- A01 NETWORK 1.2805E+10 A01 DATABASE 2886912000 A01 MIDDLEWARE 8279472000 B01 SYSTEM 1.0055E+10 JKSPARK@HANAFOS.COM 69
  • 70. http://www.ggola.com 장 경 상 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; ORIGINAL_COST REWRITTEN_COST ------------------------- -------------------------- 49725 6 JKSPARK@HANAFOS.COM 70
  • 71. http://www.ggola.com 장 경 상 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의 범위가 어느 정도는 확장성이 있도록 구현이 되면 그 MView를 기반으로 하여 다양한 SQL이 rewrite가 가능하다는 것이다. 위의 예는 emp_id별, dept_name별 summary를 가진 MView가 만들어졌지만 이 data는 emp_id별 summary를 만드는데도 아무런 지장이 없기 때문에 SQL문은 틀려도 query rewrite가 가 능하다는 것을 보여준다. CF. 지역, 상품, 년도, 분기, 월별 매출액으로 만들어진 MView가 있다고 가정해 보자. 이 JKSPARK@HANAFOS.COM 71
  • 72. http://www.ggola.com 장 경 상 를 활용하면 얼마나 다양한 query rewrite가 가능하겠는가. 34. Materialized View Management MView를 만들고 이를 잘 운용하려면 base table을 기본으로 하는 SQL의 적절성, refresh 시점의 유효성, query rewrite의 확장성 등 고려할 것이 많아서 MView를 만들다가 오류 가 생기는 경우도 자주 접할 수 있다. Oracle10g에서는 이런 문제들에 도움을 주고자 MView의 tuning과 관련된 procedure를 제공하고 있다. 35. 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) MVNAME VARCHAR2(30) CAPABILITY_NAME VARCHAR2(30) POSSIBLE CHAR(1) RELATED_TEXT VARCHAR2(2000) RELATED_NUM NUMBER MSGNO NUMBER(38) MSGTXT VARCHAR2(2000) JKSPARK@HANAFOS.COM 72
  • 73. http://www.ggola.com 장 경 상 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는 되 어있지 않다는 것을 그리고 fast refresh를 하려면 base table의 materialized view log table이 필요하다는 정보를 제공하고 있다. 36. Tuning Procedure 이제 oracle10g에서 강조하는 MView를 제대로 생성하는데 도움을 주는 procedure를 통 해 그 효율성을 확인해 보자. MView Tuning을 위한 procedure를 수행하기 위해서는 sys 계정으로부터 특별한 권한인 advisor를 받아야 한다. 적절한 권한을 부여한 후 직접 수행 해 보자. SCOTT> conn sys/manager Connected. JKSPARK@HANAFOS.COM 73
  • 74. http://www.ggola.com 장 경 상 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에서 동일한 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의 요소들이 있는지 확인할 수 있다. 이 JKSPARK@HANAFOS.COM 74
  • 75. http://www.ggola.com 장 경 상 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'); 37. Partition Change Tracking (PCT) 38. 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가 가능하게 되었다. 39. PCT Truncate 이전 버전의 oracle은 PCT refresh를 할 때 MView의 rows를 없애기 위해서 delete문을 사용해 왔다. 물론, 이런 방식 보다는 MView에 대한 truncate partition이 훨씬 더 효과적 일 것이다. 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가 수행되는 것을 원 치 않을 때 40. PCT Refresh MView refresh를 manually 하기 위하여 제공되는 dbms_mview.refresh에 PCT refresh 를 위한 option으로 “P”가 추가되었다. 다음과 같은 형식을 사용한다. JKSPARK@HANAFOS.COM 75
  • 76. http://www.ggola.com 장 경 상 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를 만드는 것이 아니라는 것을 인 식하자. 41. Other Operation 42. Partition Operation Oracle10g부터는 partitioned MView를 위한 partition operation이 가능해 졌다. 다음과 같은(사실은 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; 43. Execution Plan Oracle10g에서는 MView를 사용하는 execution plan의 설명이 보다 구체화 되었다. 작업 한 SQL이 rewrite가 되었는가를 구체적으로 표현해 준다. 다음의 두 SQL간의 차이를 보 자. SCOTT> set timing off SCOTT> !more plan.sql set linesize 100 column operation format a50 JKSPARK@HANAFOS.COM 76 표 4-3 Mview Refresh Method
  • 77. http://www.ggola.com 장 경 상 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 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 JKSPARK@HANAFOS.COM 77
  • 78. http://www.ggola.com 장 경 상 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가 수행되었는지를 구체적으로 표현해 주고 있다. 44. Trusted Option MView를 생성할 때 oracle10g부터는 option을 통해 refresh 속성을 지정할 수 있다. 다음 과 같은 형식으로 지정하며 지정하지 않는 경우에는 default로 “ENFORCED”가 적용된 다. create materialized view mv_name ... 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이다. JKSPARK@HANAFOS.COM 78
  • 79. 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 JKSPARK@HANAFOS.COM 79
  • 80. http://www.ggola.com 장 경 상 45. Indexes 46. 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 primary key JKSPARK@HANAFOS.COM 80
  • 81. http://www.ggola.com 장 경 상 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가 발생했지만 primary key를 수정하자 정상적으로 작업이 성공했다. 이제 테스트 진행을 위해 global index를 만들어보자. JKSPARK@HANAFOS.COM 81
  • 82. http://www.ggola.com 장 경 상 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'; INDEX_NAME PARTITION_NAME STATUS JKSPARK@HANAFOS.COM 82
  • 83. http://www.ggola.com 장 경 상 ------------------------ --------------------------- ----------- 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를 관리해준다는 것 을 살펴보았다. 47. Bitmap Index Oracle10g는 DML operation의 발전으로 index관련 성능상의 문제들이 좋아졌다. 그 것 은 bitmap index에도 마찬가지여서 특정 DML로 인해 성능이 떨어지는 bitmap index의 문제들이 다소 해소되었다. 따라서 bitmap index의 성능도 더욱 좋아졌고 fragment도 전 보다 덜하다. 다음은 이런 향상된 features가 제대로 적용할 수 있는 환경이다. 1. database compatible 10.0.0.0 이후에 생성된 bitmap index 2. compatible 10.0.0.0 이전에 만들어진 bitmap index의 경우 compatible을 10.0.0.0 이상 JKSPARK@HANAFOS.COM 83
  • 84. http://www.ggola.com 장 경 상 으로 올린 후 해당 bitmap index 에 첫 번째 DML이 일어날 때 부분적으로 효과가 있다. 3. 어느 경우이든 compatible 10.0.0.0 이전에 만들어진 bitmap index는 compatible 10.0.0.0 이상으로 database가 start된 후 rebuilding을 해주는 것이 완전한 효과를 볼 수 있는 길이다. 48. 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); 1 row created. SCOTT> insert into pt_sales values (40, 'nb', 400, sysdate -4); JKSPARK@HANAFOS.COM 84
  • 85. http://www.ggola.com 장 경 상 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 3 where index_name = 'PT_SALES_LI1'; PARTITION_NAME TABLESPACE_NAME STATUS JKSPARK@HANAFOS.COM 85
  • 86. http://www.ggola.com 장 경 상 ------------------------------ ------------------------------- ----------------- 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로 유지할 수 있도록 하였다. 49. Unusable Index 대용량 database를 위한 partition table의 사용이 필수적이라면 그에 따른 반대급부도 있 기 마련이다. 특정 data를 조절하기 위해 전체 table이 아닌 partition operation을 적절히 사용하는 것 은 매우 좋은 선택이다. 그러나 이것이 성능상의 많은 이점은 줄 수 있으나 그에 따른 관 JKSPARK@HANAFOS.COM 86
  • 87. http://www.ggola.com 장 경 상 련 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를 출력하게 된다. 50. Hash Partitioned Global Index Oracle8i에서 소개된 hash partition은 global index에 적용될 수 없다는 문제가 있었다. 이제 oracle10g는 global index에도 hash partition을 적용할 수 있게 되었다. 따라서 hash partitioned global index에 대한 add 및 coalesce partition command가 지원 됨은 물론이다. 그렇다면 이런 기법들이 실제로 어떤 효과가 있을까? 가장 손쉽게 생각할 수 있는 것이 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의 정도가 심할 수도 있을 것이다. JKSPARK@HANAFOS.COM 87
  • 88. http://www.ggola.com 장 경 상 이런 이유로 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; Grant succeeded. SYSTEM> conn scott/tiger Connected. SCOTT> create table g_tab_hash (id number, gid number) 2 tablespace bigdata; Table created. JKSPARK@HANAFOS.COM 88 그림 4-1 Hash partitione d global index 이 점
  • 89. http://www.ggola.com 장 경 상 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 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) JKSPARK@HANAFOS.COM 89
  • 90. http://www.ggola.com 장 경 상 ------------- -------------- 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 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 JKSPARK@HANAFOS.COM 90
  • 91. http://www.ggola.com 장 경 상 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; 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'); JKSPARK@HANAFOS.COM 91
  • 92. http://www.ggola.com 장 경 상 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만을 지정할 수 있다. 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에서는 그 JKSPARK@HANAFOS.COM 92
  • 93. http://www.ggola.com 장 경 상 이름을 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이 발생할 것이다) JKSPARK@HANAFOS.COM 93
  • 94. 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 JKSPARK@HANAFOS.COM 94
  • 95. http://www.ggola.com 장 경 상 51. Scheduling 52. 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을 잘 사용하지 않고 있 다는 점은 매우 안타까운 일이다. 53. 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을 위한 가장 기초적인 개념이다. 다음은 이런 scheduling에 필요한 또는 연결되는 다른 components에 대한 설명이다. 이 중 resource 와 관련한 것은 oracle8i에서 소개된 개념임으로 여기서는 schedule과 관련하여서만 설명 을 한다. JKSPARK@HANAFOS.COM 95
  • 96. http://www.ggola.com 장 경 상 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 54. Basic Scheduler Component 생성 Job을 만들기 위해서는 앞서 설명한 3가지 기본 components를 지정해야 한다. 그 components는 job을 생성하면서 직접 지정을 하든 아니면 원래 있던 것을 사용하든 상간 은 없다. 따라서 그 개념에 비추어 본다면 job을 만드는 방식은 다음과 같은 것들이 있을 것이다. 1. job을 생성하면서 실제 사용할 program과 schedule을 직접 입력하거나 기존에 저장된 program, schedule을 직접 지정한다. 2. 실제 program을 직접 입력하고 기존의 schedule을 지정한다. 3. schedule은 직접 입력하고 기존에 저장이 되어있던 program을 지정한다. CF. 사실 위 유형은 package와 직접 연관이 있다. 1번의 두 가지 형태와 2, 3번을 합쳐 총 4 JKSPARK@HANAFOS.COM 96
  • 97. http://www.ggola.com 장 경 상 가지 방식을 지원하는 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, - > 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 => JKSPARK@HANAFOS.COM 97 표 4-4 Job Type 과 Job Action
  • 98. http://www.ggola.com 장 경 상 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> 작업이 정상적으로 수행되었다. 위 내용의 의미는 다음과 같다. 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로 등록 하였다. JKSPARK@HANAFOS.COM 98
  • 99. http://www.ggola.com 장 경 상 CF. 사실 위의 간단한 예제는 이전 버전의 dbms_job으로 등록하는 작업과 큰 차이는 없 다. 보다 복잡하고 유연한 scheduling을 구사해야 oracle10g의 강점을 살릴 수 있을 것이 다. CF. 몇 차례의 테스트 결과 job_type을 STORED_PROCEDURE로 하는 경우에 parameter가 있으면 모두 error가 return 되었다. 55. 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 HOURLY BYDAY MINUTELY BYHOUR SECONDLY BYMINUTE BYSECOND 앞서 테스트한 “매일 새벽 1시”를 좀 더 세밀하게 “매일 새벽 1시 30분”으로 하여 calendaring 표현으로 바꾸면 “FREQ=DAILY, BYHOUR=1; BYMINUTE=30” 이렇게 할 수 있다. 또한 BYDAY=2MON 혹은 BYDAY=-1SAT처럼 2번째 월요일 혹은 끝에서 첫 번 째(즉, 마지막) 토요일과 같은 세밀한 표현이 가능하다. JKSPARK@HANAFOS.COM 99 표 4-5 Calendar Expressio n
  • 100. http://www.ggola.com 장 경 상 보다 복잡한 형태를 알아보자. 통계작업을 수행하는데 업무시간과 배치 작업시간, 그리 고 마감시점 등을 계산한 결과 “매달 중순 새벽 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 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)* JKSPARK@HANAFOS.COM 100
  • 101. http://www.ggola.com 장 경 상 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 56. 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. 다음 단계로 프로그램을 만들자. 위 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. JKSPARK@HANAFOS.COM 101
  • 102. http://www.ggola.com 장 경 상 현재 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가 지 정되지 않았기 때문에 만료시점이 없이 계속 유효하다. 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 JKSPARK@HANAFOS.COM 102
  • 103. http://www.ggola.com 장 경 상 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 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. JKSPARK@HANAFOS.COM 103
  • 104. http://www.ggola.com 장 경 상 이제 매달 마지막 토요일 새벽 1시에 scott의 통계치 gathering이 수행될 것이다. 57. Advanced Components Job에 대한 management는 “SYS” schema에 속하기 때문에 작업을 하기 위해선 “management scheduler” privilege가 필요하다. 이 권한을 가진 계정이 어떤 advanced components작업을 할 수 있는지 확인해 보자. 58. 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 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. JKSPARK@HANAFOS.COM 104
  • 105. http://www.ggola.com 장 경 상 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 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 JKSPARK@HANAFOS.COM 105
  • 106. http://www.ggola.com 장 경 상 “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에도 동일하게 적용된다) SQL> exec dbms_scheduler.purge_log(10, ‘job_log’, ‘jobx’, ‘jobc_admin’); 59. 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); JKSPARK@HANAFOS.COM 106
  • 107. http://www.ggola.com 장 경 상 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) 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 JKSPARK@HANAFOS.COM 107
  • 108. http://www.ggola.com 장 경 상 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',- > 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되 어 적용된다는 뜻이다. JKSPARK@HANAFOS.COM 108
  • 109. http://www.ggola.com 장 경 상 60. Scheduler Management 61. 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’) > 62. Running Job 기본적은 job은 scheduling을 위해 설정을 하지만 경우에 따라 scheduling과 상관없이 직 접 실행을 할 필요도 있고 필요하다면 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을 지정할 수 있다. JKSPARK@HANAFOS.COM 109
  • 110. http://www.ggola.com 장 경 상 63. 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); 만일 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을 할 수 있다. 64. 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) JKSPARK@HANAFOS.COM 110
  • 111. http://www.ggola.com 장 경 상 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을 진행한다. 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가 필요 하다. JKSPARK@HANAFOS.COM 111
  • 112. http://www.ggola.com 장 경 상 65. 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, 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 Program program_action, program_type, number_of_arguments, comments Schedule repeat_interval, comments, end_date, start_date Job Class resource_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가 있어야 JKSPARK@HANAFOS.COM 112 표 4-6 Schedule r Object 와 속성
  • 113. http://www.ggola.com 장 경 상 한다. 물론, 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’); 현재 사용 가능한 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이다. 66. 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”보다 항상 우선한다는 보장은 없다. JKSPARK@HANAFOS.COM 113
  • 114. http://www.ggola.com 장 경 상 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되어 있는 window가 여전히 active하다. 2. 위 1에서 high priority가 overlap되면 low window는 close되고 high window가 open 된다. 3. window가 끝나는 시점에 여러 개의 window가 설정이 되어 있다면 남아있는 시간이 가장 긴 window가 open된다. 4. drop되는 window는 자동으로 close된다. 67. 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 정보 JKSPARK@HANAFOS.COM 114 표 4-7 Views of Schedule r
  • 115. http://www.ggola.com 장 경 상 CF. 위 view를 살펴보면 job class및 window object는 sys schema 소유이기 때문에 “user_”로 시작하는 view가 없다는 것을 인식하자. 68. Using em 위에서 설명한 schedule 작업은 em에서도 가능하다. 사실 job을 만드는 과정에 parameters가 많기 때문에 em이 더 편할 수 있다. 다음 화면에서 그 내용들을 확인할 수 있다. CF. 만일 여러분의 database에서 아직 em database control을 start하지 않았다면 chapter 7의 “em Start”부분을 먼저 확인하여 em을 start한 후 다음의 내용을 확인하기 바란다. 1. 먼저 “em  관리”를 선택한 화면이다. 2. 우측 중앙에서 “작업”을 선택해 보자. 앞서 생성한 job이 보이고 있다. 우측의 각 버튼을 통해 편집, 보기, 삭제, 수행 등 다양한 JKSPARK@HANAFOS.COM 115 그림 4-2 em의 관 리tab 화 면 그림 4-3 em의 job(작업) 화면
  • 116. http://www.ggola.com 장 경 상 작업이 가능하다는 것을 알 수 있다. 물론, 우측 상단의 “생성”버튼을 통해 새로 만들 수 도 있다. 3. 위에서 “생성” 버튼을 click하면 다음과 같은 화면이 나타난다. 이렇게 바로 생성할 수도 있지만 “SQL 표시”를 눌러서 그 script를 확인할 수도 있다. 다 음은 SQL로 표시한 것이다. JKSPARK@HANAFOS.COM 116 그림 4-4 em의 job(작업) 생성화면 그림 4-5 em의 job(작업) 생성 script 화 면
  • 117. http://www.ggola.com 장 경 상 앞서 수행했던 package를 그대로 사용이 가능하도록 보여주고 있다. 매우 편리한 기능이 아닐 수 없다. JKSPARK@HANAFOS.COM 117
  • 118. 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 JKSPARK@HANAFOS.COM 118
  • 119. http://www.ggola.com 장 경 상 69. Special Sort 70. Case, Accent를 무시하는 Sort 71. 개요 Oracle10g는 case와 accent를 무시하는 sort를 제공한다. 달리 표현하면 대, 소문자 구분 을 하지 않도록 하는 기능과 동일한 data에 accent만 다른 경우를 무시할 수 있는 기능을 제공한다는 것이다. 이 기능을 알기 위해서는 관련 parameter를 알아야 한다. 72. 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을 통해서 확인하도록 한다. JKSPARK@HANAFOS.COM 119
  • 120. http://www.ggola.com 장 경 상 73. Example 74. 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 JKSPARK@HANAFOS.COM 120
  • 121. 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; 75. 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 JKSPARK@HANAFOS.COM 121
  • 122. 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”등이 그것이다. 76. 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 JKSPARK@HANAFOS.COM 122
  • 123. 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 JKSPARK@HANAFOS.COM 123

×