레이블이 오라클학원인 게시물을 표시합니다. 모든 게시물 표시
레이블이 오라클학원인 게시물을 표시합니다. 모든 게시물 표시

2013년 11월 3일 일요일

스프링,아이바티스트랜잭션예제[Spring Framework3.X,Transaction,iBATIS, @Transactional]

스프링,아이바티스트랜잭션예제[Spring Framework3.X,Transaction,iBATIS, @Transactional]
 
스프링의 트랜잭션 관리 방법중 @Transactional 애노테이션을 이용하여 iBATIS와 연동하는 간단 예제이다.(오라클 emp 테이블에 Data 1건 insert..)

먼저 Spring MVC 프로젝트 하나 생성하자.
 
1. pom.xml
 
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.mungchung</groupId>
 <artifactId>sample</artifactId>
 <name>abc</name>
 <packaging>war</packaging>
 <version>1.0.0-BUILD-SNAPSHOT</version>
 <properties>
  <java-version>1.6</java-version>
  <org.springframework-version>3.0.6.RELEASE</org.springframework-version>
  <org.aspectj-version>1.6.9</org.aspectj-version>
  <org.slf4j-version>1.5.10</org.slf4j-version>
 </properties>
 <dependencies>
 
  <!-- Spring -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>${org.springframework-version}</version>
   <exclusions>
    <!-- Exclude Commons Logging in favor of SLF4j -->
    <exclusion>
     <groupId>commons-logging</groupId>
     <artifactId>commons-logging</artifactId>
    </exclusion>
   </exclusions>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>${org.springframework-version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-tx</artifactId>
   <version>${org.springframework-version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-jdbc</artifactId>
   <version>${org.springframework-version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-orm</artifactId>
   <version>${org.springframework-version}</version>
  </dependency>
  <dependency>
   <groupId>org.apache.ibatis</groupId>
   <artifactId>ibatis-sqlmap</artifactId>
   <version>2.3.4.726</version>
  </dependency>
  <dependency>
   <groupId>commons-dbcp</groupId>
   <artifactId>commons-dbcp</artifactId>
   <version>1.2.2</version>
  </dependency>
  <dependency>
   <groupId>xerces</groupId>
   <artifactId>xercesImpl</artifactId>
   <version>2.9.1</version>
  </dependency>
  <dependency>
   <groupId>cglib</groupId>
   <artifactId>cglib</artifactId>
   <version>2.2</version>
   <type>jar</type>
   <scope>compile</scope>
  </dependency>
  <!-- AspectJ -->
  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjrt</artifactId>
   <version>${org.aspectj-version}</version>
  </dependency>
  <!-- Logging -->
  <dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-api</artifactId>
   <version>${org.slf4j-version}</version>
  </dependency>
  <dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>jcl-over-slf4j</artifactId>
   <version>${org.slf4j-version}</version>
   <scope>runtime</scope>
  </dependency>
  <dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-log4j12</artifactId>
   <version>${org.slf4j-version}</version>
   <scope>runtime</scope>
  </dependency>
  <dependency>
   <groupId>log4j</groupId>
   <artifactId>log4j</artifactId>
   <version>1.2.16</version>
   <exclusions>
    <exclusion>
     <groupId>javax.mail</groupId>
     <artifactId>mail</artifactId>
    </exclusion>
    <exclusion>
     <groupId>javax.jms</groupId>
     <artifactId>jms</artifactId>
    </exclusion>
    <exclusion>
     <groupId>com.sun.jdmk</groupId>
     <artifactId>jmxtools</artifactId>
    </exclusion>
    <exclusion>
     <groupId>com.sun.jmx</groupId>
     <artifactId>jmxri</artifactId>
    </exclusion>
   </exclusions>
   <scope>runtime</scope>
  </dependency>
  <!-- @Inject -->
  <dependency>
   <groupId>javax.inject</groupId>
   <artifactId>javax.inject</artifactId>
   <version>1</version>
  </dependency>
  <!-- Servlet -->
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>servlet-api</artifactId>
   <version>2.5</version>
   <scope>provided</scope>
  </dependency>
  <dependency>
   <groupId>javax.servlet.jsp</groupId>
   <artifactId>jsp-api</artifactId>
   <version>2.1</version>
   <scope>provided</scope>
  </dependency>
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>jstl</artifactId>
   <version>1.2</version>
  </dependency>
  <!-- Test -->
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.7</version>
   <scope>test</scope>
  </dependency>
 </dependencies>
 <build>
  <plugins>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
     <source>${java-version}</source>
     <target>${java-version}</target>
    </configuration>
   </plugin>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <configuration>
     <warName>abc</warName>
    </configuration>
   </plugin>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
     <execution>
      <id>install</id>
      <phase>install</phase>
      <goals>
       <goal>sources</goal>
      </goals>
     </execution>
    </executions>
   </plugin>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-resources-plugin</artifactId>
    <version>2.5</version>
    <configuration>
     <encoding>UTF-8</encoding>
    </configuration>
   </plugin>
  </plugins>
 </build>
</project>
 

2. 컨트롤러
 
package onj.edu.transaction;
import java.util.Locale;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HomeController {
 @Autowired
 private TransactionMain transactionMain;

 @RequestMapping(value = "/hello", method = RequestMethod.GET)
 public String home(Locale locale, Model model) {
  String msg = "";
  try {
   msg = transactionMain.insert();
  } catch (Throwable e) {
   msg = "Transaction 오류";
   e.printStackTrace();
  }
  model.addAttribute("msg", msg );
  return "onj";
 }
}
 
3. 트랜잭션 메인 클래스
 
package onj.edu.transaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
public class TransactionMain {
 @Autowired
 private Tran1 tr1;

 @Transactional(propagation = Propagation.REQUIRED)
 public String insert() throws Throwable {
  tr1.insertTest();
 
  return "Transaction Success!!";
 }
}

4. 트랜잭션 처리 클래스(간단히 EMP 테이블에 한건 인서트)

package onj.edu.transaction;
import java.util.HashMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.ibatis.SqlMapClientTemplate;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
public class Tran1 {
 @Autowired
 private SqlMapClientTemplate sqlMapClientTemplate;

 @Transactional(propagation = Propagation.REQUIRED)
 public void insertTest() {
  HashMap<String, String> hashMap = new HashMap<String, String>();
  hashMap.put("empno", "101");
  hashMap.put("ename", "오엔제이");
  sqlMapClientTemplate.insert("sql.empinsert1", hashMap);
 }
}
 
5. web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/spring/root-context.xml</param-value>
 </context-param>


 <listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>
 <servlet>
  <servlet-name>onjServlet</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>/WEB-INF/spring/onjServlet-context.xml</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
 </servlet>
 
 <servlet-mapping>
  <servlet-name>onjServlet</servlet-name>
  <url-pattern>*.html</url-pattern>
 </servlet-mapping>
</web-app>
 
6. /spring/root-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:tx="http://www.springframework.org/schema/tx"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

 <tx:annotation-driven proxy-target-class="true"/>

 <context:component-scan base-package="onj.edu.transaction">
  <context:exclude-filter type="annotation" __EXPRESSION__="org.springframework.stereotype.Controller" />
 </context:component-scan>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
  destroy-method="close">
  <property name="driverClassName">
   <value>oracle.jdbc.driver.OracleDriver</value>
  </property>
  <property name="url">
   <value>jdbc:oracle:thin:@192.168.0.7:1521:onj</value>
  </property>
  <property name="username">
   <value>scott</value>
  </property>
  <property name="password">
   <value>tiger</value>
  </property>
 </bean>
   
    <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
        <property name="configLocation" value="classpath:/sql-map-config.xml"/>
        <property name="dataSource" ref="dataSource"/>
    </bean>
   
    <bean id="sqlMapClientTemplate" class="org.springframework.orm.ibatis.SqlMapClientTemplate">
        <property name="sqlMapClient" ref="sqlMapClient"/>
    </bean>
 <bean id="transactionManager"
  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource" />
 </bean>
 <bean id="tr1" class="onj.edu.transaction.Tran1"/>
 <bean id="trMain" class="onj.edu.transaction.TransactionMain"/>
</beans>

7. /spring/onjServlet-context.xml

 <!-- Enables the Spring MVC @Controller programming model -->
 <annotation-driven/>
 <tx:annotation-driven proxy-target-class="true"/>
 <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
 <resources mapping="/resources/**" location="/resources/" />
 <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
 <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <beans:property name="prefix" value="/views/" />
  <beans:property name="suffix" value=".jsp" />
 </beans:bean>

 <context:component-scan base-package="onj.edu.transaction"/>

</beans:beans>
 
8. src/main/resources/sql-map-config.xml
 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
  "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
    <settings useStatementNamespaces="true"/>   
    <sqlMap resource="sqlmap/sql.xml"/>
</sqlMapConfig>

9.  SLQ매퍼, src/main/resources/sqlmap/sql.xml
 
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE sqlMap PUBLIC "-//iBATIS.com/DTD SQL Map 2.0//EN" "http://www.ibatis.com/dtd/sql-map-2.dtd">
<sqlMap namespace="sql">
 <insert id="empinsert1" parameterClass="hashMap">
  INSERT INTO emp (empno, ename) VALUES (#empno#, #ename#)
 </insert>
</sqlMap>

10.마지막으로 view 역할을 하는 jsp(/views/onj.jsp)
 
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<head>
 <title>Home</title>
</head>
<body>
//트랜잭션처리결과 출력
<P> ${msg} </P>
</body>
</html>

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월 16일 금요일

오라클 Optimizer Mode Setting 방법

오라클 Optimizer Mode Setting 방법

대한민국오라클학원/오라클학원/오라클교육/오라클강의/구로 오라클/가산오라클/오라클강


--------------------------
1.        Instance 수준의 셋팅 방법
--------------------------


오라클자바커뮤니티에서 설립한  개발자중심! 오엔제이프로그래밍 실무교육센터
(신입사원채용무료교육, 오라클, SQL, 튜닝, 자바, 스프링, Ajax, jQuery, 안드로이드, 아이폰, 닷넷, C#, ASP.Net)   www.onjprogramming.co.kr 


-        DB의 설정 파일(initSID.ora or spfileSID.ora)에 전체적으로 적용이 되도록 정의하는 방법이며 다음과 같이 기술하며 OPTIMIZER MODE는 REUL, CHOOSE, ALL_ROOWS, FIRST_ROWS와 같은 종류가 있습니다. CHOOSE인 경우 한테이블이라도 Analyzed되어 있는 경우엔 비용기반 접근 방식을 이용하는 것이며 RULE인 경우 규칙기반 접근 방식을 사용, ALL_ROWS인 경우 비용기반 옵티마이저의 한 방법이며 모든 ROW들을 처리한다고 할 때 그 비용을 최소화 하는 방법으로 실행계획을 수립하며,  FIRST_ROWS인 경우엔 최초 ROW를 추출하는데 드는 비용을 최소화 하도록 실행 계획을 구성하는 것입니다.

-        예) OPTIMIZER_MODE=FIRST_ROWS

-        만약 initSID or spfileSID.ora에 아무 내용도 정의하지 않은 경우 기본적으로 CHOOSE 방식이 됩니다.

-------------------------
2.        Session 수준의 셋팅 방법
-------------------------

-        alter session이라는 명령을 이용하면 현재 접속된 세션 레벨에서 옵티마이저 모드를 정의할 수 있습니다.

-        예) alter session set optimizer_goal=rule 앞과 같이 정의하면 해당 세션이 끝나기 전까지는 규칙 기반(,RULE-BASED) 옵티마이저 모드를 이용하게 됩니다.

--------------------------
3.        Statement 수준의 셋팅 방법
--------------------------

-        힌트(Hint) 구문을 이용한다면 매 SQL 문장마다 서로 다른 옵티마이저 모드를 적용할 수 있습니다.

-        예) SELECT /*+ FIRST_ROWS */
                          ENAME,
                          SAL,
                          JOB
              FROM EMP
              WHERE SAL > (SELECT MAX(SAL)
                                    FROM  EMP
                                    WHERE DEPTNO = 10)

2013년 8월 8일 목요일

[jquery mouse event, 오라클자바교육강좌]click, dblclick, mousedown, mouseup, mouseenter

제이쿼리마우스 이벤트 입니다. 참고하세요
click, dblclick, mousedown, mouseup, mouseenter(경계외부에서 내부로 진입시),


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



 mouseleave(경계내부에서 외부로 이동시), mousemove, mouseout, mouseover

<style>
.outer {width:200px; height:200px; background:red; padding:50px; margin:10px;}
.inner {width:100%; height:100%; background:Yellow;}
</style>
<script src="/ajaxjquery/jquery-1.10.2.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
  
$(".outer").mouseover(function() {
//inner에 갔다오는 경우, 외부에서 진입시
$("body").append("<h1>MOUSE OVER</h1>");
}).mouseenter(function() {
//외부에서 집입시
$("body").append("<h1>MOUSE ENTER</h1>");
});
});
</script>
<body>   
<div class="outer">
<div class="inner"></div></div></body>
 
 

[ORACLEJAVA커뮤티니, oraclejavanew.kr]Struts Bean 커스텀 태그

Struts Bean 커스텀 태그

자바 빈과 관련된 프로퍼티에 접근 하는데 이용되며 페이지 scope 속성들과 변수의 기술을 통해 페이지의 나머지 부분에서 쉽게 접근 할 수 있도록 자바 빈을 정의하는데 사용 됩니다.

또한 Bean 태그 라이브러리의 태그들에서는 요청 쿠키, 헤더 그리고 파라미터 값을 기반으로 하는 새로운 빈을 생성 해야 하는 경우 편리하게 이용 할 수 있는 메커니즘도 제공 합니다.

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


Custom tags within the Bean tag library

cookie : 지정한 요청 쿠키의 값에 근거해 변수를 정의
define : 지정한 빈 프로퍼티에 값에 근거해 변수를 정의
header : 지정한 요청 헤더의 값에 근거해 변수를 정의
include : 동적인 애플리케이션 요청의 응답을 로드해 빈으로 이용 할 수 있게 함
message  : 국제화된 메시지를 표시
page : 지정한 아이템을 빈으로써 페이지 문맥에서 꺼냄
parameter : 지정한 요청 파라미터에 근거해 변수를 정의
resource : 웹애플리케이션의 자원을 로드해 빈으로 이용 할 있게 함
size : Collection 또는 Map 요소의 개수를 포함한 빈을 정의
struts : 지정한 스트럿츠 내부 설정 객체를 빈으로
write : 지정한 빈 프로퍼티의 값을 표시


1.define 태그

지정한 빈의 프로퍼티 값을 꺼내 현재 페이지의 나머지 부분에서 접근 할  수 있도록 정의 합니다.반환되는 프로퍼티 값이 자바 원시 데이터 타입인 경우를 제외하고는 형 변환이 일어 나지 않습니다. 단 자바 원시데이터 타입인 경우엔 이 원시 데이터 타입의 래퍼형으로 변환 됩니다. (int 형이면 Integer로…)


[예:게시판의 VIEW 구현 시 이용]

<td width=490 style= "padding-left:10 ">
<bean:define id="content” name="boardForm" property="boardValue.content"/ >
<%= BoardUtils.convertHtmlBr((String)content) %>
< / t d >

2.header 태그

name으로 지정된 요청 헤더 값을 꺼내 String 타입의 페이지 scope 속성으로 정의

multiple 속성에 null이 아닌 값을 설정 한 경우 id 속성은 HttpServletRequest.getHeader() 대신 HttpServletRequest.getHeaders()를 호출하여 얻은 결과값을 포함 합니다.

[예제: 아래 예제는 요청 헤더와 헤더 값을 출력 합니다.]

<%@ page pageEncoding="euc-kr" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<html>
<body>
<%
java.util.Enumeration names =
((HttpServletRequest) request).getHeaderNames();

while (names.hasMoreElements()) {
String name = (String) names.nextElement();
%>

<bean:header id="head" name="<%= name %>"/>
 <br>Header Name : <%= name %> = <%= head %>
 
<%
}
%>
</body>
</html>

[결과]

Header Name : accept = */*
Header Name : accept-language = ko,en;q=0.5
Header Name : accept-encoding = gzip, deflate
Header Name : user-agent = Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)
Header Name : host = localhost
Header Name : connection = Keep-Alive
Header Name : cookie = JSESSIONID=EF8A4D1130ADFD06F6E78BE2A7BA9801

3.include 태그

지정한 애플리케이션 컴포넌트(또는 외부의URL)에 내부적으로 요청을 보내 수행 하고 이때 얻은 응답을 String 타입의 빈으로 만듭니다. 이때 생성된 빈은 id 속성의 값을 변수 명으로 가지며 페이지 scope에 저장 됩니다.

이 태그는 <jsp:include>와 비슷합니다. 그러나 표준 <jsp:include>에서는 해당 컴포넌트를 수행해서 얻은 응답을 직접 출력 스트림에 쓰는 반면 Bean 태그라이브러리의 include 태그에서는 페이지 범위를 가지는 빈으로 저장하는 것이 차이가 있습니다.

아래 처럼 include 태그를 이용해서 페이지 범위의 빈에 저장

<bean:include id="footerSpacer" page="/long/path/footerSpacer.jsp"/>

이제 페이지의 적절한 곳에서 <bean:write>를  이용하여 출력 할 수 있습니다.

<bean:write name="footerSpacer" />

[예제-test.jsp]
<%@page pageEncoding="euc-kr" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<html>
<body>
<bean:include id="inc" page="/include.jsp"/>
<br>
원래 페이지의 내용...<br>
<bean:write name="inc"/>
<br>========================<br>
<bean:write name="inc"/>
</body>
</html>

[include.jsp]
include되는 페이지의 출력...


4.message 태그

스트럿츠 태그 라이브러리에서 가장 널리 이용되는 태그 중 하나이며 지정된 메시지 키를 이용하여 국제화된 메시지를 꺼내와 출력 스트림에 출력 합니다. “{0}”와  같은 파라미터 치환을 5개까지 허용 합니다.

메시지를 꺼내오기 위해서는 key 속성을 사용하여 직접 지정 할 수도 있으며 name과 property 속성을 사용하여 빈에서 간접적으로 꺼내 올 수도 있습니다. 그리고 bundle 속성에는 MessageResources 객체를 얻어 올 수 있는 애플리케이션 범위의 빈의 이름을 지정 할 수도 있습니다. Locale 속성을 지정하지 않은 경우 필요한 locale은 세션에서 키 Action.LOCALE_KEY를 사용하여 얻습니다.

아래는 간단한 예 입니다.

<head>
<html:base/>
<title><bean:message key="title.login"/></title>
    </head>

5.parameter 태그

지정된 요청 파라미터의 값을 꺼내 String 타입의 페이지 범위의 속성으로 정의 합니다. 만약 multiple 속성에 null 이 아닌 값이 설정 될 경우에는 getParamter() 대신 getParameters() 메소드를 호출하고 String[] 타입의 페이지 범위 속성을 정의 합니다. 


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


<bean:parameter id="param1" name="param1"/>
<bean:parameter id="param2" name="param2" multiple="true"/>
<bean:parameter id="param3" name="param3" value="UNKNOWN VALUE"/>


6.resource 태그

지정된 웹 에플리케이션 자원의 값을 꺼내 input 속성의 값에 따라 InputStream이나 String 타입의 한 형태로 사용 할 수 있게 합니다. input 속성의 값이 null이 아닌 값인 경우 InputStream을 생성하며 그 외에는 String으로 로딩 합니다.

<bean:resource id="webxml" name="/WEB-INF/web.xml"/>

얻어진 정보를 JSP 페이지에서 보기 위해서는 아래처럼 하면 됩니다.

<pre>
<bean:write name="webxml" filter=”true” />
</pre>

[예제]
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<html>
<body>
<bean:resource id="webxml" name="/WEB-INF/web.xml"/>
<pre>
<bean:write name="webxml" filter=”true” />
</pre>
</body>
</html>

7.write 태그

빈 프로퍼티의 값을 꺼내 String 형태로 출력하는 태그로써 자주 사용되는 것입니다. 즉 자바 빈에서 속성 값을 get 하는 것 입니다. 만약 filter 속성이 “true” 이면 “<” 인 경우 <로 변환 됩니다. 

2013년 8월 3일 토요일

[Oracle SQL Hint]오라클힌트-실행계획 SQL연산(MERGE SEMI JOIN), ORACLE HINT

실행계획 SQL연산(MERGE SEMI JOIN)

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

SEMI JOIN은 첫 번째 매칭되는 value를 찾기만 하면 결과를 돌려주는 join 문입니다.

MERGE SEMI JOIN HASH SEMI JOIN으로 나타나는데 EXISTS와 같은 Query에서 서브 쿼리 조건에 대한 인덱스가 존재하지 않으면 비효율적인 실행 계획이 세워지게 되는데 이러한 경우 세미 조인을 적절히 이용하면 됩니다.

아래 예문을 따라 해 보면서 각각의 경우 실행계획 및 수행 시간을 유심히 보시기 바랍니다. 기본적으로 오라클 11g에서 MERGE 보다는 HASH 조인을 우선적으로 선택 함을 할 수 있습니다.

SQL> select index_name, table_name from user_indexes
  2  where table_name like 'EMPTEST'
  3  /

INDEX_NAME                     TABLE_NAME
------------------------------ ------------------------------
IDX_EMPTEST_ADDR               EMPTEST
IDX_EMPTEST_DEPTNO             EMPTEST



실습을 위해 DEPTNO 컬럼의 인덱스를 숨기자
(오라클 옵티마이저가 사용하지 않도록 )

SQL> alter index IDX_EMPTEST_DEPTNO invisible;
  인덱스가 변경되었습니다.

-- CBO로 동작하는 경우 서브조인에서 exists등이 인덱스가 없는 경우라면 어떻게 동작하는지 보자

SQL> select  count(e.ename)
  2  from emptest e
  3  where exists (select  1
  4                  from depttest d
  5                 where e.deptno = d.deptno);

COUNT(E.ENAME)
--------------
       2500000

   : 00:00:02.34
--------------------------------------------------------------------

|   0 | SELECT STATEMENT
|   1 |  SORT AGGREGATE      
|*  2 |   HASH JOIN RIGHT SEMI|
|   3 |    TABLE ACCESS FULL  | DEPTTEST
|   4 |    TABLE ACCESS FULL  | EMPTEST


-- 기본적으로 전체 테이블 FULL SCAN HASH SEMI 조인으로 수행된다.
-- 이번엔 RULE 힌트를 넣어 RBO로 동작하도록 하고 결과를 보자.

SQL> select  count(e.ename)
  2  from emptest e
  3  where exists (select 1
  4                  from depttest d
  5                 where e.deptno = d.deptno);

COUNT(E.ENAME)
--------------
       2500000

Execution Plan
----------------------------------------------------------
|   0 | SELECT STATEMENT   
|   1 |  SORT AGGREGATE    
|*  2 |   FILTER            
|   3 |    TABLE ACCESS FULL| EMPTEST 
|*  4 |    TABLE ACCESS FULL| DEPTTEST |


이번에는 HASH SEMI JOIN 힌트 구문을 이용해 보자.
당연히 해시 세미조인을 이용할 것이다.

SQL> select  count(e.ename)
  2  from emptest e
  3  where exists (select  1
  4                  from depttest d
  5                 where e.deptno = d.deptno);

COUNT(E.ENAME)
--------------
       2500000

   : 00:00:02.32

Execution Plan
--------------------------------------------------------------------
|   0 | SELECT STATEMENT      |          |     1 |    20 |  4172   (2)|
|   1 |  SORT AGGREGATE       |          |     1 |    20 |            |
|*  2 |   HASH JOIN RIGHT SEMI|          |  2500K|    47M|  4172   (2)|
|   3 |    TABLE ACCESS FULL  | DEPTTEST |     5 |    15 |     3   (0)|
|   4 |    TABLE ACCESS FULL  | EMPTEST  |  2500K|    40M|  4156   (1)|


이번엔 MERGR SEMI JOIN으로 해시 세미 조인 보다는 시간이 좀 더 걸린다.


SQL> select  count(e.ename)
  2  from emptest e
  3  where exists (select 1
  4                  from depttest d
  5                 where e.deptno = d.deptno);

COUNT(E.ENAME)
--------------
       2500000

   : 00:00:03.50

Execution Plan
----------------------------------------------------------
|   0 | SELECT STATEMENT     |          |     1 |    20 |       | 17977  
|   1 |  SORT AGGREGATE      |          |     1 |    20 |       |            |
|   2 |   MERGE JOIN SEMI    |          |  2500K|    47M|       | 17977  
|   3 |    SORT JOIN         |          |  2500K|    40M|   134M| 17973  
|   4 |     TABLE ACCESS FULL| EMPTEST  |  2500K|    40M|       |  4156  
|*  5 |    SORT UNIQUE       |          |     5 |    15 |       |     4  (25)| 0
|   6 |     TABLE ACCESS FULL| DEPTTEST |     5 |    15 |       |     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