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

2013년 8월 6일 화요일

다중 애플리케이션(Struts Multi-Application)

다중 애플리케이션(Multi-Application)


오라클자바커뮤니티에서 설립한 오엔제이프로그래밍 실무교육센터
(오라클SQL, 튜닝, 힌트,자바프레임워크, 안드로이드, 아이폰, 닷넷 실무전문 강의)   
 스트럿츠 1.1이상에서는 다중 애플리케이션 사용이 가능 합니다.

애플리케이션의 모듈이 동일한 웹애플리케이션의 일부 이지만 서로 독립적입니다.

다중 애플리케이션을 사용하는 이유는 업무를 좀 더 조직화, 세분화 할 수 있다는 장점이 있습니다.

쇼핑몰을 개발하는데 상품의 디스플레이 부분과 쇼핑카트를 구현 하는 부분을 별도의 다중 애플리케이션으로 구성 하는 것이 가능 합니다. 이렇게 하는 이유는 상호 독립적이고 수평적인 개발이 가능하도록 하기 위해서 입니다.


기본 애플리케이션이 아닌 모듈은 config/ 로 시작 합니다.

config/ 다음에 오는 부분은 애플리케이션 모듈의 접두어가 되며 프레임웍 전반을 걸쳐클라이언트의 요청을 처리하는데 사용 됩니다.

아래에 간략히 방법을 설명 하고 예제를 만들어 보도록 합니다.
(이클립스에서 톰캣 애플리테이션으로 Login이라는 프로젝트를 작성 후… 별도의 test라는 독립된 애플리케이션을 만들려고 합니다.)

1.        웹 애플리테이션의 web.xml 파일에 다중 애플리케이션을 위한 설정을 합니다.

<servlet>
          <servlet-name>action</servlet-name>
          <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
          <init-param>
                        <param-name>config</param-name>
                        <param-value>/WEB-INF/struts-config.xml</param-value>
          </init-param>
          <init-param>
                  <param-name>config/test</param-name>
                  <param-value>/WEB-INF/struts-test-config.xml</param-value>
          </init-param>
          <load-on-startup>1</load-on-startup>               
        </servlet>


2.        이번에 어떻게 모듈(애플리케이션)을 Switch하는지 알아 봅니다.

두 가지의 큰 방법이 있는데 아래와 같습니다.

-        forward를 이용하는 방법

struts-config.xml에서 test 애플리케이션으로 스위칭을 하기 위해서 forward를 이용합니다.

유심히 볼 부분은 contextRelative가 true가 되어 있다는 것이다. 이 값은 하나의 웹 애플리테이션을 사용 하는 경우에는 false(path 설정 시 기준을 애플리케이션을 기준으로)  이지만 다중 웹 애플리케이션을 사용 하기 위해서는 true(path 설정 시 기준이 context가 기준)로 설정 합니다.

즉 아래에서 test 라는 또 하나의 애플리케이션을 가리킬 때 /test/mysubmit과 같이 컨텍스트를 기준으로 경로를 사용 했습니다. (현재 톰캣 프로젝트를 하나 만들었죠^^ Login 이라는 것을…)

<struts-config>
...
<global-forwards>
<forward name="toModuleB"
contextRelative="true"
path="/test/mysubmit"
redirect="true"/>
...
</global-forwards>

</struts-config>



- org.apache.struts.actions.SwitchAction를 이용하는 방법

<action-mappings>
<action path="/toModule"
type="org.apache.struts.actions.SwitchAction"/>
...
</action-mappings>



이제 예제를 만들어 보도록 하겠습니다. 예제는 개념을 익히기 위해 최대한 간단히 작성 했습니다.


[처리흐름]

Login이라는 톰캣 프로젝트(웹애플리케이션)를 하나 만들고 별도의 독립적인 test라는 애플리케이션을 만들어 struts-config.xml과는 별도로 struts-test-config.xml을 만들어 보겠습니다. (물론 둘은 업무적으로 어떤 연관성을 가지지는 않고 있습니다. 예문에서는 단지 다중 애플리케이션을 설정하고 로딩 하는 것만 살펴 볼테니까요…)

사용자가 /Login/login.jsp를 실행 합니다.

실행 화면은 다음과 같습니다. (간단히 ID와 PASSWORD만 입력 받고 submit 버튼을 누릅니다.)

 

Submit 버튼을 누르면 이 액션을 /toTest라는 path로 넘어갑니다. 이 요청을 우선 Login 애플리케이션에서 받습니다.

<action
                path="/toTest"
                type="login2.toTest"
                validate="false" 
                name="loginForm"             
        />

위와 같이 설정이 되어 있습니다. 그래서 login2.toTest 라는 클래스가 실행을 하겠죠…

toTest.java 에서는 간단히 위에서 설명한 2번 방법대로 forward를 이용하여 test라는 애플리케이션으로 스위칭을 해 버립니다.

public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) {
                               
                //sub application인 test로 버네기 위해 forward 이용
                return (mapping.findForward("toTest"));
               
        }

위에서 return (mapping.findForward("toTest")); 부분의 처리를 위해 당연히 “toTest” 라는 forward가 있어야 겠죠^^

메인 설정 파일인 struts-config.xml에서 아래와 같이 정의하고 있습니다.

<forward name="toTest"
                contextRelative="true"
                path="/test/mysubmit.do"
                redirect="true"/>


이제 제어가 /test/mysubmit.do라는 액션으로 인해 test 라는 애플리케이션으로 넘어 가게 됩니다. 물론 위의 /test/mysubmit.do 는 struts-test-config.xml에서 정의를 하고 있습니다. 그 설정 내용은 다음과 같습니다.

<action-mappings>           
        <!-- 현재의 설정 파일은 struts-test-config.xml 이므로... path에 "/test/mysubmit" 아님을 주의! -->
        <action
                path="/mysubmit"   
                type="login2.Welcome2"   
                validate="false"                 
        />
         
    </action-mappings>


이제는 login2.Welcom2라는 클래스가 실행 되겠죠^^

그 내용은 단순히 result.jsp로 포워딩 시키는 일을 합니다.

요기까지 입니다.

그럼 이젠 전체 소스를 확인 하도록 하죠….

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


-------------------------------
/Login/test/login.jsp
-------------------------------
<%@ page pageEncoding="euc-kr" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<html>
<body>

<html:form action="/toTest" focus="id">
        <table>             
                <tr>                       
                        <th align="right">ID</th>                                             
                        <td><html:text property="id" value=""/></td>
                </tr>
                <tr>
                        <th align="right">PASSWORD</th>                                             
                        <td><html:password property="pwd" redisplay="false"/></td>
                </tr>
                <tr>                     
                        <th></th>
                        <td>
                            <html:submit/>
                            <html:reset/>                             
                        </td>
                </tr>                               
        </table>
</html:form>
</body>
</html>


-------------------------------
/WEB-INF/struts-config.xml
-------------------------------

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
     
    <!-- ========== Form Bean Definitions ================================== -->
    <form-beans>
        <form-bean name="loginForm" type="login2.LoginForm">                 
            <form-property name="pwd" type="java.lang.String" />
            <form-property name="id" type="java.lang.String" />           
        </form-bean>           
    </form-beans>
   
 
    <!-- ========== Global Forward Definitions =============================== -->
    <global-forwards>
        <forward name="toTest"
                contextRelative="true"
                path="/test/mysubmit.do"
                redirect="true"/>
    </global-forwards>
   
    <!-- ========== Action Mapping Definitions =============================== -->
    <!-- valiedate를 true라고 함으로써 LoginForm의 validate가 호출 됩니다.            -->
    <action-mappings>           
        <action
                path="/toTest"
                type="login2.toTest"
                validate="false" 
                name="loginForm"             
        />         
    </action-mappings> 
       
</struts-config>



-------------------------------
/WEB-INF/struts-test-config.xml
-------------------------------

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
   
    <!-- ========== Global Forward Definitions =============================== -->
    <global-forwards>
        <forward name="success" path="/result.jsp"/>
    </global-forwards>
   
    <action-mappings>           
        <!-- 현재의 설정 파일은 struts-test-config.xml 이므로... path에 "/test/mysubmit" 아님을 주의!  메인 애플리케이션의 foreard에 기술한 /test/mysubmit에 의해 아래의 매핑과 연결됨, 결국 login2.Welcom2가 실행됨-->
        <action
                path="/mysubmit"   
                type="login2.Welcome2"   
                validate="false"                 
        />
         
    </action-mappings>                         
</struts-config>


-------------------------------
/WEB-INF/src/login2/toTest.java
-------------------------------
package login2;

import org.apache.struts.action.Action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;


public class toTest extends Action {       
       
        public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) {
                               
                //sub application인 test로 보내기 위해 forward 이용
                return (mapping.findForward("toTest"));
               
        }
}


-------------------------------
/WEB-INF/src/login2/Welcome2.java
-------------------------------
package login2;

import org.apache.struts.action.Action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import login2.Constants;

public class Welcome2 extends Action {       
       
        public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) {
                               
                //성공적으로 처리 되었음, test 애플리케이션의 result.jsp로 보내버림...                return (mapping.findForward(Constants.SUCCESS));
               
        }
}




-------------------------------
/Login/test/result.jsp
-------------------------------
OK~ 

2013년 8월 5일 월요일

(오라클시퀀스, ORACLEJAVA,오라클자바교육)Oracle Sequence

오라클 시퀀스는 DB Table의 자동증분 컬럼에 사용하면 유용하다. 아래 내용을 확인하자.



오라클자바커뮤니티에서 설립한 오엔제이프로그래밍 실무교육센터
(오라클SQL, 튜닝, 힌트,자바프레임워크, 안드로이드, 아이폰, 닷넷 실무전문 강의) 



A. 개념
- Sequence는 순차적인 일련번호를 생성한다.(최대 38자리의 Oracle정수)
- Sequence는 다중 사용자환경에서 디스크 입출력 오버헤드나 Transaction Locking없이
고유한 순번을 생성하는데 용이하다.
- Sequence생성시 Sequence이름, 오름차순또는 내린차순여부, 번호간격등을 정의한다.
- Sequence번호는 Table과 독립적으로 생성되며,따라서 동일한 Sequence를 하나또는 여러
개의 Table에 사용할수 있다.
B. Sequence생성
- create sequence emp_sequence
increment by 1
start with 1
nomaxvalue
nocycle
cache 10;

cache Optiom은 일련의 Sequence를 미리 할당하고 메모리에 유지하면서 시퀀스를 빠르게 Access
할수있게 한다. 일련의 Sequence를 Cache하면 Sequence를 건너 뛸수있다.
예를들어 Instance가 비정상적으로 종료되면 캐시되었지만 사용안된 Sequence를 잃어버리게 된다.
즉 Cache를 상요하면 Sequence를 빠르게 Access할수 있으나 비정상적으로 종료돨 경우에는 번호가
건너 뛰게된다.

C. Sequence 변경
Alter sequence emp_sequence
Increment by 10
Maxvalue 10000
Cycle
Cache 20;
D. Sequence에 영향을 주는 변수
Sequence_cache_entries매개변수는 캐시될 시퀀스를 설정하다. 너무 낮으면 시퀀스값을 건너
뛸수도 있다. 예를들면 다섯개의 Cache된 Sequence를 사용하고 캐시가 다찼으며,매개변수 값이
4라면 현재 4개의 시퀀스가 Cache되었다면 캐시에서 최근에 가장적게 사용한 Sequence는 5번째
Sequence로 바뀌면서 이 Sequence의 모든 다른값(캐시된 마지막 Sequence까지)은 손실된다.

E. Sequence삭제
- drop sequence emp_sequence; 

2013년 8월 3일 토요일

Oracle SQL Tuning, Hint, SQL 연산(COUNT STOPKEY

SQL 연산(COUNT STOPKEY

구로디지털 오엔제이프로그래밍실무교육센터
www.onjprogramming.co.kr

COUNT STOPKEY연산은 PSEUDO COLUMNS(의사 컬럼) WHERE절에 나타날 때 실행계획에 나타나는 SQL 연산 입니다.

SQL> select  empno from emp
  2  where rownum < 2;

     EMPNO
----------
      7369

   : 00:00:00.03

Execution Plan
----------------------------------------------------------
Plan hash value: 1902391507

---------------------------------------------------------------------------
| Id  | Operation        | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT |        |     1 |     4 |     1   (0)| 00:00:01 |
|*  1 |  COUNT STOPKEY   |        |       |       |            |          |
|   2 |   INDEX FULL SCAN| PK_EMP |     1 |     4 |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------



2013년 8월 2일 금요일

[oracle hint]조인 방법 변경(USE_NL) , 오라클힌트강좌,오라클교육,오라클학원

오라클힌트-조인 방법 변경(USE_NL )
 
 
구로디지털 오엔제이프로그래밍실무교육센터
 
테이블을 조인 하는 경우 중첩 루프 조인(Nested Loop Join)이 일어나도록 하는 힌트 문장 입니다.중첩 루프 조인은 중첩 반복이라고도 하는데 하나의 테이블(outer/driving table)에서 추출된 로우를 가지고 일일이 다른 테이블(inner/probed table)을 반복해서 조회하여 찾아지는 레코드를 최종 데이터로 간주하는 방법 입니다.
 
즉 조인 입력 한 개를 외부 입력 테이블로 사용하고한 개는 내부(최하위입력 테이블로 사용하고 외부 루프는 외부 입력 테이블을 행 단위로 사용하고 각 외부 행에 대해 실행되는 내부 루프는 내부 입력 테이블에서 일치되는 행을 검색 하는거죠  이것을 원시 중첩 루프 조인이라고 하는데 검색에서 인덱스를 사용하는 경우에는 인덱스 중첩 루프 조인이라고 합니다.
 
예를 들어 EMP 테이블과 DEPT 테이블을 조인하는 경우 dept 테이블이 건수가 작다면 우선 이 테이블을 외부 루프로 해서 하나씩 읽으면서 이에 대응하는 emp 테이블의 데이터를 추출 하는 경우라면 중첩 루프 조인에 해당 합니다이때 emp 테이블의 경우 건수가 많다고 가정을 하면 대부분 인덱스를 이용하도록 하여 emp 테이블의 외래키인 deptno 컬럼은 대부분 인덱스를 걸게 되죠^^
 
중첩 루프 조인은 테이블 중 적어도 하나의 조인 컬럼에 대해 인덱스(or Hash Index)가 존재할 때 연관되는 방식으로 이 중첩 루프 조인에서 테이블 중 하나의 테이블 또는 중간 결과 셋을 대상으로FULL SCAN이 일어나게 됩니다이 테이블이 드라이빙 테이블이 되는데 이 테이블의 데이터 건마다 나머지 테이블에서 원하는 데이터를 추출하기 위해 대부분 인덱스를 사용하게 되는 겁니다.
 
보통 USE_NL 힌트 구문은 ORDERED 힌트 구문과 같이 사용되는데 USE_NL이 취하는 인자는 FROM절에서 두번째 나오는 테이블(비드라이빙 테이블, inner/probed table)을 명시해 주어야 합니다. 안수로 사용되지 않은 첫 번째 테이블은  outer/driving table이 되는 것입니다.
 
 
[형식]
 
 
[]
아래는 Oracle 11g에서 테스트 한 결과 입니다.

analyze table emp compute statistics
analyze table dept compute statistics

EMP 테이블의 deptno는 인덱스가 있다.
 
SQL> select
  2         e.ename,
  3             d.dname
  4  from   dept d, emp e
  5  where  e.deptno = d.deptno ;
 
ENAME      DNAME
---------- --------------
CLARK      ACCOUNTING
KING       ACCOUNTING
MILLER     ACCOUNTING
SMITH      RESEARCH
JONES      RESEARCH
SCOTT      RESEARCH
ADAMS      RESEARCH
FORD       RESEARCH
ALLEN      SALES
WARD       SALES
MARTIN     SALES
BLAKE      SALES
TURNER     SALES
JAMES      SALES
 
14 개의 행이 선택되었습니다.
 
   : 00:00:00.03
-----------------------------------------------------------------------------------------------
| Id  | Operation                    | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                |    14 |   252 |     6   (0)| 00:00:01 |
|   1 |  NESTED LOOPS                |                |       |       |            |          |
|   2 |   NESTED LOOPS               |                |    14 |   252 |     6   (0)| 00:00:01 |
|   3 |    TABLE ACCESS FULL         | DEPT           |     4 |    44 |     3   (0)| 00:00:01 |
|*  4 |    INDEX RANGE SCAN          | IDX_EMP_DEPTNO |     5 |       |     0   (0)| 00:00:01 |
|   5 |   TABLE ACCESS BY INDEX ROWID| EMP            |     4 |    28 |     1   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------
 
SQL> select
  2         e.ename,
  3             d.dname
  4  from   emp e, dept d
  5  where  e.deptno = d.deptno ;
 
ENAME      DNAME
---------- --------------
SMITH      RESEARCH
ALLEN      SALES
WARD       SALES
JONES      RESEARCH
MARTIN     SALES
BLAKE      SALES
CLARK      ACCOUNTING
SCOTT      RESEARCH
KING       ACCOUNTING
TURNER     SALES
ADAMS      RESEARCH
JAMES      SALES
FORD       RESEARCH
MILLER     ACCOUNTING
 
14 개의 행이 선택되었습니다.
 
   : 00:00:00.03
----------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |         |    14 |   252 |    17   (0)| 00:00:01 |
|   1 |  NESTED LOOPS                |         |       |       |            |          |
|   2 |   NESTED LOOPS               |         |    14 |   252 |    17   (0)| 00:00:01 |
|   3 |    TABLE ACCESS FULL         | EMP     |    14 |    98 |     3   (0)| 00:00:01 |
|*  4 |    INDEX UNIQUE SCAN         | PK_DEPT |     1 |       |     0   (0)| 00:00:01 |
|   5 |   TABLE ACCESS BY INDEX ROWID| DEPT    |     1 |    11 |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------------------
 
 
이번에는 USE_NL만 힌트를 주었는데 중첩 루프 조인이 되지 않는다. ORDERED와 같이 써야 한다.
 
SQL> select
  2         e.ename,
  3             d.dname
  4  from   dept d, emp e
  5  where  e.deptno = d.deptno   ;
 
-----------------------------------------------------------------------------------------------
| Id  | Operation                    | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                |    14 |   252 |     6  (17)| 00:00:01 |
|   1 |  MERGE JOIN                  |                |    14 |   252 |     6  (17)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID| EMP            |    14 |    98 |     2   (0)| 00:00:01 |
|   3 |    INDEX FULL SCAN           | IDX_EMP_DEPTNO |    14 |       |     1   (0)| 00:00:01 |
|*  4 |   SORT JOIN                  |                |     4 |    44 |     4  (25)| 00:00:01 |
|   5 |    TABLE ACCESS FULL         | DEPT           |     4 |    44 |     3   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------
 
힌트를 사용 안 했을 때와 같다.
 
SQL> select
  2         e.ename,
  3             d.dname
  4  from   dept d, emp e
  5  where  e.deptno = d.deptno;
 
-----------------------------------------------------------------------------------------------
| Id  | Operation                    | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                |    14 |   252 |     6  (17)| 00:00:01 |
|   1 |  MERGE JOIN                  |                |    14 |   252 |     6  (17)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID| EMP            |    14 |    98 |     2   (0)| 00:00:01 |
|   3 |    INDEX FULL SCAN           | IDX_EMP_DEPTNO |    14 |       |     1   (0)| 00:00:01 |
|*  4 |   SORT JOIN                  |                |     4 |    44 |     4  (25)| 00:00:01 |
|   5 |    TABLE ACCESS FULL         | DEPT           |     4 |    44 |     3   (0)| 00:00:01 |
 
 
[실습]
 
-      실습을 위한 예제 테이블 및 데이터는 아래 링크에서 확인 바랍니다.
 
myemp1 : 1000만건
myemp1_old : 100만건
mydept : 5
 
테스트환경 : oracle 11g
 
 
 
우선 인덱스를 만들자.
 
SQL> create index idx_myemp1_deptno on myemp1(deptno)
SQL> set autotrace on explain
SQL> set autotrace traceonly
SQL>alter system optimizer_mode = all_rows  -- CBO로 동작
 
 
아무런 힌트도 주지 않으니 HASH JOIN을 한다.
 
SQL> select e.ename,
  2         d.dname
  3  from   mydept1 d, myemp1 e
  4  where  e.deptno = d.deptno
  5  ;
 
20000000 개의 행이 선택되었습니다.
 
   : 00:01:42.55  -- 시간이  걸린다.
 
------------------------------------------------------------------------------
| Id  | Operation          | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |         |    20M|  1525M| 17043   (2)| 00:03:25 |
|*  1 |  HASH JOIN         |         |    20M|  1525M| 17043   (2)| 00:03:25 |
|   2 |   TABLE ACCESS FULL| MYDEPT1 |    10 |   650 |     3   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| MYEMP1  |    10M|   143M| 16941   (1)| 00:03:24 |
------------------------------------------------------------------------------
 
 
 
이번에는 중첩 루프 조인을 걸어보자.
 
 
SQL> select
  2         e.ename,
  3         d.dname
  4  from   mydept1 d, myemp1 e
  5  where  e.deptno = d.deptno   ;
 
20000000 개의 행이 선택되었습니다.
 
   : 00:01:57.85
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1400616069
 
------------------------------------------------------------------------------
| Id  | Operation          | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |         |    20M|  1525M|   169K  (1)| 00:33:53 |
|   1 |  NESTED LOOPS      |         |    20M|  1525M|   169K  (1)| 00:33:53 |
|   2 |   TABLE ACCESS FULL| MYDEPT1 |    10 |   650 |     3   (0)| 00:00:01 |
|*  3 |   TABLE ACCESS FULL| MYEMP1  |  2000K|    28M| 16939   (1)| 00:03:24 |
------------------------------------------------------------------------------
 
이번에는 인덱스를 이용하기 위해 추가로 인덱스 힌트를 넣었자어마어마하게 늦다.
(4)
 
SQL> select
  2         e.ename,
  3         d.dname
  4  from   mydept1 d, myemp1 e
  5  where  e.deptno = d.deptno ;
 
20000000 개의 행이 선택되었습니다.
 
   : 00:04:17.13
 
 
--------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name              | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                   |    20M|  1525M|   659K  (1)| 02:11:59 |
|   1 |  NESTED LOOPS                |                   |       |       |            |          |
|   2 |   NESTED LOOPS               |                   |    20M|  1525M|   659K  (1)| 02:11:59 |
|   3 |    TABLE ACCESS FULL         | MYDEPT1           |    10 |   650 |     3   (0)| 00:00:01 |
|*  4 |    INDEX RANGE SCAN          | IDX_MYEMP1_DEPTNO |  2000K|       |  3865   (1)| 00:00:47 |
|   5 |   TABLE ACCESS BY INDEX ROWID| MYEMP1            |  2000K|    28M| 65985   (1)| 00:13:12 |
--------------------------------------------------------------------------------------------------
 
 
이번에는 드라이빙 테이블을 바구어 보자.
 
 MYEMP1(1000만건)이 드라이빙 테이블이 되니 좀 느리다그래도 mydept1이 드라이빙 테이블이 되고 myemp1을 인덱스 범위 검색하는 것 보다는 빠르다.
 
SQL> select
  2         e.ename,
  3         d.dname
  4  from   myemp1 e, mydept1 d
  5  where  e.deptno = d.deptno  ;
 
20000000 개의 행이 선택되었습니다.
 
   : 00:03:16.12
 
 
------------------------------------------------------------------------------
| Id  | Operation          | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |         |    20M|  1525M|    13M  (1)| 45:15:25 |
|   1 |  NESTED LOOPS      |         |    20M|  1525M|    13M  (1)| 45:15:25 |
|   2 |   TABLE ACCESS FULL| MYEMP1  |    10M|   143M| 16941   (1)| 00:03:24 |
|*  3 |   TABLE ACCESS FULL| MYDEPT1 |     2 |   130 |     1   (0)| 00:00:01 | 

오라클힌트강좌[oracle hint]ACCESS 경로를 변경하는 힌트(REWRITE)

[Hint]ACCESS 경로를 변경하는 힌트(REWRITE)
 

 
구로디지털 오엔제이프로그래밍실무교육센터


이 힌트는 CBO에서 Matreriakized views(구체화뷰)에 대해 Query Rewrite가 일어나도록 하는 힌트인데 8i 이상부터 가능 합니다. REWRITE 힌트 구문에 VIEW가 인자로 와도 되고 안 와도 됩니다인자로 뷰 리스트를 주지 않는 경우 적절한
materialized view를 찾고 항상 비용(COST)과 관계없이 사용 합니다.
 
Materialized views라는 것이 DW(Data WareHouse)에서 집계 데이터 등을 추출할 때 쿼리 수행속도를 빠르게 해주는 것인데 Oracle에서 Query Rewrite가 일어나기 위해서는 다음과 같은 조건이 만족 되어야 합니다.
 
OPTIMIZER_MODE = ALL_ROWS or FIRST_ROWS or CHOOSE
QUERY_REWRITE_ENABLED = TRUE
COMPITABLE = 8.1.0 이상
 
사용 형식은 다음과 같습니다.
 
[형식]
 
 
 
 
create materialized view  sum_sales
build immediate  
refresh complete  -- 만들어지자 마자 실행 가능한 상태로 원본테이블이 변경되면모두 갱신함, FAST 갱신된 데이터만 반영, NEVER 반영안함
enable query rewrite  -- 오라클이 데이터 검색시 구체화뷰를 통해 검색하도록
as
select deptno,
       sum(sal) sum_sales
from   emp
group by deptno
 
만약 위 Query 실행 시 권한이 없다면서 에러가 난다면 관리자로 로그인 하여 다음과 같이 실습 계정에 권한을 주시기 바랍니다.
 
grant create any materialized view to scott;
grant global query rewrite to scott;
grant query rewrite to scott;
grant alter any materialized view to scott;
 
MVIEW를 사용하지 않는 모양의 질의를 한번 볼까여
 
위와 같이 한 후 다음과 같은 SQL문을 실행 합니다.
 
select deptno, sum(sal)
from   emp
group by deptno
 
 
--------------------------------------------------------------------
Operation            Object Name      Rows     Bytes    Cost     
-----------------------------------------------------------------
SELECT STATEMENT Optimizer Mode=ALL_ROWS               3                        4
  HASH GROUP BY                        3           15         4                                   
    TABLE ACCESS FULL             SCOTT.EMP        15         75         3                                                     
 
우선 세션 레벨에서 query_rewrite_enabled true 바꿉니다.
 
alter session set query_rewrite_enabled=true;
 
 
select 
       deptno, sum(sal)
from   emp
group by deptno
 
 
----------------------------------------------------------------
Operation            Object Name      Rows     Bytes    Cost     
----------------------------------------------------------------
SELECT STATEMENT Optimizer Mode=ALL_ROWS               4                        3
MAT_VIEW REWRITE ACCESS FULL         SCOTT.SUM_SALES         4           104       3 
 
위 실행 계획을 보면 EMP 테이블을 이용하지 않고 SUM_SALES라는 MVIEW를 이용하여 실행 계획이 만들어 짐을 알 수 있습니다.
 
 
 
[실습]
 
-      실습을 위한 예제 테이블 및 데이터는 아래 링크에서 확인 바랍니다.
 
myemp1 : 1000만건
myemp1_old : 100만건
mydept : 5
 
테스트환경 : oracle 11g
 
 
우선 구체화뷰를 만들지 않고 CBO, RBO 경우 아래의 쿼리를 날려보자.
 
select e.deptno,
       avg(sal) sal_sum
from   myemp1 e, mydept1 d
where  e.deptno = d.deptno
group by e.deptno;
 
 
SQL>alter session set optimizer_mode = choose  --myemp1통계정보 있으니 CBO로 동작
 
SQL> select e.deptno,
  2         avg(sal) sal_sum
  3  from   myemp1 e, mydept1 d
  4  where  e.deptno = d.deptno
  5  group by e.deptno;
 
    DEPTNO    SAL_SUM
---------- ----------
         0   999997.5
         1   999998.5
         2   999999.5
         3  1000000.5
         4  1000001.5
 
   : 00:00:07.93
 
---------------------------------------------------------------------------
| Id  | Operation              | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |          |     1 |    41 | 17484   (4)| 00:03:30 |
|   1 |  SORT GROUP BY NOSORT  |          |     1 |    41 | 17484   (4)| 00:03:30 |
|   2 |   MERGE JOIN           |          |    10 |   410 | 17484   (4)| 00:03:30 |
|   3 |    SORT JOIN           |          |     5 |   140 | 17480   (4)| 00:03:30 |
|   4 |     VIEW               | VW_GBC_5 |     5 |   140 | 17480   (4)| 00:03:30 |
|   5 |      HASH GROUP BY     |          |     5 |    35 | 17480   (4)| 00:03:30 |
|   6 |       TABLE ACCESS FULL| MYEMP1   |    10M|    66M| 16961   (1)| 00:03:24 |
|*  7 |    SORT JOIN           |          |    10 |   130 |     4  (25)| 00:00:01 |
|   8 |     TABLE ACCESS FULL  | MYDEPT1  |    10 |   130 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------
 
8초 정도 걸렸다.
 
이번에는 RBO로 붜리를 실행해 보자. (2분 넘는다)
 
SQL> alter session set optimizer_mode = rule
 
SQL> select e.deptno,
  2         avg(sal) sal_sum
  3  from   myemp1 e, mydept1 d
  4  where  e.deptno = d.deptno
  5  group by e.deptno;
 
    DEPTNO    SAL_SUM
---------- ----------
         0   999997.5
         1   999998.5
         2   999999.5
         3  1000000.5
         4  1000001.5
 
   : 00:02:17.53
 
-----------------------------------------------------------
| Id  | Operation                     | Name              |
-----------------------------------------------------------
|   0 | SELECT STATEMENT              |                   |
|   1 |  SORT GROUP BY                |                   |
|   2 |   NESTED LOOPS                |                   |
|   3 |    NESTED LOOPS               |                   |
|   4 |     TABLE ACCESS FULL         | MYDEPT1           |
|*  5 |     INDEX RANGE SCAN          | IDX_MYEMP1_DEPTNO |
|   6 |    TABLE ACCESS BY INDEX ROWID| MYEMP1            |
-----------------------------------------------------------
 
지금 부터는 물리뷰를 만들어서 실습하자.
Materialized views를 하나 만듭니다.
 
 
SQL> create materialized view  avg_myemp_sales
  2  build immediate
  3  refresh complete
  4  enable query rewrite
  5  as
  6  select e.deptno,
  7         avg(sal) sal_sum
  8  from   myemp1 e, mydept1 d
  9  where  e.deptno = d.deptno
 10  group by e.deptno;
구체화된 뷰가 생성되었습니다.
 
 
n  이번에는 구체화 뷰를 사용 안하고 질의해 보자엄청 시간 걸린다.(거의 3)
n  뷰를 만들지 말고 먼저 질의 해 보라.
 
 
 
SQL>
SQL>-- 뷰를 사용하도록 힌트 구성(힌트를 안써도 구체화뷰를 통해 데이터를 가져 올 것이다.)
SQL> select e.deptno,
  2         avg(sal) sal_sum
  3  from   myemp1 e, mydept1 d
  4  where  e.deptno = d.deptno
  5  group by e.deptno;
 
    DEPTNO    SAL_SUM
---------- ----------
         0   999997.5
         1   999998.5
         2   999999.5
         3  1000000.5
         4  1000001.5
 
   : 00:00:00.23  -- 바로 나온다
 
 
------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name            | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                 |     5 |   130 |     3   (0)| 00:00:01 |
|   1 |  MAT_VIEW REWRITE ACCESS FULL| AVG_MYEMP_SALES |     5 |   130 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------