레이블이 오엔제이프로그래밍실무교육인 게시물을 표시합니다. 모든 게시물 표시
레이블이 오엔제이프로그래밍실무교육인 게시물을 표시합니다. 모든 게시물 표시

2013년 10월 28일 월요일

Column Name & Constraints 이름 변경 예 테이블이나 인덱스의 이름을 변경 운영하는 개발자 전문교육 ,개인80%환급(www.onjprogramming.co.kr) [주간] [11/4]Spring3.X, MyBatis, Hibernate실무과정 [11/6]SQL초보에서실전전문가까지 [평일야간] [11/1]C#,ASP.NET마스터 [11/5]iPhone 하이브리드 앱 개발 실무과정 [11/7]JAVA&WEB프레임워크실무과정 [11/8]Spring3.X, MyBatis, Hibernate실무과정 [주말] [11/2]C#,ASP.NET마스터 [11/2]Spring3.X, MyBatis, Hibernate실무과정 [11/2]JAVA&WEB프레임워크실무과정 [11/9]안드로이드개발자과정 JAVA ORACLE iPhone/Android .NET 표준웹/HTML5 채용/취업무료교육 초보자(재학생)코스 Spring3.X, MyBatis, Hibernate실무과정 총 5일 35시간 11-04 JAVA&WEB프레임워크실무과정 총 33일 99시간 11-07 Spring3.X, MyBatis, Hibernate실무과정 총 12일 36시간 11-08 자바초보에서안드로이드까지 총 18일 54시간 11-15 Spring3.X, MyBatis, Hibernate실무과정 총 5일 35시간 11-02 JAVA&WEB프레임워크실무과정 총 14일 98시간 11-02 SQL초보에서실전전문가까지 총 8일 56시간 11-06 고급개발자를위한 오라클힌트&SQL튜닝 총 10일 30시간 11-08 SQL초보에서실전전문가까지 총 18일 54시간 11-13 고급개발자를위한 오라클힌트&SQL튜닝 총 4일 32시간 11-09 SQL초보에서실전전문가까지 총 8일 56시간 11-10 Column Name & Constraints 이름 변경 예 테이블이나 인덱스의 이름을 변경하는 것은 오라클 9iR2 이전에도 가능했지만 9iR2에서는 테이블의 컬럼 명 또는 제약조건의 이름을 변경하는 것이 가능해 졌습니다. 실제 예제를 통해 확인해 보자구요~~ SQL> create table test ( 2 c1 varchar2(4) not null, 3 c2 number(10) not null 4 ); 테이블이 생성되었습니다. 프라이머리 키를 추가 합니다. 이때 C1컬럼에 대해 인덱스가 생성 됩니다. SQL> alter table test add (constraint pk_test 2 primary key (c1)); 테이블이 변경되었습니다. SQL> desc test; 이름 널? 유형 ----------------------------------------- -------- -------------- C1 NOT NULL VARCHAR2(4) C2 NOT NULL NUMBER(10) 사용자의 제약 조건을 확인 할 수 있는 USER_CONSTRAINTS VIEW를 통해 TEST 테이블에 제약조건의 타입이 ‘P’ 인것 즉 Primary Key인 제약조건을 검색 합니다. 제약조건에는 NOT NULL, UNIQUE, CHECK, PRIMARY KEY등 테이블의 컬럼에 제약을 가하는 조건을 말합니다. SQL> select constraint_name 2 from user_constraints 3 where table_name = 'TEST' 4 and constraint_type = 'P'; CONSTRAINT_NAME ------------------------------ PK_TEST 이번에는 TEST 테이블에 생성되어 있는 인덱스를 확인 합니다. 위에서 C1 컬럼을 Primary Key로 설정하여 저절로 이 컬럼에 대한 인덱스가 생성되어 있습니다. SQL> select index_name, 2 column_name 3 from user_ind_columns 4 where table_name = 'TEST'; INDEX_NAME COLUMN_NAME ------------------------------------ PK_TEST C1 우선 테이블의 이름을 바꾸어 봅니다. 이 기능은 오라클의 이전 버전에서도 되는 기능 입니다… SQL> alter table test rename to test1; 테이블이 변경되었습니다. 이번에는 컬럼명을 바꾸어 보죠^^ SQL> alter table test1 rename column c1 to code; 테이블이 변경되었습니다. Primary Ket 제약 조건의 이름을 변경 합니다. SQL> alter table test1 rename constraint pk_test to pk_test1; 테이블이 변경되었습니다. 이번에는 Primary Key에 걸린 인덱스의 이름을 바꿉니다. SQL> alter index pk_test rename to pk_test1; 인덱스가 변경되었습니다. 위에서 변경한 내역에 대해 확인해 보겠습니다… SQL> select constraint_name 2 from user_constraints 3 where table_name = 'TEST1' 4 and constraint_type = 'P'; CONSTRAINT_NAME ------------------------------ PK_TEST1 SQL> select index_name, 2 column_name 3 from user_ind_columns 4 where table_name = 'TEST1'; INDEX_NAME COLUMN_NAME --------------------------------------------- PK_TEST1 CODE [출처] 오라클자바커뮤니티 - http://www.oraclejavanew.kr/bbs/board.php?bo_table=LecSQLnPlSql&wr_id=107 [개강확정강좌]오라클자바커뮤니티에서 운영하는 개발자 전문교육 ,개인80%환급(www.onjprogramming.co.kr) [주간] [11/4]Spring3.X, MyBatis, Hibernate실무과정 [11/6]SQL초보에서실전전문가까지 [평일야간] [11/1]C#,ASP.NET마스터 [11/5]iPhone 하이브리드 앱 개발 실무과정 [11/7]JAVA&WEB프레임워크실무과정 [11/8]Spring3.X, MyBatis, Hibernate실무과정 [주말] [11/2]C#,ASP.NET마스터 [11/2]Spring3.X, MyBatis, Hibernate실무과정 [11/2]JAVA&WEB프레임워크실무과정 [11/9]안드로이드개발자과정 JAVA ORACLE iPhone/Android .NET 표준웹/HTML5 채용/취업무료교육 초보자(재학생)코스 Spring3.X, MyBatis, Hibernate실무과정 총 5일 35시간 11-04 JAVA&WEB프레임워크실무과정 총 33일 99시간 11-07 Spring3.X, MyBatis, Hibernate실무과정 총 12일 36시간 11-08 자바초보에서안드로이드까지 총 18일 54시간 11-15 Spring3.X, MyBatis, Hibernate실무과정 총 5일 35시간 11-02 JAVA&WEB프레임워크실무과정 총 14일 98시간 11-02 SQL초보에서실전전문가까지 총 8일 56시간 11-06 고급개발자를위한 오라클힌트&SQL튜닝 총 10일 30시간 11-08 SQL초보에서실전전문가까지 총 18일 54시간 11-13 고급개발자를위한 오라클힌트&SQL튜닝 총 4일 32시간 11-09 SQL초보에서실전전문가까지 총 8일 56시간 11-10

Column Name & Constraints 이름 변경 예

테이블이나 인덱스의 이름을 변경하는 것은 오라클 9iR2 이전에도 가능했지만 9iR2에서는 테이블의 컬럼 명 또는 제약조건의 이름을 변경하는 것이 가능해 졌습니다.

실제 예제를 통해 확인해 보자구요~~


SQL> create table test (
  2  c1 varchar2(4) not null,
  3  c2 number(10)  not null
  4  );

테이블이 생성되었습니다.

프라이머리 키를 추가 합니다. 이때 C1컬럼에 대해 인덱스가 생성 됩니다.

SQL> alter table test add (constraint pk_test
  2                        primary key (c1));

테이블이 변경되었습니다.

SQL> desc test;
 이름                                      널?      유형
 ----------------------------------------- -------- --------------

 C1                                        NOT NULL VARCHAR2(4)
 C2                                        NOT NULL NUMBER(10)

사용자의 제약 조건을 확인 할  수 있는 USER_CONSTRAINTS VIEW를 통해 TEST 테이블에 제약조건의 타입이 ‘P’ 인것 즉 Primary Key인 제약조건을 검색 합니다. 제약조건에는 NOT NULL, UNIQUE, CHECK, PRIMARY KEY등 테이블의 컬럼에 제약을 가하는 조건을 말합니다.

SQL> select constraint_name
  2  from  user_constraints
  3  where  table_name = 'TEST'
  4  and    constraint_type = 'P';

CONSTRAINT_NAME
------------------------------
PK_TEST

이번에는 TEST 테이블에 생성되어 있는 인덱스를 확인 합니다. 위에서 C1 컬럼을 Primary Key로 설정하여 저절로 이 컬럼에 대한 인덱스가 생성되어 있습니다.

SQL> select index_name,
  2        column_name
  3  from  user_ind_columns
  4  where  table_name = 'TEST';

INDEX_NAME          COLUMN_NAME
------------------------------------

PK_TEST                    C1

우선 테이블의 이름을 바꾸어 봅니다. 이 기능은 오라클의 이전 버전에서도 되는 기능 입니다…

SQL> alter table test rename to test1;

테이블이 변경되었습니다.

이번에는 컬럼명을 바꾸어 보죠^^

SQL> alter table test1 rename column c1 to code;

테이블이 변경되었습니다.

Primary Ket 제약 조건의 이름을 변경 합니다.

SQL> alter table test1 rename constraint pk_test to pk_test1;

테이블이 변경되었습니다.

이번에는 Primary Key에 걸린 인덱스의 이름을 바꿉니다.

SQL> alter index pk_test rename to pk_test1;

인덱스가 변경되었습니다.

위에서 변경한 내역에 대해 확인해 보겠습니다…

SQL> select constraint_name
  2  from  user_constraints
  3  where  table_name = 'TEST1'
  4  and    constraint_type = 'P';

CONSTRAINT_NAME
------------------------------
PK_TEST1

SQL> select index_name,
  2        column_name
  3  from  user_ind_columns
  4  where  table_name = 'TEST1';

INDEX_NAME            COLUMN_NAME
---------------------------------------------

PK_TEST1                    CODE



2013년 8월 8일 목요일

[오라클자바닷넷교]Struts Logic Custom Tag

Struts Logic 커스텀 태그 


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


Logic 태그 라이브러리는 출력 텍스트를 조건을 걸어 생성하는 경우나 객체 컬렉션을 돌면서 출력 텍스트를 반복적으로 생성 하는 경우 그리고 애플리케이션의 흐름을 제어 하는 경우 등에 유용한 태그들을 포함하고 있습니다.

사용 용도에 따라 값 비교, 부분 문자열 매칭, 리다이렉팅과 포워딩, 컬렉션 유틸리티  4가지 정도로 기능상 분류 할 수 있습니다.

아래는 Logic 태그 라이브러리내의 커스텀 태그 입니다.

Custom tags within the Logic tag library

empty : 요청한 변수가  null 또는 빈 문자열인 경우 이 태그의 BODY를 수행
equal : 요청한 변수가 지정한 값과 동일한 경우 이 태그의 BODY를 수행
forward : ActionForward Entry를 통해 지정한 페이지로 포워드를 수행
greaterEqual : 요청한 변수가 지정한 값보다 크거나 같으면 이 태그의 BODY를 수행
greaterThan : 요청한 변수가 지정한 값보다 크면 이 태그의 BODY를 수행
iterate : 지정한 컬렉션으로 이 태그내의 BODY 컨텐츠를 반복
lessEqual : 요청한 변수가 지정한 값보다 작거나 동일한 경우 이 태그의 BODY를 수행
lessThan : 요청한 변수가 지정한 값보다 작은 경우 이 태그의 BODY를 수행
match : 지정한 값이 요청한 변수에 포함되어 일치하는 경우 이 태그의 컨텐츠를 수행
messageNotPresent : 지정한 메시지가 이 요청에  없는 경우 이 태그의 BODY 컨텐츠를 생성
messagePresent : 지정한 메시지가 이 요청에  있는 경우 이 태그의 BODY 컨텐츠를 생성
notEmpty : 요청한 변수가 null도 빈 문자열도 아닌 경우 이 태그의 BODY 컨텐츠를 수행
notEqual : 요청한 변수가 지정한 값과 동일하지 않은 경우 이 태그의 BODY 컨텐츠를 수행
notMatch : 지정한 값이 요청한 변수의 문자열의 부분에 없는 경우 이 태그의 BODY 컨텐츠를 수행

notPresent : 지정한 값이 이 요청에 없는 경우 이 태그의 BODY 컨텐츠를 수행
present : 지정한 값이 이 요청에 있는 경우 이 태그의 BODY 컨텐츠를 수행
redirect : HTTP 리다이렉트를 표시


1.        값 비교

비교 결과가 “true” 인 경우에만 body 컨텐츠를 출력 합니다.

값을 취하여 VALUE 속성의 값과 비교, 태그에 주어진 값이 숫자로 변환되는 경우 숫자비교를 수행, 숫자로 변환되지 않는 경우 문자열 지교

<logic:present parameter="id">
<!—요청 파라미터에 “id”가 있으면 이 부분이 출력됨 -->
</logic:present>

어떤 컬렉션에서 반복을 수행하기 전에 컬렉션이 비었는지 여부를 체크하기 위해 아래와 같이 notEmpty를 사용 합니다.

<logic:notEmpty name="userSummary" property="addresses">
<!—사용자의 address 컬렉션에서 모든 객체들을 돌며 반복 출력 합니다.-->
<logic:iterate id="address" name="userSummary" property="addresses">
<!-- Print out the address obejct in a table -->
</logic:iterate>
</logic:notEmpty>

아래는 ActionForm내의 property와 숫자 값을 비교하는 예 입니다.

<logic:lessThan property="age" value="21">
<!-- 21세보다 나이가 작은 경우 이 부분이 실행 -->
</logic:lessThan>

아래는 bean1.value와 bean2.value가 같을 경우 본문을 수행 합니다.

<bean:define id=”value2” name=”beans2” property=”value”/>
<logic:equal value=”<%= (String) value2 %>” name=”bean1” property=”value”>
Equals~~~
</logic:equal>

<logic:equal name="bean" property="doubleProperty" value="<%= doub1 %>">
equal
</logic:equal>
<logic:greaterEqual name="bean" property="doubleProperty" value="<%= doub1 %>">
greaterEqual
</logic:greaterEqual>


이러한 비교를 위한 평가용 태그(equal, notEqual,greaterEqual, lessEqual, greaterThan, leeThan, match, notMatch)는 모두 평가 할 value 프로퍼티를 지정 해야 합니다.

아래는 empty와 notEmpty예제 입니다.

<logic:empty name="myBean">
The bean is missing
</logic:empty>
<logic:notEmpty name="myBean">
The bean is not missing
</logic:notEmpty>


2.        부분 문자열 매칭

value속성에 지정한 문자열과  다른 속성을 통해 주어진 값과 비교를 수행 합니다. 이때 주어지는 값은 cookie, header, parametert, property, name 속성들중 하나 입니다. 매칭 태그에는 값 비교 태그에는 없는 location 속성이 있는데 이는 태그가 문자열의 시작에서  또는 끝부분에서 매칭을 시작할 것인가를 알려 줍니다.

아래는 요청 파라미터 “action”의 값이 문자열 “processLogin”으로 시작하는지 여부를 결정하는 매칭 태그 입니다.

<logic:matchTag parameter="action" value="processLogin" location="start">
Processing Login....
</logic:matchTag>

만약 “location” 속성을 지정하지 않으면 변수와 value 속성값 사이의 매칭은 아무 곳에서나 시작 합니다. Location이 “start”인 경우 value 속성의 값으로 문자열이 시작해야 ”true”임

[브라우저 타입을 확인 하는 예제]

<logic:match header="User-Agent" value="Mozilla">
Mozilla!
</logic:match>
<logic:notMatch header="User-Agent" value="Mozilla">
Not Mozilla :(
</logic:notMatch>


[아래는 빈 프로퍼티와 문자열을 비교 합니다.]
<logic:match name="bean" property="stringProperty" value="hello world">
Hello World!
</logic:match>
<logic:notMatch name="bean" property="stringProperty" value="hello world">
I'm so sad and lonely.
</logic:notMatch>

3.        리다이렉팅과 포워딩

redirect 태그는 컨테이너가 지원하는 경우 URL을 완전히 다시 써서 클라이언트에 보냅니다.

forward 태그는 지정된 전역 ActionForward로 포워딩/리다이렉팅을 수행, redirect와 달리 name 속성을 포함하는데 이것은 ActionForward의 논리적인 이름 입니다. 스트럿츠에서 forward인지 redirext인지는 설정 파일에서 기술 합니다.

아래는 redirect 예제 입니다.

<logic:notPresent name="loginForm">
<logic:redirect href="/registeruser/index.jsp" />
</logic:notPresent>

아래는 포워드 예젭니다.

<logic:forward name="login" />

설정 파일에서는 ……

<global-forwards>
<forward name="login" path="/loginForm.jsp"/>
</global-forwards>




4.        컬렉션 유틸리티

Iterate 태그는 지정된 컬렉션내에 있는 모든 요소에 대해 한번씩 자신의 BODY 컨텐츠를 반복 합니다. id라는 속성을 가지는데 이는 반복에 사용 할 요소 등을 포함하는 페이지 스코프의 JSP 빈 이름 입니다.

<logic:iterate id="address" name="userSummary" property="addresses">
<!-- Print out the address obejct in a table -->
</logic:iterate>

위에서 iterate 태그는 userSummary 빈의 getAddress() 메소드를 호출 함으로써 address 컬렉션을 얻어 냅니다.각 반복 동안 address 컬렉션에 포함된 각각의 주소가 “address” 변수에 차례로 할당 됩니다.

아래의 예제를 참고 하세요~

<UL>
        <logic:iterate id=”item” name=”list”>
                <LI><bean:write name=”item”/></LI>
        </logic:iterate>
</UL>

다음 예제도 참고 하세요…

<logic:iterate id="employee" name="department" property="employees" scope= "request">

<bean:write name="employee" property="username" />

<bean:write name="employee" property="name" />

<bean:write name="employee" property="phone" />

</logic:iterate>

[예제]

        다음과 같은 빈이 있을 때


import java.io.Serializable;
public class User implements Serializable {
private String lastName;
private String firstName;
private String email;
public String getEmail() {
return email;
}
...
public void setEmail(String string) {
email = string;
}
...
}


        Action에서 다음과 같은 처리를 한 경우


while (rs.next()){
String firstName = rs.getString(1);
String lastName = rs.getString(2);
String email = rs.getString(3);

User user = new User();
user.setEmail(email);
user.setFirstName(firstName);
user.setLastName(lastName);

list.add(user);
}

if (list.size() > 0){
request.setAttribute("users", list);
}

        JSP 파일에서 처리 예

<logic:present name="users">
<table border="1">
<tr>
<th>
<bean:message key="userRegistration.firstName"/>
</th>
<th>
<bean:message key="userRegistration.lastName" />
</th>
<th>
<bean:message key="userRegistration.email" />
</th>
</tr>
<logic:iterate id="user" name="users">
<tr>
<td>
<bean:write name="user" property="firstName"/>
</td>
<td>
<bean:write name="user" property="lastName"/>
</td>
<td>
<bean:write name="user" property="email"/>
</td>
</tr>
</logic:iterate>
</table>
</logic:present>





5.        메시지와 에러

messagePresent와 messageNotPresent 테그는 요청 스코프에 ActionMessage나 ActionErrors 객체가 존재하는지에 따라 BODY 컨텐츠의 수행 여부를 결정 합니다. 

2013년 8월 3일 토요일

ORACLE Tuning, [oracle hint]조인 방법 변경(HASH_AJ)

[Hint]조인 방법 변경(HASH_AJ)
 
구로디지털 오엔제이프로그래밍실무교육센터
 
ANTI 조인은 테이블의 레코드를 추출하는 경우 조인의 대상이 되는 테이블과 일치하지 않는 데이터를 추출하는 연산 입니다. SQL연산에서 NOT IN, NOT EXISTS, MINUS등이 해당되는데 이러한 안티 조인은 MERGE ANTI-JOIN or HASH ANTI_JOIN으로 풀리도록 할 수 있는데HASH_AJ에 대해 살펴보도록 하죠,,.,
 
주로 NOT IN등의 SQL문에 이용되며 힌트 구문은 다음과 같이 서브 쿼리에 명시해야 하며 서브 쿼리의 WHERE절에 NOT NULL 조건도 명시해줘야 합니다.
 
[형식]
 
SQL> SELECT ENAME, SAL
      FROM  EMP
      WHERE EMPNO IS NOT NULL
      AND    ENAME IS NOT NULL
      AND    (EMPNO, ENAME) NOT IN
                                  (SELECT
                                           EMPNO, ENAME
                                   FROM EMP_BAK
                                   WHERE EMPNO IS NOT NULL
                                   AND    ENAME IS NOT NULL)
 
Execution Plan
-----------------------------------------------------------------
SELECT STATEMENT Optimizer=CHOOSE
    HASH JOIN(ANTI)
      TABLE ACCESS (FULL) OF EMP
 TABLE ACCESS (FULL) OF EMP_BAK
 
 
============================================================
[아래의 예는 실행계획 SQL 연산(HASH ANTI-JOIN) 강좌의 일부 내용 입니다]
=============================================================
 
아래의 Query는 동일한 의미를 가지는 질의 입니다확인해 보세요~

SQL> SELECT EMPNO,
              
 ENAME,               SAL      FROM   EMP      WHERE  (EMPNO, ENAME, SAL) NOT IN (SELECT EMPNO,                                                         ENAME,                                                         SAL                                                FROM   EMP_OLD);

Execution Plan
--------------------------------------------------------
0
  
 SELECT STATEMENT Optimizer=CHOOSE
1
   0  
 FILTER
2
   1     TABLE ACCESS (FULL) OF EMP

3
   1     TABLE ACCESS (FULL) OF EMP_OLD


SQL> SELECT EMPNO,
               ENAME,               SAL      FROM   EMP E      WHERE  NOT EXISTS (SELECT 1
FROM EMP_OLD EO
WHERE
  
EO.EMPNO = E.EMPNO
AND
    
 EO.ENAME = E.ENAME
AND
     EO.SAL    
= E.SAL);

Execution Plan
-------------------------------------------------------------
SELECT STATEMENT Optimizer=CHOOSE
1
   0  
 FILTER
2
   1     TABLE ACCESS (FULL) OF EMP

3
   1     TABLE ACCESS (FULL) OF EMP_OLD


SQL> SELECT EMPNO, ENAME, SAL
  FROM   EMP      MINUS
SELECT EMPNO, ENAME, SAL
  FROM  
 EMP_OLD


Execution Plan
-------------------------------------------------------------
0
  
 SELECT STATEMENT Optimizer=CHOOSE
1
   0  
 MINUS
2
   1    
 SORT (UNIQUE)
3
   2        TABLE ACCESS (FULL) OF EMP

4
   1     SORT (UNIQUE)
5
   2        TABLE ACCESS (FULL) OF EMP_OLD


위의 세 Query HASH ANTI JOIN으로 풀 수 있는 것은 NOT IN을 포함하고 있는 첫번째 질의 입니다. NOT IN의 비교 대상이 되는 컬럼은 NOT NULL로 서브쿼리까지 명시해 주어야 합니다물론 HASH_AJ 라는 힌트 구문도 사용해야 하구요~


SQL> SELECT EMPNO,
              
 ENAME,               SAL      FROM   EMP      WHERE  EMPNO IS NOT NULL      AND     ENAME IS NOT NULL      AND     SAL    IS NOT NULL
AND
    
(EMPNO, ENAME, SAL)
NOT IN (SELECT
EMPNO,
                                    
ENAME,                                    SAL                            FROM  EMP_OLD
WHERE
  
EMPNO IS NOT NULL      AND     ENAME IS NOT NULL      AND     SAL    IS NOT NULL);

Execution Plan
--------------------------------------------------------
0
  
 SELECT STATEMENT Optimizer=CHOOSE
1
   0  
 HASH JOIN(ANTI)
2
   1     TABLE ACCESS (FULL) OF EMP

3
   1     TABLE ACCESS (FULL) OF EMP_OLD


HSH ANTI JOIN
으로 풀 경우 성능이 향상되므로 위 문장과 같이 한 테이블에 존재하지 않는 로우만 추출하는 경우엔 HASH ANTI JOIN  되도록 힌트를 사용하는 것이 유리합니다.
 
 
 
[실습]
 
-      실습을 위한 예제 테이블 및 데이터는 아래 링크에서 확인 바랍니다.
 
myemp1 : 1000만건
myemp1_old : 100만건
mydept : 5
 
테스트환경 : oracle 11g
 
 
 
 
 
 
아래 세개 SQL문장은 같은 결과를 만들어 내는 동일한 SQL문이다.
 
인덱스 먼저 만들자.
 
SQL>create index idx_myemp1_old_ename_sal on myemp1_old(ename, sal)
SQL>create index idx_myemp1_ename_sal on myemp1(ename, sal)
 
먼저 not in을 사용하여 질의해 보자. (엄청 느리다 인덱스 있어도 느리지만 정말 느리다)
 
첫번째 NOT IN을 이용한 방법
 
SQL> select empno,
  2         ename
  3  from   myemp1 e1
  4  where  (ename, sal) not in (select ename, sal
  5                                from myemp1_old e2
  6                               where e1.ename = e2.ename
  7                                 and e1.sal = e2.sal);
 
9000001 개의 행이 선택되었습니다.
 
   : 00:01:35.99
 
-----------------------------------------------------------------------------------------------
| Id  | Operation          | Name                     | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |                          |    10M|   219M|    30M  (1)|100:03:28 |
|*  1 |  FILTER            |                          |       |       |            |          |
|   2 |   TABLE ACCESS FULL| MYEMP1                   |    10M|   219M| 16971   (1)| 00:03:24 |
|*  3 |   INDEX RANGE SCAN | IDX_MYEMP1_OLD_ENAME_SAL |     1 |    18 |     3   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------
 
 
두번째 NOT EXISTS를 이용한 방법
 
 
SQL> select empno,
  2         ename
  3  from   myemp1 e1
  4  where  not exists (select 1
  5                       from myemp1_old e2
  6                      where e1.ename = e2.ename
  7                        and e1.sal = e2.sal);
 
9000001 개의 행이 선택되었습니다.
 
   : 00:00:52.96
-------------------------------------------------------------------------------------------------| Id  | Operation             | Name                     | Rows  | Bytes |TempSpc| Cost (%CPU)| Time  |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |                          |  9000K|   351M|       | 36134   (1)| 00:07:1
|*  1 |  HASH JOIN RIGHT ANTI |                          |  9000K|   351M|    28M| 36134   (1)| 00:07:1
|   2 |   INDEX FAST FULL SCAN| IDX_MYEMP1_OLD_ENAME_SAL |   999K|    17M|       |  1100   (1)| 00:00:1
|   3 |   TABLE ACCESS FULL   | MYEMP1                   |    10M|   219M|       | 16961   (1)| 00:03:2
-------------------------------------------------------------------------------------------------
 
 
세번째 MINUS를 이용한 방법
 
 
SQL> select ename, sal from myemp1
  2  minus
  3  select ename, sal from myemp1_old;
 
9000001 개의 행이 선택되었습니다.
 
   : 00:01:22.86
 
------------------------------------------------------------------------------------------
| Id  | Operation           | Name       | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |            |    10M|   188M|       | 81447  (10)| 00:16:18 |
|   1 |  MINUS              |            |       |       |       |            |          |
|   2 |   SORT UNIQUE       |            |    10M|   171M|   268M| 74150   (1)| 00:14:50 |
|   3 |    TABLE ACCESS FULL| MYEMP1     |    10M|   171M|       | 16961   (1)| 00:03:24 |
|   4 |   SORT UNIQUE       |            |   999K|    17M|    26M|  7297   (1)| 00:01:28 |
|   5 |    TABLE ACCESS FULL| MYEMP1_OLD |   999K|    17M|       |  1582   (1)| 00:00:19 |
------------------------------------------------------------------------------------------
 
 
세가지 방법 중에서는 두번째 NOT EXISTS가 가장 빠른 결과를 보여준다아마도myemp1_old ename, sal 인덱스 덕분인 것 같다.
 
이번에는 첫번째 not in 예문을 해시 안티 조인(hash_aj)로 바꾸어 보자안쪽과 바깥쪽where절에 비교 대상 컬럼에 대해 is not null 비교를 해야 한다.
 
조금 빨라졌다.
 
SQL> select empno,
  2         ename
  3  from   myemp1 e1
  4  where  (ename, sal) not in (select
  5                                     ename, sal
  6                                from myemp1_old e2
  7                               where e1.ename = e2.ename
  8                                 and e1.sal = e2.sal
  9                                 and ename is not null
 10                                 and sal   is not null)
 11  and    ename is not null
 12  and    sal   is not null;
 
9000001 개의 행이 선택되었습니다.
 
   : 00:00:53.32
 
-------------------------------------------------------------------------------------------------
| Id  | Operation             | Name                     | Rows  | Bytes |TempSpc| Cost (%CPU)| Time  |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |                          |  9000K|   351M|       | 36156   (1)| 00:07:14 |
|*  1 |  HASH JOIN RIGHT ANTI |                          |  9000K|   351M|    28M| 36156   (1)| 00:07:14 |
|*  2 |   INDEX FAST FULL SCAN| IDX_MYEMP1_OLD_ENAME_SAL |   999K|    17M|       |  1102   (1)| 00:00:14
|*  3 |   TABLE ACCESS FULL   | MYEMP1                   |    10M|   219M|       | 16981   (1)| 00:03:24 |
-------------------------------------------------------------------------------------------------
 
이번에는 merge anti join(merge_aj)로 바꾸어서 실행 해보자.
 
해시 안티 조인 보다는 조금 느리다.
 
SQL> select empno,
  2         ename
  3  from   myemp1 e1
  4  where  (ename, sal) not in (select
  5                                     ename, sal
  6                                from myemp1_old e2
  7                               where e1.ename = e2.ename
  8                                 and e1.sal = e2.sal
  9                                 and ename is not null
 10                                 and sal   is not null)
 11  and    ename is not null
 12  and    sal   is not null;
 
9000001 개의 행이 선택되었습니다.
 
   : 00:01:15.66
 
-------------------------------------------------------------------------------------------------
| Id  | Operation              | Name                     | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |                          |  9000K|   351M|       | 92322   (1)| 00:18:28 |
|   1 |  MERGE JOIN ANTI       |                          |  9000K|   351M|       | 92322   (1)| 00:18:28 |
|   2 |   SORT JOIN            |                          |    10M|   219M|   691M| 85505   (1)| 00:17:07 |
|*  3 |    TABLE ACCESS FULL   | MYEMP1                   |    10M|   219M|       | 16981   (1)| 00:03:24 |
|*  4 |   SORT UNIQUE          |                          |   999K|    17M|    53M|  6817   (1)| 00:01:22 |
|*  5 |    INDEX FAST FULL SCAN| IDX_MYEMP1_OLD_ENAME_SAL |   999K|    17M|       |  1102   (1)| 00:00:14