레이블이 오라클강좌오라클자바인 게시물을 표시합니다. 모든 게시물 표시
레이블이 오라클강좌오라클자바인 게시물을 표시합니다. 모든 게시물 표시

2013년 10월 23일 수요일

오라클 SQL 튜닝의 도구 – SQL*TRACE와 TKPROF [ORACLE강좌]

오라클  SQL 튜닝의 도구 – SQL*TRACE와 TKPROF [ORACLE강좌]

Oracle의 SQL*TRACE는 사용자가 실행 한 SQL문에 대해 구문분석(Parsing), 실행(execute), 추출(fetch) 부분으로 나누어 각 단계에서 걸리는 Overhead와 시간 등의 통계 정보를 일정한 형태로 저장 합니다. EXPALIN PLAN에서 제공하는 정보와 더블어 CPU/IO의 필요량, 실행계획의 각 단계에서의 레코드 개수등의 정보도 확인 가능 합니다. EXPLAIN PLAN 명령어와 함께 자주 사용되는 튜닝의 도구 입니다. 

SQL*TRACE나 TKPROF를 실행 했을 때의 결과는 이해하기가 쉽지 않지만 강력한 튜닝의 도구 입니다. SQL*TRACE에 의해 분석되는 결과는 바이너리 형태로 운영체제의 파일 시스템에 생성 됩니다. 물론 바이너리 이므로 결과를 직접 눈으로 보면 이해가 되지 않지만 TKPROF 유틸리티를 이용하여 텍스트 파일 형태로 변환 시켜 확인이 가능 합니다. 

SQL*TRACE의 결과는 데이터베이스 전체 또는 특정 세션에 대해 적용 할 수 있는데 데이터베이스 전체에 트레이스를 적용하면 실제 Application 수행에 추가적인 부하를 가져오므로 특별한 경우를 제외하고 전체 데이터베이스 시스템에 TRACE를 거는 것은 삼가 해야 합니다. 대부분은 특정 세션에 대해서만 부분적으로 활성화 하여 사용 합니다. 


SQL*TRACE의 사용 

SQL TRACE를 사용하기 전에 몇 가지 설정이 필요한데 먼저 초기파일에서 USER_DUMP_DEST 파라미터를 확인해야 합니다. 이 매개변수는 TRACE를 실행 할 때 생성되는 파일의 위치를 설정 하는 것입니다. 또한 시간 정보를 TRACE 항목에 추가할려면 TIMED_STATISTICS 항목을 TRUE로 해야 하거나 SQL*Plus등에서는 alter session set timed_statistics=true 라고 해주어야 합니다. 아래에 자세히 확인 하도록 합니다. 

TIMED_STATISTICS 

시간 통계 정보에 대해 수집여부를 결정, 기본값은 false 
세션레벨에서는 alter session set timed_statistics=true라고 하면 됩니다. 

MAX_DUMP_FILE_SIZE 

TRACE의 결과로 생기는 바이너리 파일의 최대 사이즈를 단위는 블록 입니다. 기본값은 500 블록 입니다. 또한 세션 레벨에서 다음과 같이 지정 가능 합니다. Alter session set max_dump_file_size = 800(800개의 시스템 블록) 

USER_DUMP_DEST 

TRACE의 결과로 생기는 바이너리 파일의 위치를 지정 합니다. 세션레벨에서는 alter session set  user_dump_dest = “C:\oracle\admin\wink\udump” 등으로 지정 합니다. 

위의 세개의 파라미터를 init.ora 파일에 지정하였다면 SQL*TRACE의 시작을 전체 데이터베이스에서 할건지 세션 레벨에서 할건지를 정할 수가 있습니다. 인스턴스 레벨에서 할려면 init.ora 파일에서 SQL_TRACE 항목을 TRUE로 설정하면 되구요 세션 레벨에서 할려면 alter session set sql_trace = true 라고 하면 됩니다. 

자 이제 실습을 위해 위의 3개의 매개변수를 init.ora 에 설정토록 합니다. 

MAX_DUMP_SIZE = 800 
TIMED_STATISTICS = TRUE 
USER_DUMP_DEST = C:\oracle\admin\wink\udump 

다음을 따라 하도록 합니다. 

SQL> conn / as sysdba 
연결되었습니다. 
SQL> shutdown immediate 
데이터베이스가 닫혔습니다. 
데이터베이스가 마운트 해제되었습니다. 
ORACLE 인스턴스가 종료되었습니다. 
SQL> startup open 
ORACLE 인스턴스가 시작되었습니다. 

Total System Global Area 135338868 bytes 
Fixed Size 453492 bytes 
Variable Size 109051904 bytes 
Database Buffers 25165824 bytes 
Redo Buffers 667648 bytes 
데이터베이스가 마운트되었습니다. 
데이터베이스가 열렸습니다. 

SQL>conn scott/tiger 

SQL> alter session set sql_trace=true; 

세션이 변경되었습니다. 

SQL> select job,avg(sal) from emp 
2 group by job 
3 having avg(sal) > (select avg(sal) from emp 
4 where job = 'SALESMAN'); 

JOB AVG(SAL) 
--------- ---------- 
ANALYST 3000 
MANAGER 2758.33333 
PRESIDENT 5000 

session에서 trace를 중지 

SQL> alter session set sql_trace=false; 

SQL*Plus를 종료하고 c:\oracle\admin\wink\udump에 가보면 trc 파일이 생겼을 것 
입니다. 저의 경우 DB SID가 wink이므로 wink_ora_3316.trc 와 같은 파일이 생겼습니다. 

TKPROF를 이용하여 TRACE파일을 텍스트 파일로 변경 하기 

TKPROF Utility를 이용하면 매우 유용한 분석 정보를 얻을 수 있습니다. 즉 TKPROF의 결과 파일은 트레이스가 실행되는 동안 프로세스에 의해 데이터베이스에서 실행된 작업에 대한 요약 정보 입니다. 

텍스트 파일의 내용을 보면 PARSE, EXECUTION, FETCH시 작업을 실행 한 횟수, CPU 사용 시간, 검색된 행이 무엇인지, SQL이 수행된 총 소요시간, DISK IO 블록 수, 조건을 만족하는 전체 행의 수, 수행된 SQL문이 사용한 SGA 영역의 크기, SQL문장의 실행 계획, 해당 세션에서 작업했던 전체 작업에 대한 CPU, 메모리, 블록의 크기 등의 정보를 확인 할 수 있습니다. 

SQL문을 해석하기 위해서는 아래의 단계가 필요 합니다. 

파싱(parse) 

SQL문을 실행 계획으로 번역 하는 것을 말합니다. 해당 SQL을 실행 하는데 필요한 적절한 권한, 컬럼이 있는지, 참조된 객체에 관한 확인 등의 작업이 이루어지게 됩니다. 

실행(execution) 

오라클에 의해 SQL문을 실제 실행 한 것을 말합니다. 

추출/인출(fetch) 

쿼리에 의해 추출된 레코드를 이여기 합니다. Select 문에서만 이용 됩니다. 


다음은 TKPROF의 통계 정보 컬럼 입니다. 

Count : 분석, 실행, 추출을 몇번 했는지를 나타 냅니다. 
CPU : 분석, 실행, 추출에 대한 CPU 처리 시간(CURSOR를 공유하면 분석단계의 처리 시간은 0 입니다.) 
Elapsed : 분석, 실행, 추출 처리 단계별로 처리된 소요 시간 
Disk : 테이블의 데이터를 읽기 위해 데이터 파일로부터 읽어 들인 블록 수 
Query : SELECT로 데이터를 읽어 올 때 이미 다른 사용자에 의해 같은 데이터가 사용 되었다면 그 블록에서 데이터를 가져옵니다. 
Current : 메모리에 저장된 데이터를 가지고 오기 위해 읽은 버퍼의 블록 수(update, insert, delete 후 select 했을 때) 

TKPROF를 실행하기 위한 문법 

Explain = 사용자계정/패스워드(명시된 사용자에 대해 EXPLAIN PLAN 실행) 
Print = n (트레이스 파일내의 분석된 SQL문의 수를 n 만큼만 제한할 때 이용) 
Record = 파일명(트레이스 파일내에 분석된 SQL문을 지정한 파일에 저장) 
Sort=option(트레이스 파일내에 분석된 SQL문을 지정한 옵션에 의해 정렬) 
Sys=[NO](트레이스 파일내에 생성된 SQL 문장 중에 오라클 서버가 내부적인 작업을 위해 실행한 SQL문장을 출력 시 포함 할건지를 결정) 
Table=스키마.테이블명(실행 계획을 지정한 테이블에 저장) 

이전의 SQL*TRACE에 의해 생긴 바이너리 파일을 TKPROF를 이용하여 분석을 해보도록 하겠습니다. 

명령프롬픝에서 다음과 같이 실행 합니다.(TRACE 파일이 만들어진 곳에서 실행) 

C:\oracle\admin\wink\udump>tkprof wink_ora_3316.trc sql1.tkp sys=no explain=scot 
t/tiger 

TKPROF: Release 9.2.0.1.0 - Production on 목 Dec 16 01:33:23 2004 

Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved. 

다음은 sql1.tkp 파일의 내용 입니다. 



TKPROF: Release 9.2.0.1.0 - Production on 목 Dec 16 01:33:23 2004 

Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved. 

Trace file: wink_ora_3316.trc 
Sort options: default 

******************************************************************************** 
count = number of times OCI procedure was executed 
cpu = cpu time in seconds executing 
elapsed = elapsed time in seconds executing 
disk = number of physical reads of buffers from disk 
query = number of buffers gotten for consistent read 
current = number of buffers gotten in current mode (usually for update) 
rows = number of rows processed by the fetch or execute call 
******************************************************************************** 

alter session set sql_trace=true 


call count cpu elapsed disk query current rows 
------- ------ -------- ---------- ---------- ---------- ---------- ---------- 
Parse 0 0.00 0.00 0 0 0 0 
Execute 1 0.00 0.00 0 0 0 0 
Fetch 0 0.00 0.00 0 0 0 0 
------- ------ -------- ---------- ---------- ---------- ---------- ---------- 
total 1 0.00 0.00 0 0 0 0 

Misses in library cache during parse: 0 
Optimizer goal: CHOOSE 
Parsing user id: 59 (SCOTT) 
******************************************************************************** 


아래는 사용자가 실행한 SQL 문장 입니다. 

select job,avg(sal) from emp 
group by job 
having avg(sal) > (select avg(sal) from emp 
where job = 'SALESMAN') 

call count cpu elapsed disk query current rows 
------- ------ -------- ---------- ---------- ---------- ---------- ---------- 
Parse 1 0.00 0.00 0 0 0 0 
Execute 1 0.00 0.00 0 0 0 0 
Fetch 2 0.00 0.00 0 6 0 3 
------- ------ -------- ---------- ---------- ---------- ---------- ---------- 
total 4 0.00 0.01 0 6 0 3 

Misses in library cache during parse: 1 이 값이 0이라는 의미는 실행한 SQL문이 이전에 실행 된적이 없었음을 나타 냅니다. 
Optimizer goal: CHOOSE 옵티마이저 모드 입니다. 
Parsing user id: 59 (SCOTT) 

Rows Row Source Operation 
------- --------------------------------------------------- 
3 FILTER 
5 SORT GROUP BY 
14 TABLE ACCESS FULL EMP 
1 SORT AGGREGATE 
4 TABLE ACCESS FULL EMP 


Rows Execution Plan 
------- --------------------------------------------------- 
0 SELECT STATEMENT GOAL: CHOOSE 
3 FILTER 
5 SORT (GROUP BY) 
14 TABLE ACCESS GOAL: ANALYZED (FULL) OF 'EMP' 
1 SORT (AGGREGATE) 
4 TABLE ACCESS GOAL: ANALYZED (FULL) OF 'EMP' 

******************************************************************************** 

alter session set sql_trace=false 


call count cpu elapsed disk query current rows 
------- ------ -------- ---------- ---------- ---------- ---------- ---------- 
Parse 1 0.00 0.00 0 0 0 0 
Execute 1 0.01 0.00 0 0 0 0 
Fetch 0 0.00 0.00 0 0 0 0 
------- ------ -------- ---------- ---------- ---------- ---------- ---------- 
total 2 0.01 0.00 0 0 0 0 

Misses in library cache during parse: 0 
Optimizer goal: CHOOSE 
Parsing user id: 59 (SCOTT) 



******************************************************************************** 
아래의 TOTAL은 전체 작업 결과에 대한 분석 결과 입니다. 

OVERALL TOTALS FOR ALL NON-RECURSIVE STATEMENTS 

call count cpu elapsed disk query current rows 
------- ------ -------- ---------- ---------- ---------- ---------- ---------- 
Parse 2 0.00 0.00 0 0 0 0 
Execute 3 0.01 0.00 0 0 0 0 
Fetch 2 0.00 0.00 0 6 0 3 
------- ------ -------- ---------- ---------- ---------- ---------- ---------- 
total 7 0.01 0.01 0 6 0 3 

Misses in library cache during parse: 1 


OVERALL TOTALS FOR ALL RECURSIVE STATEMENTS 

call count cpu elapsed disk query current rows 
------- ------ -------- ---------- ---------- ---------- ---------- ---------- 
Parse 1 0.00 0.00 0 0 0 0 
Execute 1 0.00 0.00 0 0 0 0 
Fetch 1 0.01 0.00 0 3 0 1 
------- ------ -------- ---------- ---------- ---------- ---------- ---------- 
total 3 0.01 0.00 0 3 0 1 

Misses in library cache during parse: 0 

3 user SQL statements in session. 
1 internal SQL statements in session. 
4 SQL statements in session. 
1 statement EXPLAINed in this session. 
******************************************************************************** 
Trace file: wink_ora_3316.trc 
Trace file compatibility: 9.00.01 
Sort options: default 

1 session in tracefile. 
3 user SQL statements in trace file. 
1 internal SQL statements in trace file. 
4 SQL statements in trace file. 
4 unique SQL statements in trace file. 
1 SQL statements EXPLAINed using schema: 
SCOTT.prof$plan_table 
Default table was used. 
Table was created. 
Table was dropped. 
54 lines in trace file. 

2013년 10월 17일 목요일

오라클 물리적 구조

Oracle 물리적 구조 oracle physical structure



------------------- 
물리적 DataBase구조 
-------------------- 
oracle 설치된 폴다에 가보면 oradata 폴더에 대부분파일이 위치한다,
확인해 보자.

A. DataFile 
- 모든 Oracle DataBAse는 하나이상의 DataFile을 가지며, DB의 영역이 부족할 때 자동으로 
확장할 수 있는 기능이 있다. 
- 하나이상의 DataFile이 TableSpace를 형성한다. 
- 수정된 Data나 새로운 Data는 파일에 즉시 Write할 필요가 없다.즉 디스크 Access량을 줄이고 
성능을 향상시키려면 Data를 메모리에 저장했다가 DBWR BackGround Process가 한번에 디스크에 
저장한다. 
B. Redo Log File 
- Oracle DB는 2개 이상의 Redo Log File을 가진다. 
- Redo Log의 주기능은 변경사항을 저장,이미 수정된 Data가 장애 때문에 DataFile에 기록되지 
못했다면 수정된 부분이 Redo Log에 있으므로 수행한 작업을 손실하지는 않는다. 
C.  Control File 
- Control File에는 DB이름, DataFile과 Redo Log File의 위치,DB생성시간등이 기록되어 있다. 
- Oracle은 Instance가 시작될때마다 DataBase와 Redo Log File을 지정한다. 새 DataFile이나 
Redo Log File이 생성되는 경우에는 Oracle은 Control File을 자동으로 수정한다. 

D, 파라미터파일
     -데이터베이스 이름
   - SGA메모리 구조와 할당크기
   - 컨트롤 파일명과 위치
   - 아카이브 파일정보
   - 언두세그먼트 정보

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





오라클 ORACLE 인덱스가 없는 테이블 조회 SQL

오라클  ORACLE 인덱스가 없는 테이블 조회 SQL 

참고하세요`

select        OWNER, 
        TABLE_NAME 
from 

select        OWNER, 
        TABLE_NAME 
from        dba_tables 
minus 
select        TABLE_OWNER, 
        TABLE_NAME 
from        dba_indexes 

orasnap_noindex 
where        OWNER not in ('SYS','SYSTEM') 
order        by OWNER,TABLE_NAME 

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


2013년 8월 26일 월요일

오라클 테이블 생성 예제

테이블 생성 하기


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


 
à 오라클을 설치하게 되면 SCOTT계정은 자동으로 생성되어 있을 것이다. 그리고 default tablespace SYSTEM 테이블스페이스로 설정 되어 있다. 원래 SYSTEM 테이블스페이스에 사용자의 테이블을 만드는 것은 좋은 방법이 아니다. 왜냐면 이 부분은 오라클 시스템에서 사용되는 객체들이 저장되는 곳이기 때문이다(딕셔너리 정보 등이 저장된다) . 그러므로 우선 SYS 계정으로 접속하여 SCOTT 사용자의 default tablespace USERS 라는 테이블스페이스로 변경하자. 그리고 실습을 위해 USER_DATA 라는 테이블스페이스를 만들자. 데이터파일의 경로는 PC환경에 맞게 수정하길 바란다.
 
SQL> connect / as sysdba
연결되었습니다.
 
SQL> alter user scott default tablespace users;
 
사용자가 변경되었습니다.
 
SQL> create tablespace user_data
  2  datafile 'C:\oracle\oradata\wink\test01.dbf'
  3  size 10m
  4  autoextend on
  5  next 1m
  6  maxsize 1000m;
 
테이블 영역이 생성되었습니다.
 
SQL> connect scott/tiger
연결되었습니다.
 
à 아래 예문에서 주의 깊게 볼 부분은 tablespace 구이다. 이것은 employee 테이블을 어느 테이블스페이스에 만들것인지에 대해 설정이며 생략되면 scott 사용자의 defaut tablespace에 만들어 지게 된다. 또한 테이블스페이스에서 지정한 매개변수들을 그대로 employee 테이블은 상속 받게 된다. 물론 그 다음 예문처럼 명시적으로 지정을 하는 것도 가능하다.
 
SQL> create table employee (
  2  empno number(4) primary key,
  3  ename varchar2(15) not null,
  4  addr varchar2(50) ,
  5  sal number(8,2)
  6  ) tablespace user_data;
 
테이블이 생성되었습니다.
 
SQL> create table employee2 (
  2  empno number(4) primary key,
  3  ename varchar2(15) not null,
  4  addr varchar2(50) ,
  5  sal number(8,2)
  6  )
  7  pctfree 10
  8  pctused 40
  9  tablespace user_data
 10  storage (
 11     initial 10k
 12     next 10k
 13     maxextents 20
 14     pctincrease 0
 15  );
 
테이블이 생성되었습니다.

[오라클자바커뮤니티강좌,oraclejava,javaoracle교육강좌강의,오라클자바실무개발잘하는학원,오라클강좌자바강좌강의]Flashback Feature – Transaction Query


FlashBack Transaction Query라고 하는 것은 이전 강좌에서 설명 드렸던 Flashback Version Query의 결과로 나타난 해당 Transaction에 대해 특별한 정보를 얻을 수 있는 것 정도로 보시면 됩니다.


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


VERSIONS_XID 값이 트랜잭션의 아이디라고 했는데 이 값을 FLASHBACK_TRANSACTION_QUERY의 인자 값으로 줘서 쿼리를 실행 하면 해당 트랜잭션에 대한 정보를 볼 수 있습니다.

예를 들면 어떤 DML을 이용했으며 어떠한 SQL 구문이 실행 되었다든지 하는 것이 확인 가능 합니다.

아래의 예를 통해 이해 하도록 하겠습니다.

아래에서 “0600030021000000”  값이 VERSIONS_XID 값 입니다. 이 값은 Flashback Version Query의 결과로서 얻어 낼 수 있습니다. 이전 강좌의 내용을 참고 하세요~

SELECT xid, operation, start_scn,commit_scn, logon_user, undo_sql
FROM  flashback_transaction_query
WHERE  xid = HEXTORAW('0600030021000000');


XID              OPERATION                        START_SCN COMMIT_SCN
---------------- -------------------------------- ---------- ----------
LOGON_USER
------------------------------
UNDO_SQL
----------------------------------------------------------------------------------------------------
0600030021000000 UPDATE                              725208    725209
SCOTT
update "SCOTT"."FLASHBACK_VERSION_QUERY_TEST" set "DESCRIPTION" = 'ONE' where ROWID = 'AAAMP9AAEAAAA
AYAAA';

0600030021000000 BEGIN                                725208    725209
SCOTT

XID              OPERATION                        START_SCN COMMIT_SCN
---------------- -------------------------------- ---------- ----------
LOGON_USER
------------------------------
UNDO_SQL
----------------------------------------------------------------------------------------------------



2 rows selected.
 

2013년 8월 9일 금요일

오라클의 각 Table은 내부적으로 ROWID 라는 의사 열을 가진다. 보통 SELECT등으로 Table을 질의하면 출력되지 않으나 컬럼 명으로 ROWID라는 예약어를 사용하면 읽어 들일 수 있다.  

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


ROWID에는 Table의 각 행에 대해 물리적인 주소라고 이해 하면 될 것이다. 오라클7에서 ROWID에는 파일번호, 블록번호, 블록 내 ROW번호 3가지 정보를 가지고 있었지만 오라클8 이상부터 ROWID에는 객체의 고유번호를 포함하고 있다는 것은 참고로 알아두자. 데이터를 검색할 때 ROWID를 이용하여 검색 한 다면 가장 빨리 원하는 자료를 검색하는 것이 가능하다.
아래를 보면 앞의 중복된 데이터를 지우는 질의가 이해 될 것이다. 같은 주민등록번호 와 이름을 가지더라도 rowid는 틀리다. 이 특징을 이용하여 중복 데이터를 찾는 것이다.

SQL> select rowid, jumin, name from addrbook;
ROWID              JUMIN          NAME
------------------ -------------- ------------
AAAJMZAABAAAPEKAAA 111111-2222222 홍길동
AAAJMZAABAAAPEKAAB 333333-4444444 가길동
AAAJMZAABAAAPEKAAC 111111-2222222 홍길동

2013년 8월 5일 월요일

[자바교육,오라클교육,자바프레임워크, 스트럿츠]Struts Action처리 후 다음 View 결정하기, 오라클자바커뮤니티

Action처리 후 다음 View 결정하기 


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


Action 클래스의 execute() 메소드를 보시면 Return형이 org.apache.struts.action.ActionForward임을 알 수 있습니다. ActionForward 클래스는 execute()메소드가 종료된 후 Controller에서 제어를 넘길 곳을 지정 합니다.

또한 코드 내에서 실제 파일명을 주는 대신 ActionForward 매핑에 JSP페이지(View)를 할당하고 이 ActionForward를 웹 애플리케이션 전체에서 사용 가능 하게 할 수 있습니다.

아래는 struts-config.xml 파일 내의 ActionForward 매핑 예 입니다.

<global-forwards>
        <forward name="success" path="/main.jsp" />
        <forward name="logoff" path="/logoff.do" />
        <forward name="login" path="/login.jsp" />
    </global-forwards>

또는 어떠한 Action에서만 Actionforward를 참조하게 하기 위해서는 다음과 같이 할 수도 있습니다.

<action
        Path=”/LoginSunmit”
        Type=”login2.LoginAction”
        Scope=”request”>
        <forward name=”SUCCESS” path=”/success.jsp” redirect=”true”/>
</action>

위에서 redirect를 true로 했으므로 이 경우엔 forward가 아니라 redirection을 이용합니다.

아래는 로그인 예제의 LogoffAction.java(로그오프 Action 처리) 입니다.

package login2;

import org.apache.struts.action.Action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletResponse;

import login2.LoginForm;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

public class LogoffAction extends Action {       
       
        public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) {
               
                //필요한 어트리뷰트를 뽑아 냅니다.
                //LoginAction에서 사용자의 로그온개체를 Consants.USER_KEY라는 이름으로 세션에 저장
                HttpSession session = request.getSession();
                UserInfoVO userinfo = (UserInfoVO)session.getAttribute(Constants.USER_KEY);
               
                if (userinfo != null) {
                        //로그를 남기자
                        StringBuffer buf = new StringBuffer("User Logout : " + userinfo.getId());
                        servlet.log(buf.toString());                                       
                }               
               
                //사용자의 로그인을 삭제, session.invaidate() 의 경우 션의 모든 것을 무효화 시킴.
                session.removeAttribute(Constants.USER_KEY);
               
                //성공적으로 처리 되었음을 알림
                return (mapping.findForward(Constants.SUCCESS));
               
        }

       

2013년 8월 3일 토요일

[Oracle SQL Explain Plan]ORACLE HINT 강좌 실행계획 SQL 연산(HASH ANTI-JOIN)

실행계획 SQL 연산(HASH ANTI-JOIN

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

ANTI 조인은 조인의 대상이 되는 테이블과 일치하지 않는 데이터를 추출하는 연산 입니다. SQL연산에서 NOT IN, NOT EXISTS, MINUS등이 해당되며 이러한 안티 조인은 MERGE ANTI-JOIN or HASH ANTI_JOIN으로 풀리도록 할 수 있습니다.

아래의 Query는 동일한 의미를 가지는 질의 입니다. 확인해 보세요~

create table myemp1
(empno number not null primary key,
 ename varchar2(100),
 deptno number,
 addr   varchar2(100),
 sal    number
 )

-- 실습을 위해 myemp1 1000만건 만들자.
DECLARE
          v_c NUMBER := 1;
BEGIN

          WHILE (v_c <= 10000000) LOOP
                insert into myemp1 values ( v_c, '홍길동'||v_c, mod(v_c, 5), '서울'||v_c, mod(v_c, 1000000));
                v_c := v_c + 1;
                insert into myemp1 values ( v_c, '다길동'||v_c, mod(v_c, 5), '부산'||v_c, mod(v_c, 1000000));
                v_c := v_c + 1;
                insert into myemp1 values ( v_c, '나길동'||v_c, mod(v_c, 5), '대구'||v_c, mod(v_c, 1000000));
                v_c := v_c + 1;
                insert into myemp1 values ( v_c, '나길동'||v_c, mod(v_c, 5), '광주'||v_c, mod(v_c, 1000000));
                v_c := v_c + 1;
          END LOOP;
          commit;
END;


create table myemp1_old
as select * from myemp1 where rownum < 1000000


  

 -- 통계정보 셍성
    exec DBMS_STATS.GATHER_TABLE_STATS(USER, 'MYDEPT1_OLD')
   
    exec DBMS_STATS.GATHER_TABLE_STATS(USER, 'MYDEPT1')




별다른 인덱스가 없는 상태에서 실행해 보자. 37초 걸린다.
오라클 11g에서 기본적으로 merge anti joinm으로 실행계획을 잡는다.


SQL> select count(e1.ename) 
  2   from myemp1 e1
  3   where (ename, sal) not in (select ename, sal
  4                                from myemp1_old e2);

COUNT(E1.ENAME)--28
-------------------
            9000001

   : 00:00:37.87

Execution Plan
----------------------------------------------------------
|   0 | SELECT STATEMENT     |            |     1 |    82 |       | 90492  
|   1 |  SORT AGGREGATE      |            |     1 |    82 |       |           
|   2 |   MERGE JOIN ANTI NA |            |    10M|   782M|       | 90492  
|   3 |    SORT JOIN         |            |    10M|   162M|   536M| 72263  
|   4 |     TABLE ACCESS FULL| MYEMP1     |    10M|   162M|       | 16961  
|*  5 |    SORT UNIQUE       |            |  1070K|    66M|   156M| 18229  
|   6 |     TABLE ACCESS FULL| MYEMP1_OLD |  1070K|    66M|       | 




이번에는 HASH ANTI JOIN으로 힌트를 주고 실행하자. ANTI JOIN 일때는 where절의 not in 출현 컬럼에 대해 is not null 조건을 주도록 하자.

SQL> select
           count(e1.ename)
  from myemp1 e1
  where (ename, sal) not in (select
                                    ename, sal
                               from myemp1_old e2
                               where ename is not null
                                 and sal is not null)
 and ename is not null
  and    sal is not null
 /

실행해 보면 수행시간이 절반 이상으로 준다. MERGE ANTI JOIN 보다 HASH ANTI JOIN 성능이 낫다.

COUNT(E1.ENAME)
---------------
        9000001

   : 00:00:14.92

Execution Plan
----------------------------------------------------------
|   0 | SELECT STATEMENT      |            |     1 |    82 |       | 36297  
|   1 |  SORT AGGREGATE       |            |     1 |    82 |       |
|*  2 |   HASH JOIN RIGHT ANTI|            |    10M|   782M|    78M| 36297  
|*  3 |    TABLE ACCESS FULL  | MYEMP1_OLD |  1070K|    66M|       | 
|*  4 |    TABLE ACCESS FULL  | MYEMP1     |    10M|   162M|       |



SQL> SELECT count(ename)
  2                FROM   MYEMP1 E
  3  WHERE  NOT EXISTS (SELECT  1
  4                     FROM MYEMP1_OLD EO
  5                     WHERE  EO.ENAME = E.ENAME
  6                     AND     EO.SAL    = E.SAL)
  7  and ename is not null
  8  and   sal is not null;

COUNT(ENAME)
------------
     9000001

   : 00:00:11.56

Execution Plan
----------------------------------------------------------
|   0 | SELECT STATEMENT      |            |     1 |    82 |       | 36295  
|   1 |  SORT AGGREGATE       |            |     1 |    82 |       |
|*  2 |   HASH JOIN RIGHT ANTI|            |    10M|   782M|    78M| 36295  
|   3 |    TABLE ACCESS FULL  | MYEMP1_OLD |  1070K|    66M|       | 
|*  4 |    TABLE ACCESS FULL  | MYEMP1     |    10M|   162M|       |



이번에는 HASH_AJ 힌트를 사용해 보자.
수행 시간은 대략 비슷하다.



SQL> SELECT count(ename)
  2                FROM   MYEMP1 E
  3  WHERE  NOT EXISTS (SELECT  /*+ hash_aj */1
  4                     FROM MYEMP1_OLD EO
  5                     WHERE  EO.ENAME = E.ENAME
  6                     AND     EO.SAL    = E.SAL)
  7  and ename is not null
  8  and   sal is not null;

COUNT(ENAME)
------------
     9000001

   : 00:00:11.00

Execution Plan
----------------------------------------------------------
|   0 | SELECT STATEMENT      |            |     1 |    82 |       | 36295  
|   1 |  SORT AGGREGATE       |            |     1 |    82 |       |
|*  2 |   HASH JOIN RIGHT ANTI|            |    10M|   782M|    78M| 36295  
|   3 |    TABLE ACCESS FULL  | MYEMP1_OLD |  1070K|    66M|       | 
|*  4 |    TABLE ACCESS FULL  | MYEMP1     |    10M|   162M|       |



이번에는 MINUS로 풀어 보자.

SQL> with a as (
  2      select  ename, sal
  3      from myemp1
  4      minus
  5      select  ename, sal
  6      from myemp1_old
  7   )
  8  select count(ename) from a  ;

COUNT(ENAME)
------------
     9000001

   : 00:00:30.48

Execution Plan
----------------------------------------------------------
|   0 | SELECT STATEMENT      |            |     1 |    52 |       | 90492  
|   1 |  SORT AGGREGATE       |            |     1 |    52 |       |
|   2 |   VIEW                |            |    10M|   495M|       | 90492  
|   3 |    MINUS              |            |       |       |       |
|   4 |     SORT UNIQUE       |            |    10M|   162M|   268M| 72263  
|   5 |      TABLE ACCESS FULL| MYEMP1     |    10M|   162M|       |
|   6 |     SORT UNIQUE       |            |  1070K|    66M|    78M| 18229  
|   7 |      TABLE ACCESS FULL| MYEMP1_OLD |  1070K|    66M|       | 


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

HSH ANTI JOIN으로 풀 경우 성능이 향상되므로 위 문장과 같이 한 테이블에 존재하지 않는 로우만 추출하는 경우엔 HASH ANTI JOIN  되도록 힌트를 사용하는 것이 유리합니다.


이번에는 조금 더 개선을 해서 EMPTEST TABLE  EMPNO, ENAME, SAL 컬럼으로 비트맵 인덱스를 구성해서 쿼리를 해 보자.

create bitmap index idx_bitmap_empno_ename_sal on emptiest (empno, ename, sal)

select   /*+ index(idx_bitmap_empno_ename_sal e1) */
          count(e1.ename)  
 from emptest e1
 where (ename, sal) not in (select
                                   ename, sal
                              from emptest_old e2
                              where ename is not null
                                and sal is not null)
 and    ename is not null
 and    sal is not null