O10g app support_11

341 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
341
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
5
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

O10g app support_11

  1. 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. 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. 3. http://www.ggola.com 장 경 상 75. NLS_COMP...............................................................................................121 76. NLSSORT...................................................................................................122 JKSPARK@HANAFOS.COM 3
  4. 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. 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. 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. 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. 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. 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. 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. 11. http://www.ggola.com 장 경 상 참조 =============================================================== internal connection : o9i 37p JKSPARK@HANAFOS.COM 11
  12. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 43. http://www.ggola.com 장 경 상 참조 =============================================================== fact : o8i 27p dimension : o8i 33p JKSPARK@HANAFOS.COM 43
  44. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 61. http://www.ggola.com 장 경 상 참조 =============================================================== workspace : o9i 408p olap : o8i 36p JKSPARK@HANAFOS.COM 61
  62. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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

×