본문 바로가기

sql_tuning

3. 조인 튜닝

728x90
반응형

조인 문장 튜닝

*조인의 방법 3가지

                           힌트

1. nested loop 조인 : use_nl

2. hash 조인         : use_hash

3. sort merge 조인  : use_merge

 

nested loop 조인

   중첩    루프  조인

 

문제38. 이름, 부서위치를 출력하는 조인문장의

 실행계획을 보고

 emp 테이블을 먼저 읽고 dept 조인했는지

 dept 테이블을 먼저 읽고 emp 조인했는지 알아내시오

 

select e.ename, d.loc

from emp e, dept d

where e.deptno = d.deptno;

dept -> emp -> hash join -> select statement

 

얇은책과 두꺼운책 조인해서 결과를 보겠다는 거임.

그래서 얇은책 먼저 ㄱㄱ

옵티마이져가 알아서 똑똑하게 했음.

 

 

nested loop 조인

 "조인하려는 데이터의 양이 작은 경우는 nested loop 조인이

 유리하다"

 

dept ---------------> emp

10                          10

20                         

30

40

4                        14

 

자로 훑어 내려가는데

꺾을때가 오래 걸림.

경우의 수로만 따지면 56 똑같지만

dept 출발하면 4번만 꺾으면 되서

빠르다.

 

 

select /*+ use_nl(d e) */

        e.ename, d.loc

 from emp e, dept d

 where e.deptno = d.deptno ;

hash 조인에서 nested loops 바뀐걸 있다.

 

 

*조인 순서를 변경하는 힌트

1. ordered : from 절에서 기술한 순서대로 조인하겠다.

2. leading  : leading 힌트 안에 테이블 순서대로 조인하겠다.

 

 

select /*+ ordered use_nl(d e) */

        e.ename, d.loc

 from emp e, dept d

 where e.deptno = d.deptno ;

emp 먼저 드라이빙 하라고 하는것임.

블럭수 45

 

 

 

 

select /*+ ordered use_nl(d e) */

        e.ename, d.loc

 from dept d, emp e

 where e.deptno = d.deptno ;

 

from절에 있는 바꾸면

드라이빙 순서 바꿀 있다.

dept 먼저 돌리면

블럭수 28

 

 

 

*ordered from절에 직접써서 순서를 바꿔야 해서 귀찮다.

 

*조인 순서를 변경하는 힌트

1. ordered : from 절에서 기술한 순서대로 조인하겠다.

2. leading  : leading 힌트 안에 테이블 순서대로 조인하겠다.

 

 

*leading 이용해본다.

 

select /*+ leading(e d) use_nl(d e) */

        e.ename, d.loc

 from dept d, emp e

 where e.deptno = d.deptno ;

from절과 상관없이 leading 먹인 순서대로

출력된다.

 

 

 

 

문제39. emp salgrade dept 조인해서

이름, 월급, 부서위치, 급여등급을 출력하시오.

 

select e.ename, e.sal, d.loc, s.grade

from emp e, dept d, salgrade s

where e.deptno = d.deptno

and e.sal between s.losal

             and     s.hisal;

 

 

 

문제40. 위의 조인문장의 조인순서와 조인방법을

 아래의 방법으로 수행하시오

조인순서:   salgrade ----> emp------------->  dept

                                       

조인방법:        nested loop조인   nested loop 조인

 

 

 

 

SELECT /*+ leading(s e d)  use_nl(e)  use_nl(d) */

 e.ename, e.sal, d.loc, s.grade

from emp e, dept d, salgrade s

where e.deptno = d.deptno

and e.sal between s.losal

             and     s.hisal;

 

 

salgrade leading 의해 제일먼저 드라이빙되서

use_nl(s) 언급 안해도 .

 

 

 

문제41.위의 sql 아래와 같은 조인순서로 실행되게하시오.

조인순서:   dept----------> emp------------->  salgrade

                                                    

조인방법:        nested loop조인    nested loop 조인

 

select /*+ leading( d e s ) use_nl(e) use_nl(s) */

           e.ename, e.sal, d.loc, s.grade

 from emp e, dept d, salgrade s

where e.deptno = d.deptno

 and

       e.sal between s.losal

                and          s.hisal;

 

*nested loop 조인 걸면 뒤에 테이블값으로 표시한다

dept---------->emp

           

    nested loop 

   use_nl(e)

 

 

자료 큰거로 돌려 봅시다.

create table  sales600

 as

 select *

  from  sales;

 

 create table customers600

 as

  select *

  from customers;

 

sales600 count 90만개

cunstomers600 count 5만개

 

 

SELECT  COUNT(*)

     FROM sales600 s, customers600 c

     WHERE s.cust_id = c.cust_id          

     AND c.country_id = 52790 

     AND s.time_id BETWEEN TO_DATE('1999/01/01','YYYY/MM/DD') 

                       AND TO_DATE('1999/12/31','YYYY/MM/DD') ;

블럭수 5966

 

SELECT   /*+ leading(c s) use_nl(s) */

   COUNT(*)

     FROM sales600 s, customers600 c

     WHERE s.cust_id = c.cust_id          

     AND c.country_id = 52790 

     AND s.time_id BETWEEN TO_DATE('1999/01/01','YYYY/MM/DD') 

                       AND TO_DATE('1999/12/31','YYYY/MM/DD') ;

 

 

쿼리가 use_nl 맞는 코딩이지만

값이 엄청 느리다.

(nested loop 작은값에만 유리함)

use_nl(c) 하면 빨리 값이 출력되긴 하나

leading 절에 반하는 명령.( 잘못된 명령)

옵티마이져가 명령 이상해서 자기 맘대로

hash 뽑은것.

 

 

문제42.(오늘의 마지막 문제)

아래의 sql 튜닝하시오

(무조건 nested loop 조인으로 수행하되

 가장 좋은 조인 순서를 결정하시오!)

 

 

create  table sales100  as  select  * from sales;

create  table times100  as  select  * from times;

create  table products100 as select * from products;

 

튜닝전:

select /*+ leading(s t p) use_nl(t) use_nl(p) */

  p.prod_name, t.CALENDAR_YEAR, sum(s.amount_sold)

  from   sales100  s, times100  t, products100  p

  where   s.time_id = t.time_id

  and  s.prod_id = p.prod_id

    and  t.CALENDAR_YEAR in (2000,2001)

    and p.prod_name like 'Deluxe%'

    group  by  p.prod_name, t.calendar_year;

 

 

 

카운트: sales100    91만개

          times100   1800

          products100    72

 

튜닝후:

 

순서는 p-t-s

 

create index times100_calendar_year_func

on times100(to_char(calendar_year) );

 

create index products100_name

on products100(prod_name);

 

 

select /*+ leading(p t s) use_nl(t) use_nl(s) */

  p.prod_name, t.CALENDAR_YEAR, sum(s.amount_sold)

  from   sales100  s, times100  t, products100  p

  where   s.time_id = t.time_id

  and  s.prod_id = p.prod_id

    and  TO_CHAR(t.CALENDAR_YEAR) = TO_DATE('2000', 'rrrr')

    AND  TO_CHAR(t.CALENDAR_YEAR) = TO_DATE('2001', 'rrrr')

    and p.prod_name like 'Deluxe%'

    group  by  p.prod_name, t.calendar_year;

 

 

 

그런데

 

where  p.prod_name like 'Deluxe%;

이게 1건임.

 

select /*+ leading(s t p) use_nl(t) use_nl(p) */

  p.prod_name, t.CALENDAR_YEAR, sum(s.amount_sold)

  from   sales100  s, times100  t, products100  p

  where   s.time_id = t.time_id

  and  s.prod_id = p.prod_id

    and  t.CALENDAR_YEAR in (2000,2001)

    and p.prod_name like 'Deluxe%'

    group  by  p.prod_name, t.calendar_year;

 

sales100 이랑 times100 time_id 조인

sales100 이랑 products100 이랑  pro_id 조인

(*times100 이랑 products100 조인된게 없음)

근데 출력조건이

times100에서 calendar 2000, 2001

products100 에서 'deluxe%' 충족시키는게 조건임.

 

 

 

선생님 설명+

1. 테이블마다 주석처리로 테이블 건수를 적어줘야 .

 

2. where 절에 뽑고자 하는 값의 건수도 적어줘야 .

 

3. 출력하고자 하는 가장 작은 값부터

 드라이빙 되게끔 순서를 결정한다.

(테이블 데이터수 순서가 아니라)

 

      sales100    91만개

  times100   1800

products100    72

 

 

 

select /*+ leading(s t p) use_nl(t) use_nl(p) */

  p.prod_name, t.CALENDAR_YEAR, sum(s.amount_sold)

  from   sales100  s, times100  t, products100  p ----918843, 1826, 72

  where   s.time_id = t.time_id

  and  s.prod_id = p.prod_id

    and  t.CALENDAR_YEAR in (2000,2001)----731

    and p.prod_name like 'Deluxe%'-------------1

    group  by  p.prod_name, t.calendar_year;

 

1. products100 테이블에서 p.prod_name like 'Deluxe%' 조건의 데이터

   1건을 찾아낸다.

select count(*)

from products100

where prod_name like 'Deluxe%';

 

 

 

 

연결고리가 prod_id 이므로

몇번인지 확인한다.

select prod_id

from products100

where prod_name like 'Deluxe%';

 

 

 

 

 

 

2. 1건의 prod_id 47번을 sales100 테이블에 조인 시도를 한다.

 (1 밖에 없으므로 1번만 조인시도 한다.)

select count(*)

from products100 p, sales100 s

where p.prod_id = s.prod_id

and p.prod_name like 'Deluxe%';

 

3. prod_id 47번을 sales100 테이블에서 12837건을 찾아낸다.

select count(*)

from sales100

where prod_id = 47;

 

4. prod_id 12837건을 times100 테이블로 조인 시도를 한다.

 (조인시도가 12837)

select count(*)

from products100 p, sales100 s

where p.prod_id = s.prod_id

and p.prod_name like 'Deluxe%';

 

 

 

 

5. times100 테이블로 조인 시도한 12837 중에

   t.CALENDAR_YEAR in (2000,2001) 조건에

  만족한 것만 결과로 출력된다.

 

 

나중에 배울건데,

중첩 루프 조인 -> 이중 루프문 프로그램

 loop ...........

   loop ...........

      end loop;

  end loop;

이렇게 루프문 안에 루프문 있는거.

 

연결고리를 유심히 보면서 nested loop 시킨다.

p 첫번째다.

t-p 연결시키지 않는다.

 

 

 

 

 

1.sql 튜닝이란 무엇이고 배워야 하는가?

2. 인덱스 튜닝

3. 조인 튜닝

4. 서브쿼리 튜닝

5. 기타 튜닝 방법들 소개

 

 

문제43.(어제 마지막 문제 이어서)

sales100 테이블의 prod_id 인덱스를 걸면

속도가 빨라지는지 확인하시오.

 

create index sales100_prod_id

on sales100(prod_id);

 

select /*+ leading(s t p) use_nl(t) use_nl(p) */

  p.prod_name, t.CALENDAR_YEAR, sum(s.amount_sold)

  from   sales100  s, times100  t, products100  p

  where   s.time_id = t.time_id

  and  s.prod_id = p.prod_id

    and  t.CALENDAR_YEAR in (2000,2001)

    and p.prod_name like 'Deluxe%'

    group  by  p.prod_name, t.calendar_year;

 

딱히 빨라짐

인덱스 하나 걸어보겠음

create index times100_time_id

on times100(time_id);

빨라졌습니다

 

현업에서는

where 절의 연결고리에(time_id, prod_id)

primary key 걸어서

암시작 인덱스가 걸려있어서 빠르게 돌아가게 한다.

외에 index 걸면 인덱스가 많아져서

오히려 느려진다.

 

그럼 여기서 걸어야 인덱스는

 s.time_id ,

t.time_id,

  s.prod_id,

p.prod_id

4개다

 

create index sales100_prod_id

on sales100(prod_id);

 

create index products100_prod_id

on products100(prod_id);

 

create index times100_time_id

on times100(time_id);

 

create index sales100_time_id

on sales100(time_id);

 

 

 

문제44. 아래의 sql 튜닝하시오

(조인방법은 무조건 nested loop 조인으로 하고

조인순서는 알아서 결정하고

인덱스도 알아서 생성하시오)

 

SELECT /*+ leading(c s) use_nl(s) */  COUNT(*)

     FROM sales600 s, customers600 c -----918843, 55500

     WHERE s.cust_id = c.cust_id          

     AND c.country_id = 52790  ---------------18520

     AND s.time_id BETWEEN TO_DATE('1999/01/01','YYYY/MM/DD') 

                       AND TO_DATE('1999/12/31','YYYY/MM/DD');

                                      ----------------247945

 

체크하고 주석달기:

(사실2개밖에 없어서 체크할 필요는 없음)

select count(*)

from sales600 s, customers600 c

where s.cust_id = c.cust_id;

918843

 

c.country_id = 52790 

*sales600 country_id 컬럼이 없음.

 

s.time_id BETWEEN TO_DATE('1999/01/01','YYYY/MM/DD') 

                       AND TO_DATE('1999/12/31','YYYY/MM/DD')

 

걸어야 인덱스:

create index sales600_cust_id

on sales600(cust_id);

 

create index customers600_cust_id

on customers600(cust_id);

 

 

SELECT /*+ leading(c s) use_nl(s) */  COUNT(*)

     FROM sales600 s, customers600 c

     WHERE s.cust_id = c.cust_id          

     AND c.country_id = 52790 

     AND s.time_id BETWEEN TO_DATE('1999/01/01','YYYY/MM/DD') 

                       AND TO_DATE('1999/12/31','YYYY/MM/DD');

 

 

 

문제45. 아래의 sql 조인방법은 무조건 nested loop

조인으로 하되

조인 순서를 결정하고 인덱스도 알아서 생성하시오!

 

튜닝전:

select e.ename, e.sal, d.loc, e.deptno

from emp e, dept d

where e.deptno = d.deptno

and e.job = 'SALESMAN'

and d.loc = 'CHICAGO';

 

튜닝후:

건수 카운트해서 주석처리

 

create index emp_deptno

on emp(deptno);

 

create index dept_deptno

on dept(deptno);

 

또는 dept primary key 걸어서

암시적 인덱스

alter table dept

add costraint dept_dpetno_pk primary key(deptno);

 

 

select  /*+ leading(d e) use_nl(e) */

e.ename, e.sal, d.loc, e.deptno

from emp e, dept d---------14,5

where e.deptno = d.deptno

and e.job = 'SALESMAN'--------4

and d.loc = 'CHICAGO';---------1

 

 

 

문제46. 아래의 조금더 sql 튜닝하시오

 

SELECT /*+ leading(c s) use_nl(c) */  COUNT(*)

     FROM sales600 s, customers600 c -----918843, 55500

     WHERE s.cust_id = c.cust_id          

     AND c.country_id = 52790  ---------------18520

     AND s.time_id BETWEEN TO_DATE('1999/01/01','YYYY/MM/DD') 

                       AND TO_DATE('1999/12/31','YYYY/MM/DD');

                                      ----------------247945

 

걸어야 인덱스:

연결고리절

create index sales600_cust_id

on sales600(cust_id);

 

create index customers600_cust_id

on customers600(cust_id);

 

그리고

create index customers600_country_id

on customers600(country_id);

 

create index sales600_time_id

on sales600(time_id);

 

 

문제47. 아래의 sql 올바른 hash 조인으로 변경하시오

 

 SELECT /*+ leading(c s) use_hash(c) */ 

       COUNT(*)

    FROM sales600 s, customers600 c -----918843, 55500

     WHERE s.cust_id = c.cust_id          

     AND c.country_id = 52790  ---------------18520

     AND s.time_id BETWEEN TO_DATE('1999/01/01','YYYY/MM/DD') 

                       AND TO_DATE('1999/12/31','YYYY/MM/DD');

                                      ----------------247945

 

 

 

hash 조인은 full 타게 해야 한다.

 

SELECT /*+ leading(c s) use_hash(s)  full(c) full(s) */ 

       COUNT(*)

    FROM sales600 s, customers600 c -----918843, 55500

     WHERE s.cust_id = c.cust_id          

     AND c.country_id = 52790  ---------------18520

     AND s.time_id BETWEEN TO_DATE('1999/01/01','YYYY/MM/DD') 

                       AND TO_DATE('1999/12/31','YYYY/MM/DD');

                                      ----------------247945

 

구글서칭

해시 조인(Hash-Join)은 두 테이블 중 하나를 기준으로

비트맵 해시 테이블을 메모리에 올린 후

 나머지 테이블을 스캔 하면서

해싱 테이블을 적용하여 메모리에 로딩된 테이블과 비교하여 매칭되는 데이터를 추출하는 방식의 조인이다.

RDBMS에 서 비용이 가장 많이 들어가는 Join 방법으로

 주로 작은 Table과 큰 Table 의 Join 시 사용되어 지며 , Driving 조건과 상관없이 좋은 성능을 발휘할 수 있다.

 

1. 작은 테이블(Build Input)을 읽어 Hash Area에 해시 테이블 생성한다.

(해시 함수에서 리턴 받은 버킷 주소로 찾아가 해시 체인에 엔트리를 연결)

2. 큰테이블 집합(Probe Input)을 읽어 해시 테이블을 탐색하면서 조인하는 방식이다.

(해시 함수에서 리턴 받은 버킷 주소로 찾아가 해시 체인을 스캔하면서 데이터를 찾는다)

 

 

조인 연산 비교

   Nested Loop와 Hash Joi의 비교

구분

Nested loop join

Hash join

대량의 범위

인덱스를 랜덤 액세스에 걸리는 부하가 가장 큰 문제점으로, 최악의 경우 하나의 ROW를 액세스하기 위해 Block단위로 하나하나 액세스를 해야 함.

적은 집합에 대하여 먼저 해시 값에 따른 Hash Bucket정보를 구성한 후 큰 집합을 읽어 해시 함수를 적용하여 Hash Bucket에 담기 전에 먼저 호가인해 볼 수 있기 때문에 해시조인이 효율적인 수행이 가능

대량의 자료

다량의 랜덤 액세스 수행으로 인해 수행 속도가 저하

대용량 처리의 선결조건인 ‘랜덤 액세스’와 ‘정렬’에 대한 문제 개선과 H/W의 성능 개선을 통해 각 조인 집합을 한번 스캔하여 처리하기 때문에 디스크 액세스 면에서 훨씬 효율적

 

 

 

 

 

 

 

 

해쉬조인의 원리

 

select /*+ leading(d e) use_hash(e)  */

 e.ename, d.loc

 from emp e, dept d

where e.deptno = d.deptno;

 

※해쉬조인

*해쉬테이블:  메모리로 올라가는 테이블

*탐색테이블:  메모리에 올라가지 않은 테이블

** 메모리에 올릴 없어서 

    하나만 올라가는데 충분히 빨라진다.

>>>2 테이블 작은 테이블 메모리로 올려야 한다.

    만약 테이블을 올리면 컴퓨터가 나눠서 작업함. 부하.

*****그래서

해쉬조인할 leading 순서 정해줘야 한다.******

메모리에 올려서 작업하는 테이블은

풀테이블스캔 해야 한다.

메모리를 통해 떴는데

일일이 인덱스찾아서 조인시키고 있는게

오래 걸린다.

만약

인덱스가 걸려있으면 힌트절에 풀스캔 하라고 써줄 .

                                 full(s)  이렇게.

 

*nested loop 조인은 메모리에 올라가지 않는 테이블

 

select /*+ leading(d e) use_hash(e) full(d) full(e)  */

 e.ename, d.loc

 from emp e, dept d

where e.deptno = d.deptno;

 

*leading 순서는 지킬 .  (leading(d e) )

*full scan 지시 테이블 힌트절은 순서 상관없이 써도 .

                                      (full(e) full(d) )

*hash 만큼 full sacn 힌트 넣어줄 .

 

****

적은 용량 테이블을 메모리에 올려서

해시함수적용 임시테이블을 만든다.

그걸 기반으로 용량 테이블도 풀스캔해서

서로  chain 시켜서 원하는 일치값을

출력하는게

해시함수다.

 

 

select /*+ leading(d e) use_hash(e)  full(e) full(d) */

 e.ename, d.loc

 from emp e, dept d

where e.deptno = d.deptno

and e.job = 'SALESMAN';

이렇게 조건이 붙으면

조건에서 건수가 적은게 메모리로 올라간다.

조건 salesman 돌렸더니

emp 3

      dept 4

나오면 emp 메모리에 올려서 돌리는게 빠르다.

 

 

 

문제48. 아래의 SQL HASH 조인으로 수행되게

 최대한 줄인 블럭 갯수로 검사 받으세요.

 

select /*+ leading(p s t) use_hash(p)  full(p) */

  p.prod_name, t.CALENDAR_YEAR, sum(s.amount_sold)

  from   sales100  s, times100  t, products100  p---918843, 1826, 72

  where   s.time_id = t.time_id

  and  s.prod_id = p.prod_id

    and  t.CALENDAR_YEAR in (2000,2001)----731

    and p.prod_name like 'Deluxe%'---------1

    group  by  p.prod_name, t.calendar_year;

 

 

 

원래 3 조인할 거면 연결고리를 2 설정해줘야 하는데

use_hash(p) 라고 하나만 설정해줬다.

그래서 옵티마이져가 알아서 나머지 한개 조인은

최적으로 골라서 돌린거다.

 

 

 

 

우용답:

select /*+ leading(p s t) use_hash(p) use_hash(t) full(p) full(t) */

  p.prod_name, t.CALENDAR_YEAR, sum(s.amount_sold)

  from   sales100  s, times100  t, products100  p

  where   s.time_id = t.time_id

  and  s.prod_id = p.prod_id

    and  t.CALENDAR_YEAR in (2000,2001)

    and p.prod_name like 'Deluxe%'

    group  by  p.prod_name, t.calendar_year;

 

 

1.해쉬 만큼 full .

2. leading use_hash 맞지 않는데도 불구하고

  옵티마이져가 알아서 최적으로 수행함.

 그래서 index 날라오고, nested 날라오고 그런거임.

   

 

 

select /*+ leading(p s t) use_hash(s) use_hash(t) full(p) full(t) */

  p.prod_name, t.CALENDAR_YEAR, sum(s.amount_sold)

  from   sales100  s, times100  t, products100  p---918843, 1826, 72

  where   s.time_id = t.time_id

  and  s.prod_id = p.prod_id

    and  t.CALENDAR_YEAR in (2000,2001)----731

    and p.prod_name like 'Deluxe%'---------1

    group  by  p.prod_name, t.calendar_year;

 

 

 

create index products100_name

on products100(prod_name);

 

create index sales100_prod_id

on sales100(prod_id);

 

create index products100_prod_id

on products100(prod_id);

 

create index times100_time_id

on times100(time_id);

 

create index sales100_time_id

on sales100(time_id);

해쉬조인해서 테이블 올라가는게

빨간색.

트리 해석하면

가장 오른쪽으로 들어가 있는게 먼저 시행되는 .

 

 

 

 

문제49. 아래와 같이 실행계획이 나오게 하시오

 

leading:       t s p

use_hash:   s p

full: t s p (순서 상관없음)

 

select /*+ leading(t s p) use_hash(s) use_hash(p)

            full(s) full(p) full(t) */

  p.prod_name, t.CALENDAR_YEAR, sum(s.amount_sold)

  from   sales100  s, times100  t, products100  p---918843, 1826, 72

  where   s.time_id = t.time_id

  and  s.prod_id = p.prod_id

    and  t.CALENDAR_YEAR in (2000,2001)----731

    and p.prod_name like 'Deluxe%'---------1

    group  by  p.prod_name, t.calendar_year;

 

 

 

문제50. 아래와 같이 실행계획이 나오게 하시오

 

leading: t s p

use_hash: s p

full: 전부

 

select /*+ leading(t s p) use_hash(s) use_hash(p)

            full(s) full(p) full(t) */

  p.prod_name, t.CALENDAR_YEAR, sum(s.amount_sold)

  from   sales100  s, times100  t, products100  p---918843, 1826, 72

  where   s.time_id = t.time_id

  and  s.prod_id = p.prod_id

    and  t.CALENDAR_YEAR in (2000,2001)----731

    and p.prod_name like 'Deluxe%'---------1

    group  by  p.prod_name, t.calendar_year;

 

 

select /*+ leading(t s p) use_hash(s) use_hash(p)

            full(s) full(p) full(t)

            no_swap_join_inputs(p) */

  p.prod_name, t.CALENDAR_YEAR, sum(s.amount_sold)

  from   sales100  s, times100  t, products100  p---918843, 1826, 72

  where   s.time_id = t.time_id

  and  s.prod_id = p.prod_id

    and  t.CALENDAR_YEAR in (2000,2001)----731

    and p.prod_name like 'Deluxe%'---------1

    group  by  p.prod_name, t.calendar_year;

 

P no_swap_join_inputs(p) 해줘서

문제49 처럼 p 대한 해쉬함수를 만들지 않고

Full access 한다.

 

해쉬 조인시 유용한 힌트 2가지?

1.swap_join_inputs :        해쉬 테이블을 지정하는 힌트

2. no_swap_join_inputs: 탐색 테이블을 지정하는 힌트

 

 

문제51. 아래와 같이 실행계획이 출력되게 하시오

 

3개다 테이블 엑세스

leading s t p

해쉬 p, s

 

select /*+ leading(s t p) use_hash(t) use_hash(p)

            full(s) full(p) full(t)

           */

  p.prod_name, t.CALENDAR_YEAR, sum(s.amount_sold)

  from   sales100  s, times100  t, products100  p---918843, 1826, 72

  where   s.time_id = t.time_id

  and  s.prod_id = p.prod_id

    and  t.CALENDAR_YEAR in (2000,2001)----731

    and p.prod_name like 'Deluxe%'---------1

    group  by  p.prod_name, t.calendar_year;

이거 swap 걸어도

S 해쉬함수 걸어서 t 체인탐색하고,

결과값을 가지고

P 해쉬함수 걸어서 체인탐색해서 출력.

 

 

swap 방법:

select /*+ leading(s t p) use_hash(t) use_hash(p)

           full(s) full(p) full(t)

           swap_join_inputs(p) */

  p.prod_name, t.CALENDAR_YEAR, sum(s.amount_sold)

  from   sales100  s, times100  t, products100  p

  where   s.time_id = t.time_id

  and  s.prod_id = p.prod_id

    and  t.CALENDAR_YEAR in (2000,2001)

    and p.prod_name like 'Deluxe%'

    group  by  p.prod_name, t.calendar_year;

트리뷰좀 보여줘봐

 

 

 

문제52. emp salgrade 테이블을 조인해서

이름, 월급, 급여등급(grade) 출력하시오

 

select e.emp, e.sal, s.grade

from epm e, salgrade s

 where e.sal between s.losal and hisal;

*NON EQUI JOIN

 

 

문제53. 위의 sql 실행계획을 해쉬조인으로 수행되게 하시오

 

select  /*+ leading (s e) use_hash(e) */

e.ename, e.sal, s.grade

from emp e, salgrade s----------------14,5

 where e.sal between s.losal and hisal;

해쉬조인 안된다 !

 

※해쉬조인은 조인의 연결고리가 =(이퀄) 조건일 때만 가능하다.

만약에 emp salgrade 대용량 테이블 이어서

조인 성능이 느리다면 반드시 해쉬조인을 사용해야 하는데

사용 못하는 상황이면 아래와 같이 sort merge join

수행해야 한다.

 

조인 방법 3가지

1. nested loop 조인  : use_nl

2. hash 조인          : use_hash

3. sort merge 조인   : use_merge

 

 

sort merge join 원리

 " 연결고리가 되는 컬럼의 데이터를 정렬해서

  조인하는 조인방법"

"대용량 데이터를 조인할 유리한 조인 방법"

 

 

 sort merge와 Hash join의 비교

구 분

Sort merge join

Hash join

조인이 되는 두 테이블의 크기가 다를 경우

조인이 되는 두 테이블의 크기가 다르다면 정렬되는 시간이 동일하지 않아 시간에 대한 손실이 발생

조인에 대한 알고리즘을 구현하기 때문에 두 집합의 크기가 차이가 나도 대기 시간이 발생치 않음

대용량 데이터의 경우

정렬에 대한 부담 때문에 sort merge조인은 제 기능을 발휘하지 못하는 경우가 발생. 즉 메모리 내의 지정한 정렬 영역보다 정렬할 크기가 지나치게 큰 경우 정렬할 범위가 넓어질수록 효율성을 하락

대용량 처리의 선결 조건인 ‘랜덤 액세스’와 ‘정렬’에 대한 문제 개선과 H/W의 성능 개선을 통해 각 조인 집합을 한 번 스캔하여 처리하기 때문에 디스크 액세스 면에서도 훨씬 효율적

 

 

정렬해서 찾아내려가는 있다.

 

 

문제54. 아래의 sql 작성하는데 조인 순서와 조인 방법이

 아래와 같이 되게 하시오!

조인순서: dept ---->emp ------>salgrade

조인방법:    hash조인     nl조인

 

select /*+ leading( d e s ) use_hash(e) use_nl(s)

                full(d) full(e) */

      e.deptno, e.sal, s.grade

from emp e, dept d, salgrade s----14, 5, 5

where e.deptno = d.deptno

 and

 e.sal between s.losal and s.hisal;

 

 

문제55. 아래의 sql 조인순서와 조인방법을

아래와 같이 하시오

조인순서: times100--------->sales100------->products100

조인방법:                 hash                        nl

 

select /*+ leading(t s p) use_hash(s) use_nl(p) full(t)  full(s) */

             p.prod_name, t.CALENDAR_YEAR, sum(s.amount_sold)

  from   sales100  s, times100  t, products100  p

  where   s.time_id = t.time_id

  and  s.prod_id = p.prod_id

    and  t.CALENDAR_YEAR in (2000,2001)

    and p.prod_name like 'Deluxe%'

    group  by  p.prod_name, t.calendar_year;

 

nl 조인에서 시간 많이 잡아먹은걸 확인할 있다.

이런걸 확인하고 튜닝들어가야 한다.

 

 

문제56. 아래와 같이 실행계획이 나오게 하시오

실행계획에 위와 같이 nested loop 나오면

인덱스를 통해서 조인한 결과를 메모리에 올려놓고

다음번에 똑같은 결과를 찾으러 조인할

테이블 엑세스 안하고 메모리에서 찾아서

출력하겠다는 뜻으로 11g 에서 새로 나온 기능

advanced nested loop 조인이라고 한다.

 

 select /*+ leading(p s t) use_nl(s) use_hash(t) full(t) */

         p.prod_name, t.CALENDAR_YEAR, sum(s.amount_sold)

      from   sales100  s, times100  t, products100  p

      where   s.time_id = t.time_id

      and  s.prod_id = p.prod_id

      and  t.CALENDAR_YEAR in (2000,2001)

      and p.prod_name like 'Deluxe%'

      group  by  p.prod_name, t.calendar_year;

 

 

outer join 튜닝

 

-Outer 기호( + )가 붙지 않은 테이블

   항상 Build Input 테이블로 선택된다.

 

-Leading 이나 Ordered 힌트로 그 순서를 바꿀 수 없다.

 

 

select e.ename, d.loc

 from emp e, dept d

 where e.deptno(+) =  d.deptno;

 

 

문제57. 위의 조인문의 조인순서와 조인방법을

아래와 같이 되게 하시오

조인순서: dept ---------------> emp

조인방법:         hash조인

 

select /*+ leading(d e) use_hash(e) full(e) */

  e.ename, d.loc

 from emp e, dept d

 where e.deptno(+) =  d.deptno;

 

 

 

select /*+ leading(e d) use_hash(d) full(d) */

  e.ename, d.loc

 from emp e, dept d

 where e.deptno(+) =  d.deptno;

 

 

-Outer 기호( + )가 붙지 않은 테이블

   항상 Build Input 테이블로 선택된다.

  ***bild input=>해시함수 적용되서 선스캔 되는 테이블.

 

-Leading 이나 Ordered 힌트로 그 순서를 바꿀 수 없다.

 

 

 

문제58. 아래의 아우터조인의 조인순서와 조인방법을

아래와 같이 되게 하시오

조인순서: dept ---------------> emp

조인방법:         hash조인

 

select /*+ leading(d e ) use_hash(d)  full(d) */

  e.ename, d.loc

 from emp e, dept d

 where e.deptno =  d.deptno(+) ;

근데 자꾸 emp 부터 읽는다.

설명: 아우터 조인의 조인순서는

         항상

          아우터 조인 사인이 없는 에서

                                   있는 쪽으로 조인한다.

        그리고 order, leading 으로

순서를 바꿀 없다.

 

해쉬 조인시 유용한 힌트 2가지?

1. swap_join_inputs :        해쉬 테이블을 지정하는 힌트

2. no_swap_join_inputs: 탐색 테이블을 지정하는 힌트

>>>> 힌트를 이용해서

아우터조인 있는 테이블부터

해시함수를 build input 시켜보자.

 

select /*+ leading(d e ) use_hash(d)  full(d)

           swap_join_inputs(d) */

  e.ename, d.loc

 from emp e, dept d

 where e.deptno =  d.deptno(+) ;

 

 

 

문제59. (오늘의 마지막 문제)

아래의 SQL 을 튜닝하시오 !

 

  select  t.calendar_year,  sum(s.amount_sold)

        from  sales100   s, times100  t

        where  s.time_id = t.time_id (+)

          and  t.week_ending_day_id = 1581

          group  by t.calendar_year;

 

 

select /*+ leading( t s) use_hash(s) full(s)

                swap_join_inputs(t) */

  t.calendar_year,  sum(s.amount_sold)

        from  sales100   s, times100  t

        where  s.time_id = t.time_id (+)

          and  t.week_ending_day_id = 1581

          group  by t.calendar_year;

 

create index sales100_time_id on sales100(time_id);

create index times100_time_id on times100(time_id);

create index times100_week_ending_day_id on times100(week_ending_day_id);

 

select /*+ leading (t s) use_hash(s)

swap_join_inputs(t) */

t.CALENDAR_YEAR, sum(s.amount_sold)

from sales100 s, times100 t

where s.time_id = t.time_id (+)

and t.week_ending_day_id =1581

group by t.calendar_year;

 

 

조인 튜닝(sqld 시험에 나옴)

  *조인의 방법 3가지

     -nested loop 조인 : 조인되는 거수가 얼마 안될

                               사용하는 조인 방법

                             조인되는 연결고리에 인덱스의 유무에 따라

                          성능이 크게 차이난다.

             **nested loop index 있어야 한다.

 

     -hash 조인: 조인되는 데이터의 양이 대용량

                 사용하는 조인 방법.

                인덱스를 엑세스 하기 보다  full table scan 오히려

               성능상 유리하다.

           **index 없고, full scan 해야 한다.

  힌트: select /*+ leading(d e) use_hash(e)

                  full(d) full(e)

               parallel (d 4) parallel(e 4) */

      >>>혼자서 full scan 힘드니까 4명이서 나눠서 스캔하는건데

             병렬구조(parallel) 나눠서 스캔한다.

           

 ** 명까지 분담작업 가능한가?

      >>>show parameter cpu_count

 

 

 

     최대값은  8 * 2 = 16  이다.(show parameter 2배가 최대)

16까지 이용 가능하나, 그러면 나혼자 cpu 쓰겠다는 뜻이다.

다른사람에게 피해 안가게 4정도가 적당하다.

과부하 가는 튜닝법이라 허락맞고 돌리거나 몰래 돌린다

 

12c 접속해서 병렬힌트 썼을 실행계획 확인

도스창 키고 12c 접속해서

sqlplus / as sysdba

 

alter session set container=pdborcl;

 

connect sys/oracle@localhost:1522/pdborcl as sysdba

 

alter database pdborcl open;

 

connect scott/tiger@localhost:1522/pdborcl

 

set  autot traceonly explain

 

 select /*+  leading(d e) use_hash(e)

                   full(d)  full(e)

            parallel(d 16)  parallel(e 4)  */

         e.ename,  d.loc

  from  emp  e, dept  d

  where  e.deptno = d.deptno ;

(보기 힘들면 set pages/lines)

 

 

 

문제60 예제로 활용하시오.

 설명:

  -leading 조인 순서를 결정하는 힌트

  -use_hash 메모리에서 조인하는 해시조인을 유도하는 힌트

  -full full table scan 하라고 하는 힌트

  -parallel full table scan 빠르게 하기 위해

   병렬로 작업하라.

 

   -sort merge 조인

 

4. 서브쿼리 튜닝

5. 기타 튜닝 방법들 소개

 

 

 

 

 

 

문제60. 아래의 sql 튜닝하시오

(병렬도 힌트 full 힌트 사용해서 작성하시오)

 

create table sales100

 as

  select * from sh.sales;

 

create table times100

 as

 select * from sh.times;

 

set timing on

 

set autot on

 

select /*+  leading(t s) use_hash(s)

              full(t) full(s)

              parallel(t 4) parallel(s 4) */

         t.calendar_year, sum(s.amount_sold)

 from sales100 s , times100 t

 where s.time_id = t.time_id(+)

 and t.week_ending_day_id = 1581

 group by t.calendar_year;

 

 

 

 

 

문제61. 아래의 sql 튜닝하시오

 

튜닝전:

select /*+ leading(s t) use_hash(t)

              swap_join_inputs(s) */

         t.calendar_year, sum(s.amount_sold)

 from sales100 s , times100 t-------918843, 1826

 where s.time_id = t.time_id(+)

 group by t.calendar_year;

 

 

 

 

튜닝후:

select /*+ leading(t s) use_hash(s)

             swap_join_inputs(t)

            full(t)  full(s)

            parallel(t 4)  parallel(s 4) */

         t.calendar_year,  sum(s.amount_sold)

        from  sales100   s, times100  t

        where  s.time_id  = t.time_id (+)

          group  by t.calendar_year;

 

   0  db block gets

4498  consistent gets

4433  physical reads

     

   : 00:00:00.63

   : 00:00:00.47

   : 00:00:00.45

 

아주 스몰하게 줄어듬......

우리 윈도우시스템 컴퓨터에서는

병렬구조 효과를 보지 못함.

리눅스 운영체제에서는 다름.

 

 

 

 

full outer join 튜닝

문제62.

아래의 sql 결과를 union all 구현하시오!

insert into emp (empno, ename, sal, deptno)

 values (1929, 'JACK', 4500, 70 );

 

select e.ename, d.loc

 from emp e full outer join dept d

 on (e.deptno = d.deptno) ;

 

 

 

union 구현:

select e.ename, d.loc

from emp e , dept d

where e.deptno(+) = d.deptno

union

select e.ename, d.loc

from emp e, dept d

where e.deptno = d.deptno(+);

 

*union 특징:  중복제거, 정렬

*full outer join 성능이 좋음 있다.

 

 

 

11g 얼마나 좋은지 옛날것 비교해보기.

 

*선생님이 일부러 옛날버전으로 힌트줘서

출력하게 시키신거임.

 select  /*+   optimizer_features_enable('10.2.0.1')

        opt_param('_optimizer_native_full_outer_join','off') */  e.ename, d.loc

   from  emp  e  full  outer  join   dept  d

   on (e.deptno = d.deptno );

 

emp, dept 2번씩 스캔하고 난리남.

 

힌트설명

  optimizer_features_enable('10.2.0.1')----->옵티마이저를

                                               10g 버전으로 사용하겠다.

 

 opt_param('_optimizer_native_full_outer_join','off')

                                       ---------------------------->full outer join 성능을

                                             높이는 파라미터를 끄겠다.

                 *파라미터? 프로그램 옵션같은건데

                ex) 카카오톡 알림을 무음으로 하겠다

                               같은 옵션같다.

                               오라클의 파라미터...그래서 뭔데....


 

10g 버젼에서의 튜닝후:

 select  /*+   opt_param('_optimizer_native_full_outer_join','force') */                

          e.ename, d.loc

   from  emp  e  full  outer  join   dept  d

   on (e.deptno = d.deptno );

 

 

 

 

문제63. telecom_price 테이블과 우리반 테이블을 조인해서

학생 이름, 나이, 주소, 텔레콤, month_price

출력하시오

upDate emp2

set telecom = 'sk'

where telecom = 'SK';

 

select e.ename, e.age, e.address, e.telecom,

          t.month_price

from emp2 e full outer join telecom_price t

on (e.telecom = t.telecom_name);

 

 

union 구현:

SELECT e.ename, e.age, e.address, e.telecom,

       T.month_PRICE

FROM EMP2 e , TELECOM_PRICE t

WHERE e.telecom(+) = t.telecom_name

UNION

SELECT e.ename, e.age, e.address, e.telecom,

       T.month_PRICE

FROM EMP2 e, TELECOM_PRICE t

WHERE e.telecom = t.telecom_name(+);

 

 

 

 

문제64. 위의 결과에서 통신사가 sk 학생들만 출력하시오

(조인튜닝으로 작성하시오)

select /*+ leading(t e) use_nl(e)  */

  e.ename, e.age, e.address, e.telecom,

          t.month_price

from emp2 e , telecom_price t

where e.telecom = t.telecom_name

   and e.telecom = 'sk';

!

telecom_price 테이블은 sk 1건이다.

 

:

select /*+ leading(t e) use_nl(e)  */

   e.ename, e.age, e.address, e.telecom,

          t.month_price

from emp2 e, telecom_price t

where e.telecom = t.telecom_name

and t.telecom_name  = 'sk';

 

 

 

문제65.(점심시간문제)

 서일 학생의 이름,나이,통신사,month_price

출력하는데 튜닝된 sql 작성하시오

(힌트넣으시오)

 

select /*+ leading(e t ) use_nl(t) */

  e.ename, e.age, e.telecom, t.month_price

 from emp2 e, telecom_price t

where e.telecom = t.telecom_name

 and

     e.ename = '서일';

**서일 1 뽑으니까 emp 테이블 먼저

 

 

 

expand grouping sets 레포팅함수용 힌트라서

여기서 필요 없음.

select /*+ expand_gset_to_union */

  e.ename, e.age, e.telecom, t.month_price

 from emp2 e, telecom_price t

where e.telecom = t.telecom_name

 and

     e.ename = '서일';

 

 

 

문제66. 직업이 SALESMAN 이고 부서번호가 30번인

사원의 이름, 월급, 직업, 부서위치를 출력하시오

 

create index emp_deptno

on emp(deptno);

 

create index dept_deptno

on dept(deptno);

 

select /*+ leading (d e) use_nl(e)  */

  e.ename, e.sal, e.job, d.loc

 from emp e, dept d------------14, 5

where e.deptno = d.deptno

 and e.job = 'SALESMAN'--------------EMP 4

and d.deptno  = 30;--------dept 1

 

 

 

 

문제67 풀기전에

create table bonus

 as

 select empno, sal * 1.2 as bonus

 from emp;

 

문제67. 사원이름이 ALLEN 사원의

이름, 월급, 부서위치, 보너스를 출력하시오

(조인튜닝 힌트를 사용하시오)

 

select /*+ leading(e d b) use_nl(d) use_nl(b)  */

 e.ename, e.sal, d.loc, b.bonus

from emp e, dept d, bonus b-----14, 5, 15

where e.deptno = d.deptno

and e.empno = b.empno

 and e.ename = 'ALLEN';------------1

 

연결고리에 인덱스 있어야 하고

emp테이블의  ename에도 인덱스 걸면 빨라진다.

 

++유니크 컬럼 데이터는 primary key 걸어주는게 더빠르다.

empno 에는 alter/add constraint 하는게 성능 .

 

 

ANTI JOIN

이것도 SEMI 인데
IN
아닌 NOT IN 서브 연결자로

사용하는 조인이다.

 

문제78. 관리자가 아닌 사원들의 이름을 출력하시오

 

select ename

from emp

 where empno not in (select nvl(mgr, -1)

                                         from emp

                          where mgr is not null);

 

 

 

HASH ANTI JOIN

NOT IN 사용했는데 서브쿼리가 대용량이면

HASH ANTI JOIN 쓴다.

 

                                   

select ename

from emp

 where empno not in (select  /*+ unnest  hash_aj */

                         nvl(mgr, -1)

                                         from emp

                          where mgr is not null);

**HASA ANTI 조인 쓰라고 힌트 넣어주는거

 

 

문제79. 아래의 sql 해시 안티 조인이 되게 하시오

select *

 from dept

 where deptno not in ( select

                            deptno

                                            from emp);

 

>>

select *

 from dept

 where deptno not in ( select /*+ unnest hash_aj */

                            deptno

                                            from emp);

 

 

 

 

 

문제80. 위의 sql 실행계획이 아래와 같이 되게 하시오.

select *

 from emp

 where deptno not in ( select /*+ swap_join_inputs(d)  */                               

                                                  nvl(deptno, -1)

                                            from dept d

                                           where deptno is not null)

and deptno is not null;                         

 

 

해시 안티 조인 하려면

 연결고리가 되는 컬럼에

 null 없다 조건 걸어줘야 한다.

 

해시 안티 조인은?

 세미조인처럼 완전한 조인이 아니라 절반의 조인인데

 , 메인쿼리의 테이블부터 엑세스 하고

서브쿼리의 테이블을 엑세스 하는 고정된 조인순서를

갖는 실행계획인데

in 아니라 not in 사용한 경우 실행계획이다.

 

 

 

728x90
반응형

'sql_tuning' 카테고리의 다른 글

5. 파티셔닝  (0) 2019.04.02
4. 서브쿼리 튜닝  (0) 2019.04.02
2-1. 인덱스 엑세스 방법  (0) 2019.04.02
2. 인덱스 튜닝  (0) 2019.04.02
1. sql 튜닝이란 무엇이고 왜 배워야 하는가?  (0) 2019.04.02