2014년 2월 26일 수요일

[오라클12C,Oracle12C,SQL,identity,sequence]오라클12C 시퀀스,ID컬럼,GENERATED AS IDENTITY,오라클12시퀀스예제,실무오라클교육기관,개발자실무교육

[오라클12C,Oracle12C,SQL,identity,sequence]오라클12C 시퀀스,ID컬럼,GENERATED AS IDENTITY,오라클12시퀀스예제,실무오라클교육기관,개발자실무교육,재직자환급

oracle12c generated as identity란?
데이터베이스 테이블의 숫자로 된 unique key컬럼의 경우 기존 시쿼스등을 이용하여 자동증분 하게 했지만
오라클12C에서부터는 generated as identity로 정의하여 사용이 가능하다.

1. 먼저 기존 시퀀스를 이용한 방법을 보자.

SQL>create sequence emp_seq increment by 1 start with 1;
테이블 구조는 다음과 같다.
create table emp
 (emp_no number,
  ename varchar2(20));

insert문에서는 다음과 같이 사용했다.

SQL>Insert into emp values  (emp_seq.nextval, ‘홍길동’);

2. 이번에는 GENERATED AS IDENTITY 를 이용해보자.
 
 
테이블 만들 때 정의하면 된다.
 
create table emp
  (emp_no number generated as identity ,
   ename varchar2(20));

insert문장은 다음처럼 사용!
 
Insert into emp values (‘홍길동’);

만약 GENERATED AS IDENTITY로 선언한 컬럼에 값을 주면 아래와 같은 오류가 발생한다.

insert into emp values (1,'홍길동');
insert into test values (1,'홍길동')
*
ERROR at line 1:
ORA-32795: cannot insert into a generated always identity column

[오라클12C새기능]커서리턴,DBMS_SQL.RETURN_RESULT, REF CURSOR대신 사용, 구로가산오라클교육,오라클SQL 교육,SQL강좌강의 오라클

[오라클12C새기능]커서리턴,DBMS_SQL.RETURN_RESULT, REF CURSOR대신 사용, 구로가산오라클교육,오라클SQL 교육,SQL강좌강의
 
오라클12C 이전에는 커서의 값을 다른 App등으로 리턴시켜주기 위해서는 REF CURSOR를 선언하곤 했는데... 오라클12c부터는 DBMS_SQL.RETURN_RESULT를 이용해서 묵시적으로 가능하다, 예제를 참고하자.
 
CREATE TABLE test (
  id           NUMBER,
  name         VARCHAR2(10)
);
 
INSERT INTO test VALUES (1, '1길동');
INSERT INTO test VALUES (2, '2길동');
COMMIT;

CREATE OR REPLACE PROCEDURE get_name (p_id IN NUMBER DEFAULT NULL)
AS
  cursor_1 SYS_REFCURSOR;
  cursor_2 SYS_REFCURSOR;
BEGIN
  IF p_id IS NOT NULL THEN
 
    OPEN cursor_1 FOR
      SELECT name
      FROM   test
      WHERE  id = p_id;
 
      DBMS_SQL.RETURN_RESULT(cursor_1);

  END IF;
  OPEN cursor_2 FOR
    SELECT COUNT(*)
    FROM   test;
 
    DBMS_SQL.RETURN_RESULT(cursor_2);

END;
/

SQL*Plus에서...
 
SQL> EXEC get_name(1);
PL/SQL procedure successfully completed.
ResultSet #1
NAME                   
----------------------
1길동                
1 row selected.
 
ResultSet #2
 
  COUNT(*)
----------
         2
1 row selected.
SQL> EXEC get_name;
 
PL/SQL procedure successfully completed.
 
ResultSet #1
 
  COUNT(*)
----------
         2
1 row selected.
 

[오라클팁,구로가산오라클교육,ORACLE TIP]inline view를 이용한 멀티테이블 삭제(key-reserved) DELETE문의 FROM절에 인라인뷰를 이용하여 여러테이블을 기술할 수 있는데 이 경우에는 키보전 테이블(key reserved table)의 데이터는 삭제되지 않는다. 아래예제를 통해 이해하자. SQL> create table a ( 2 seq number, 3 name varchar2(20)); 테이블이 생성되었습니다. SQL> create table b ( 2 seq number primary key, 3 name varchar2(20)); 테이블이 생성되었습니다. SQL> CREATE SEQUENCE seq_test; 시퀀스가 생성되었습니다. SQL> INSERT ALL 2 INTO a VALUES(seq_test.nextval, name01) 3 INTO b VALUES(seq_test.currval,name02) 4 SELECT 'aaa' AS name01 , 'bbb' AS name02 5 FROM DUAL 6 / 2 개의 행이 만들어졌습니다. SQL> INSERT ALL 2 INTO a VALUES(seq_test.nextval, name01) 3 INTO b VALUES(seq_test.currval,name02) 4 SELECT 'aaa' AS name01 , 'bbb' AS name02 5 FROM DUAL 6 / 2 개의 행이 만들어졌습니다. SQL> select * from a; SEQ NAME ---------- -------------------- 3 aaa 4 aaa SQL> select * from b; SEQ NAME ---------- -------------------- 3 bbb 4 bbb -- 키보전 테이블인 b는 삭제되지 않는다. SQL> DELETE FROM 2 (SELECT * 3 FROM a , b 4 WHERE a.seq = b.seq 5 ); 2 행이 삭제되었습니다. SQL> select * from a; 선택된 레코드가 없습니다. SQL> select * from b; SEQ NAME ---------- -------------------- 3 bbb 4 bbb 위 예문에서 두 테이블 a, b 둘다 primary key가 없다면 다음과 같은 오류가 발생한다. SQL> DELETE FROM 2 (SELECT * 3 FROM a , b 4 WHERE a.seq = b.seq 5 ); (SELECT * * 2행에 오류: ORA-01752: 뷰으로 부터 정확하게 하나의 키-보전된 테이블 없이 삭제할 수 없습니다 [출처] 오라클자바커뮤니티 - http://www.oraclejavanew.kr/bbs/board.php?bo_table=oracleTip&wr_id=733 자바 오라클/빅데이터 아이폰/안드로이드 닷넷/WPF 표준웹/HTML5 채용/취업무료교육 초보자코스 [기업100%환급]Spring ,MyBatis,Hibernate실무과정 총 5일 40시간 03-10 [기업100%환급]자바기초에서 JDBC, Servlet/JSP까지 총 5일 40시간 03-17 Spring3.X, MyBatis, Hibernate실무과정 총 12일 36시간 03-03 자바기초에서JSP,Ajax,jQuery,Spring3.2,MyBatis까지 총 24일 72시간 03-14 [주말저녁]자바기초에서JSP,Servlet,Ajax,jQUERY,스프링,마이바티스,하이버네이트 총 18일 72시간 03-01 JAVA&WEB프레임워크실무과정 총 14일 98시간 03-08 Spring3.X, MyBatis, Hibernate실무과정 총 5일 35시간 03-09 자바기초에서JSP,Ajax,jQuery,Spring3.2,MyBatis까지 총 10일 70시간 03-22 [기업100%환급]PL/SQL,ORACLE HINT,TUNING 총 5일 40시간 03-03 [기업100%환급]SQL기초에서 Schema Object까지 총 5일 40시간 03-10 SQL초보에서실전전문가까지 총 18일 54시간 03-10 SQL초보에서실전전문가까지 총 8일 56시간 03-01 [주말저녁]SQL기초에서 Schema Object까지 총 10일 40시간 03-08

[오라클팁,구로가산오라클교육,ORACLE TIP]inline view를 이용한 멀티테이블 삭제(key-reserved)

DELETE문의 FROM절에 인라인뷰를 이용하여 여러테이블을 기술할 수 있는데 이 경우에는 키보전 테이블(key reserved table)의 데이터는 삭제되지 않는다.

아래예제를 통해 이해하자.

SQL> create table a (
  2  seq number,
  3  name varchar2(20));

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

SQL>  create table b (
  2  seq number primary key,
  3  name varchar2(20));

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

SQL> CREATE SEQUENCE seq_test;

시퀀스가 생성되었습니다.

SQL>  INSERT ALL
  2    INTO a VALUES(seq_test.nextval, name01)
  3    INTO b VALUES(seq_test.currval,name02)
  4    SELECT 'aaa' AS name01 , 'bbb' AS name02
  5    FROM DUAL
  6  /

2 개의 행이 만들어졌습니다.

SQL>  INSERT ALL
  2    INTO a VALUES(seq_test.nextval, name01)
  3    INTO b VALUES(seq_test.currval,name02)
  4    SELECT 'aaa' AS name01 , 'bbb' AS name02
  5    FROM DUAL
  6  /

2 개의 행이 만들어졌습니다.

SQL> select * from a;

      SEQ NAME
---------- --------------------
        3 aaa
        4 aaa

SQL> select * from b;

      SEQ NAME
---------- --------------------
        3 bbb
        4 bbb


-- 키보전 테이블인 b는 삭제되지 않는다.

SQL> DELETE FROM
  2            (SELECT *
  3              FROM a , b
  4              WHERE a.seq = b.seq
  5            );

2 행이 삭제되었습니다.

SQL> select * from a;

선택된 레코드가 없습니다.

SQL> select * from b;

      SEQ NAME
---------- --------------------
        3 bbb
        4 bbb



위 예문에서 두 테이블 a, b 둘다 primary key가 없다면 다음과 같은 오류가 발생한다.

SQL>  DELETE FROM
  2            (SELECT *
  3              FROM a , b
  4              WHERE a.seq = b.seq
  5            );
          (SELECT *
          *
2행에 오류:
ORA-01752: 뷰으로 부터 정확하게 하나의 키-보전된 테이블 없이 삭제할 수 없습니다 

2014년 2월 20일 목요일

[오라클조인]해시조인(Hash Join),use_hash, ordered, Build Input, Probe,Oracle Join ,오라클힌트란,HINT교육강좌,힌트카페,힌트블로그, 오라클튜닝개요 Hash Join

[오라클조인]해시조인(Hash Join),use_hash, ordered, Build Input, Probe,Oracle Join ,오라클힌트란,HINT교육강좌,힌트카페,힌트블로그, 오라클튜닝개요

Hash Join


 해시 조인(Hash-Join)은 두 테이블 중 하나를 기준으로 비트맵 해시 테이블을 메모리에 올린 후 나머지 테이블을 스캔 하면서 해싱 테이블을 적용하여 메모리에 로딩된 테이블과 비교하여 매칭되는 데이터를 추출하는 방식의 조인이다.

RDBMS에 서 비용이 가장 많이 들어가는 Join 방법으로 주로 작은 Table과 큰 Table 의 Join 시 사용되어 지며 , Driving 조건과 상관없이 좋은 성능을 발휘할 수 있다.

[처리순서]

1. 작은 테이블(Build Input)을 읽어 Hash Area에 해시 테이블 생성한다.
2. 큰테이블 집합(Probe Input)을 읽어 해시 테이블을 탐색하면서 조인하는 방식이다.

[특징]

- 해시 조인은 안티 조인과 병렬처리와 잘 맞으며 범위 검색(Range scan)이 아닌 동등 비교(Equi-Join, where절에서 등호로 비교하는 경우)에 더 적합하다.

- NL조인 과 달리 Random 액세스 부하가 없다.(단, 양쪽집합을 읽는 과정에서 인덱스를 이용한다면 Random 액세스 발생)

- NL조인 과 달리 Hash Area에 미리 생성해 둔 해시 테이블(또는 해시 맵)을 이용한다.
(해시테이블을 만드는 단계는 전체범위처리 불가피, Probe Input을 스캔하는 단계는 NL조인처럼 부분범위처리가능)

- 소트머지조인과 달리 조인 전에 미리 양쪽 집합을 정렬하는 부담이 없으며 NL조인 과 달리 래치획득 과정없이 PGA에서 빠르게 데이터 탐색한다.

- 해시 테이블을 생성하는 비용이 수반됨으로 Build Input이 작을때 효과적이며 PGA(or SGA) 메모리에 할당되는 Hash Area에 담길 정도로 충분히 작아야 한다. 해시키 값으로 사용되는 컬럼에 중복값이 거의 없을 때 효과적이다.


- SQL 문장에서 옵티마이저는 해쉬 조인으로 수행하기 위해 작은 테이블을 메모리에 로드 한 후 큰 테이블을 여러 Partition으로 분리하여 메모리에 로드가 되어 있는 작은 테이블을 해쉬 알고리즘에 의하여 탐색하게 되고 여러 Partition 으로 나뉘어 지는 테이블은 HASH_AREA_SIZE 에 명시된 메모리에 상주되며 메모리가 충분치 않아 메모리에 모두 상주 시킬수 있는 상황이라면 디스크에 위치하게 된다. (충분한 hash_area_size 제공필요)

- 해시조인을 사용하기 위해서는 USE_HASH hint를 사용 한다.


해시 조인 사용기준

- 기준
1. 한쪽 테이블이 Hash Area에 담길 정도로 충분히 작아야함.
2. Build Input 해시 키 컬럼에 중복 값이 거의 없어야 함.
 
- 조건
1. 조인 컬럼에 적당한 인덱스가 없어 NL조인이 비효율적일때
2. 조인 컬럼에 인덱스가 있더라고 NL 조인 드라이빙 집합에서 Inner 쪽 집합으로서의 조인 액세스량이 많아 Random 액세스 부하가 심할 때
3. 소트 머지 조인하기에는 두 테이블이 너무 커 소트 부하가 심할때
4. 수행빈도가 낮고 쿼리 수행 시간이 오래 걸리는 대용량 테이블을 조인할때
 
- 결과
1. 해시테이블은 단 하나의 쿼리를 위해 생성하고 조인이 끝나면 곧바로 소멸하는 자료구조이다.
2. 수행빈도가 낮고, 쿼리수행 시간이 오래 걸리는, 대용량테이블을 조인할 때



[예제]

실습을 위한 데이터는 아래 URL에서 만들자.

http://www.oraclejavanew.kr/bbs/board.php?bo_table=LecHINT&wr_id=117


myemp1 : 2,000만건
mydept1 : 5건(0,1,2,3,4)

아래 실습은 Oracle11g R2에서 진행되었다.

SQL> set autotrace on
SQL> set timing on


[myemp1의 deptno 컬럼에 인덱스가 없는 경우]

SQL>  select /*+ use_hash(d e) */ e.empno, e.ename, d.dname
  2    from mydept1 d, myemp1 e
  3  where e.deptno = d.deptno
  4    and  e.deptno = 0;

4000000 개의 행이 선택되었습니다.

경  과: 00:00:33.65

Execution Plan
----------------------------------------------------------
Plan hash value: 2850841177

------------------------------------------------------------------------------
| Id  | Operation          | Name    | Rows  | Bytes | Cost (%CPU)| Time    |
------------------------------------------------------------------------------
|  0 | SELECT STATEMENT  |        |  4000K|  129M| 40081  (1)| 00:08:01 |
|*  1 |  HASH JOIN        |        |  4000K|  129M| 40081  (1)| 00:08:01 |
|*  2 |  TABLE ACCESS FULL| MYDEPT1 |    1 |    10 |    3  (0)| 00:00:01 |
|*  3 |  TABLE ACCESS FULL| MYEMP1  |  4000K|    91M| 40057  (1)| 00:08:01 |
------------------------------------------------------------------------------



SQL> select /*+ use_hash(e) */ e.empno, e.ename, d.dname
  2    from mydept1 d, myemp1 e
  3  where e.deptno = d.deptno
  4    and  e.deptno = 0;

4000000 개의 행이 선택되었습니다.

경  과: 00:00:35.82

Execution Plan
----------------------------------------------------------
Plan hash value: 934128101

-------------------------------------------------------------------------------
| Id  | Operation          | Name    | Rows  | Bytes | Cost (%CPU)| Time    |
-------------------------------------------------------------------------------
|  0 | SELECT STATEMENT    |        |  4000K|  129M| 40061  (1)| 00:08:01 |
|  1 |  MERGE JOIN        |        |  4000K|  129M| 40061  (1)| 00:08:01 |
|*  2 |  TABLE ACCESS FULL | MYEMP1  |  4000K|    91M| 40057  (1)| 00:08:01 |
|*  3 |  SORT JOIN        |        |    1 |    10 |    4  (25)| 00:00:01 |
|*  4 |    TABLE ACCESS FULL| MYDEPT1 |    1 |    10 |    3  (0)| 00:00:01 |
-------------------------------------------------------------------------------


아래처럼 테이블 하나만 쓸려면 ordered와 같이 사용되야 한다.

SQL> select /*+ ordered  use_hash(e) */ e.empno, e.ename, d.dname
  2  from mydept1 d, myemp1 e
  3  where e.deptno = d.deptno
  4  and  e.deptno = 0;

4000000 개의 행이 선택되었습니다.

경  과: 00:00:33.90

Execution Plan
----------------------------------------------------------
Plan hash value: 2850841177

------------------------------------------------------------------------------
| Id  | Operation          | Name    | Rows  | Bytes | Cost (%CPU)| Time    |
------------------------------------------------------------------------------
|  0 | SELECT STATEMENT  |        |  4000K|  129M| 40081  (1)| 00:08:01 |
|*  1 |  HASH JOIN        |        |  4000K|  129M| 40081  (1)| 00:08:01 |
|*  2 |  TABLE ACCESS FULL| MYDEPT1 |    1 |    10 |    3  (0)| 00:00:01 |
|*  3 |  TABLE ACCESS FULL| MYEMP1  |  4000K|    91M| 40057  (1)| 00:08:01 |
------------------------------------------------------------------------------



[myemp1의 deptno 컬럼에 인덱스가 있는 경우]




먼저 Myemp1 테이블의 deptno 컬럼에 비트맵 인덱스를 만들자.

SQL> --비트맵 인덱스를 만들자.
SQL> create bitmap index idx_myemp1_deptno on myemp1(deptno);

인덱스가 생성되었습니다.

경  과: 00:00:24.32



SQL> select /*+ use_hash(d e) index_combine(e)*/ e.empno, e.ename, d.dname
  2  from mydept1 d, myemp1 e
  3  where e.deptno = d.deptno
  4  and  e.deptno = 0;

4000000 개의 행이 선택되었습니다.

경  과: 00:00:55.20

--------------------------------------------------------------------------------
|  0 | SELECT STATEMENT              |                    |  4000K|  129M| 705
54  (1)| 00:14:07 |
|*  1 |  HASH JOIN                    |                    |  4000K|  129M| 705
54  (1)| 00:14:07 |
|*  2 |  TABLE ACCESS FULL          | MYDEPT1            |    1 |    10 |
 3  (0)| 00:00:01 |
|  3 |  TABLE ACCESS BY INDEX ROWID | MYEMP1            |  4000K|    91M| 705
31  (1)| 00:14:07 |
|  4 |    BITMAP CONVERSION TO ROWIDS|                    |      |      |
|*  5 |    BITMAP INDEX SINGLE VALUE | BIDX_MYEMP1_DEPTNO |      |      |


실망스러운 결과다.


이번에는 B*Tree 인덱스를 만들어 보자.

SQL> create  index idx_myemp1_deptno on myemp1(deptno);

인덱스 범위 검색으로 별루다.

SQL> select /*+ use_hash(d e) index(e idx_myemp1_deptno) */ e.empno, e.ename, d.dname
  2  from mydept1 d, myemp1 e
  3  where e.deptno = d.deptno
  4  and  e.deptno = 0;

4000000 개의 행이 선택되었습니다.

경  과: 00:01:07.43


|  0 | SELECT STATEMENT            |                  |  4000K|  129M|  116
K  (1)| 00:23:13 |
|*  1 |  HASH JOIN                  |                  |  4000K|  129M|  116
K  (1)| 00:23:13 |
|*  2 |  TABLE ACCESS FULL          | MYDEPT1          |    1 |    10 |    3
  (0)| 00:00:01 |
|  3 |  TABLE ACCESS BY INDEX ROWID| MYEMP1            |  4000K|    91M|  115
K  (1)| 00:23:12 |
|*  4 |    INDEX RANGE SCAN          | IDX_MYEMP1_DEPTNO |  4000K|      |  7732
  (1)| 00:01:33 |


이번에는 드라이빙 테이블을 myemp1으로 바꾸어 보자.

Use_hash의 인자로 비드라이빙 테이블이 오고 힌트 맨 앞에는 ordered를 써주자. 물론 from절 바로 다음에 드라이빙 테이블을 기술하자.(ordered를 기술하면 from절 다음에 반드시 드라이빙 테이블이 와야한다)

SQL>  select /*+ ordered use_hash(d) */ e.empno, e.ename, d.dname
  2  from myemp1 e, mydept1 d
  3  where e.deptno = d.deptno
  4  and  e.deptno = 0;

4000000 개의 행이 선택되었습니다.

경  과: 00:00:57.92

Execution Plan

--------------------------------------------------------------------------------
|  0 | SELECT STATEMENT  |        |  4000K|  129M|      | 46920  (1)| 00:0
9:24 |
|*  1 |  HASH JOIN        |        |  4000K|  129M|  137M| 46920  (1)| 00:0
9:24 |
|*  2 |  TABLE ACCESS FULL| MYEMP1  |  4000K|    91M|      | 40057  (1)| 00:0
8:01 |
|*  3 |  TABLE ACCESS FULL| MYDEPT1 |    1 |    10 |      |    3  (0)| 00:0
0:01 |
--------------------------------------------------------------------------------


이번에는 leading힌트를 사용해 보자. From절 다음에 꼭 드라이빙 테이블이 와야 하는 것은 아니다.

SQL>  select /*+ leading(d) use_hash(e) */ e.empno, e.ename, d.dname
  2  from myemp1 e, mydept1 d
  3  where e.deptno = d.deptno
  4  and  e.deptno = 0;

4000000 개의 행이 선택되었습니다.

경  과: 00:00:33.48

Execution Plan

------------------------------------------------------------------------------
| Id  | Operation          | Name    | Rows  | Bytes | Cost (%CPU)| Time    |
------------------------------------------------------------------------------
|  0 | SELECT STATEMENT  |        |  4000K|  129M| 40081  (1)| 00:08:01 |
|*  1 |  HASH JOIN        |        |  4000K|  129M| 40081  (1)| 00:08:01 |
|*  2 |  TABLE ACCESS FULL| MYDEPT1 |    1 |    10 |    3  (0)| 00:00:01 |
|*  3 |  TABLE ACCESS FULL| MYEMP1  |  4000K|    91M| 40057  (1)| 00:08:01 |
------------------------------------------------------------------------------

Leading 힌트에서는 from절 다음 테이블의 위치는 중요하지 않다.


SQL>    select /*+ leading(d) use_hash(e) */ e.empno, e.ename, d.dname
  2  from mydept1 d, myemp1 e
  3  where e.deptno = d.deptno
  4    and  e.deptno = 0;

4000000 개의 행이 선택되었습니다.

경  과: 00:00:33.61

Execution Plan
-----------------------------------------------------------------------------
| Id  | Operation          | Name    | Rows  | Bytes | Cost (%CPU)| Time    |
------------------------------------------------------------------------------
|  0 | SELECT STATEMENT  |        |  4000K|  129M| 40081  (1)| 00:08:01 |
|*  1 |  HASH JOIN        |        |  4000K|  129M| 40081  (1)| 00:08:01 |
|*  2 |  TABLE ACCESS FULL| MYDEPT1 |    1 |    10 |    3  (0)| 00:00:01 |
|*  3 |  TABLE ACCESS FULL| MYEMP1  |  4000K|    91M| 40057  (1)| 00:08:01 |
------------------------------------------------------------------------------