일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 팀스파르타
- 데이터분석
- difftime
- 그래프시각화
- 정보획득량
- 불순도제거
- Dense_Rank
- sqld
- 회귀분석 알고리즘
- %in%
- count
- 여러 데이터 검색
- 데이터분석가
- if문 작성법
- 순위출력
- 빅데이터분석
- 빅데이터
- 회귀분석
- 총과 카드만들기
- 단순회귀 분석
- Sum
- 막대그래프
- merge
- 그래프 생성 문법
- loop 문
- sql
- 히스토그램 그리기
- 상관관계
- max
- Intersect
- Today
- Total
ch0nny_log
[빅데이터분석] SQL 튜닝_TIL 28 본문
[TIL 28] 240624
★ 점심시간문제
emp와 dept와 bonus 테이블을 조인하여 이름과 월급과 부서위치와 comm2를 출력하는데 다음과 같이 조인순서가 되게 하시오
조인순서: emp --> dept ---> bonus
조인방법: hash join hash join
해쉬 테이블 : emp와 dept
select /*+ leading(e, d,b) use_hash(d) use_hash(b) swap_join_inputs(b) */ e.ename, e.sal, d.loc,b.comm2
from emp e, dept d, bonus b
where e.deptno = d.deptno
and e.empno = b.empno;
select * from table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
30. 선택적 조인
예제 30. 선택적 조인을 하면서 조인의 성능을 높일 수 있다.
-- 환경구성 @demo -- emp 테이블에 emp_kind 라는 컬럼을 추가하고 default값을 1로 해서 추가함. -- default 값을 1로 주겠다는 것은 emp_kind 값을 입력안하면 무조건 1로 값이 할당되는 것 alter table emp add emp_kind varchar2(1) default 1 not null; -- 사원번호가 홀수이면 정규직으로 하고 짝수면 비정규직으로 emp_kind 수정 update emp set emp_kind = case when mod(empno,2) = 1 then 1 else 2 end ; select ename, emp_kind from emp; --- emp_kind 가 1이면 정규직 , emp_kind 가 2이면 비정규직 -- 정규직 사원 테이블 생성 create table emp_kind1 as select empno, ename, sal + 200 as office_sal from emp where emp_kind ='1'; -- 비정규직 사원 테이블 생성 create table emp_kind2 as select empno, ename, sal + 200 as sal from emp where emp_kind ='2'; -- primary key 제약을 각각 건다 alter table emp_kind1 add constraint pk_emp_kind1 primary key(empno); alter table emp_kind2 add constraint pk_emp_kind2 primary key(empno); -- emp테이블에서 sal 컬럼을 삭제함 alter table emp drop column sal ;
문제 1. emp와 emp_kind1과 emp_kind2 테이블을 조인해서 사원번호가 7389인 사원의 사원번호, 이름, 정규직 사원 월급(office_sal), 비정규직 월급(sal)을 출력하시오.
select e.empno, e.ename, k1.office_sal, k2.sal from emp e , emp_kind1 k1 ,emp_kind2 k2 where e.empno=k1.empno (+) and e.empno= k2.empno(+) and e.empno=7902;
설명: 왜 위의 sql에서는 outer join 사인이 필요한가? (사인을 안 쓰면 출력이 안됨)
-> 7839 사원이 정규직일지 비정규직일지 모르기 때문에 꼭 써야 됨
실행계획을 보면 비정규직 사원 테이블과도 조인을 해서 버퍼의 개수가 1 이상으로 보임
그래서 위의 sql에 정규직 사원번호가 들어오면 비정규직 테이블하고는 조인되지 않게 하고 비 정규직 사원번호가 들어오면 정규직 사원 테이블하고는 조인되지 않게 하려면 선택적 조인을 하면 됨
★문제 2. 위의 sql을 선택적 조인(상호 배타적 관계의 조인)을 하게 하시오.
select e.empno, e.ename, k1.office_sal, k2.sal from emp e , emp_kind1 k1 ,emp_kind2 k2 where decode (e.emp_kind, 1, e.empno) = k1.empno (+) and decode (e.emp_kind, 2, e.empno) = k2.empno(+) and e.empno=7839; select * from table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
★문제 3. 아래의 환경을 만들고 아래 SQL을 상호 배타적 관계 조인이 되게 튜닝하시오
@demo drop table dept_10; drop table dept_20; drop table dept_30; create table dept_10 as select empno, sal*0.1 bonus_10 from emp where deptno = 10; create table dept_20 as select empno, sal*0.2 bonus_20 from emp where deptno = 20; create table dept_30 as select empno, sal*0.3 bonus_30 from emp where deptno = 30; alter table dept_10 add constraint dept_10_pk primary key(empno); alter table dept_20 add constraint dept_20_pk primary key(empno); alter table dept_30 add constraint dept_30_pk primary key(empno); 1) 튜닝전 select e.empno, e.ename, d1.bonus_10, d2.bonus_20, d3.bonus_30 from emp e, dept_10 d1, dept_20 d2, dept_30 d3 where e.empno = d1.empno (+) and e.empno = d2.empno (+) and e.empno = d3.empno (+) and e.empno = 7788;
2) 튜닝후 select e.empno, e.ename, d1.bonus_10, d2.bonus_20, d3.bonus_30 from emp e, dept_10 d1, dept_20 d2, dept_30 d3 where decode( e.deptno, 10,e.empno) = d1.empno (+) and decode( e.deptno,20,e.empno) = d2.empno (+) and decode( e.deptno,30,e.empno) = d3.empno (+) and e.empno = 7788; select * from table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
설명: 사원번호가 7788번인 사원은 부서번호가 20번이므로 조인연결 조건 중에 decode(e.deptno,20, empno) =d2.empno(+)만 작동이 되고 나머지는 0이 됨.
31. 인라인 조인 해체 되지 않게 하시오. ( no merge )
예제 31. 인라인 뷰와 조인할 때 인라이뉴가 해체되지 않게 하시오. (no merge)
설명: 위의 sql의 조인 순서는 salgrade -> in line view 순이야 하는데 query transformer가 in line view를 아래와 같이 해체해 버림
select e.ename, 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 ;
-- in line view 를 해체하지 못하게 하시오 ! select /*+ no_merge(v) */ v.ename, v.loc, s.grade from salgrade s, ( select /*+ no_merge */ e.ename, e.sal, d.loc from emp e, dept d where e.deptno = d.deptno ) v where v.sal between s.losal and s.hisal ; -- 위 2개 /*+ no_merge(v) */ 중 1개만 써도 됨
1. no_merge 힌트 : 인라인 뷰나 뷰를 해체하지 말아라 ! 2. merge : 인라인 뷰나 뷰를 해체해라 !
문제1. 아래의 sql의 inline view가 해체되지 않도록 힌트를 주시오.
1) 튜닝전 select v.ename, v.sal, v.grade, d.loc from dept d, (select e.ename, e.sal, s.grade, e.deptno from emp e , salgrade s where sal between s.losal and s.hisal) v where v.deptno=d.deptno; select * from table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST')); 2) 튜닝후 select /*+ no_merge(v) */v.ename, v.sal, v.grade, d.loc from dept d, (select e.ename, e.sal, s.grade, e.deptno from emp e , salgrade s where sal between s.losal and s.hisal) v where v.deptno=d.deptno; select * from table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
32. 조인 순서 조
예제 32. 뷰와 조인을 할 대 조인 순서를 조정할 수 있다.
-- 환경설정 create view emp_dept as select e.ename, e.sal, d.loc, e.deptno from emp e ,dept d where e.deptno =d.deptno;
Q1. emp_dept 뷰와 salgrade 테이블과 조인을 해서 이름, 월급, 부서위치, 급여등급을 출력하시오.1) 튜닝전 -- 뷰가 해체됨 select v.ename, v.sal, v.loc, s.grade from emp_dept v, salgrade s where v.sal between s.losal and s.hisal;
2) 튜닝후 select /*+ no_merge(v) */v.ename, v.sal, v.loc, s.grade from emp_dept v, salgrade s where v.sal between s.losal and s.hisal; select * from table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
Q2. 의 조인 순서를 아래와 같이되게 하시오.
조인순서: 인라인뷰 v ----> salgrade
조인방법: nested loop 조인select /*+ no_merge(v) leading(v,s) use_nl(s)*/v.ename, v.sal, v.loc, s.grade from emp_dept v, salgrade s where v.sal between s.losal and s.hisal; select * from table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
설명 : 위의 실행계획이 나오려면 인라인 뷰를 해체하지 않게 no_merge 힌트를 먼저 사용해줘야됨.
★ Q3. 위의 sql의 조인순서를 변경하는데 emp_dept뷰 안의 조인순서가 emp -> dept 순이 되게 하고 조인방법은 nested loop 조인이 되게하시오.select /*+ no_merge(v) leading(v,s) use_nl(s) leading(v.e,v.d) use_nl(v.d)*/v.ename, v.sal, v.loc, s.grade from emp_dept v, salgrade s where v.sal between s.losal and s.hisal; select * from table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
33. MVIEW
예제 33. 조인 성능을 높이고 싶다면 MVIEW 생성을 고려하시오.
일반 VIEW는 데이터를 저장하고 있지 않은데 materialize view 는 데이터를 저장하는 view 입니다.
Q1. 아래와 같이 VIEW를 생성하시오.
create view emp_dept2 as select d.loc, sum(e.sal) as sumsal from emp e , dept d where e.deptno=d.deptno group by d.loc; select * from emp_dept2;
설명: view는 데이터를 저장하지 않고 바라만 봄.
Q2. emp_dept3로 위의 결과를 담아내는 MVIEW를 생성하시오.create materialized view emp_dept3 as select d.loc, sum(e.sal) as sumsal from emp e , dept d where e.deptno=d.deptno group by d.loc;
설명: emp_dept3 mview를 만들기 위해 20분이 걸렸다고 가정해봅시다.
그러면 앞으로 emp_dept3 를 조회할때는 20분이 걸리지 않고 3건밖에 안되기 때문에 금방조회됩니다.
왜냐면 3건의 데이터를 저장하고 있기 때문입니다.
그러나 emp_dept2 같은 경우는 매번 select * from emp_dept2; 를할 때마다 20분이 걸릴겁니다.
왜냐면 결과 데이터가 저장되지 않기 때문입니다. 그래서 매번 조인해야합니다.
문제 1. emp와 dept, salgrade를 조인해서 dallas에서 근무하는 사원들의 이름,월급,부서위치,급여등급을 출력하는 쿼리문이 20분 이상 돈다고 가정하고 이 결과를 빠르게 보기 위해서 mview생성하시오. mview는 emp_dept_salgrade로 하시오
1) MARERIALIZED VIEW - buffer 2개
create materialized view emp_dept_salgrade as 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 and d.loc='DALLAS';
2. 일반 VIEW - buffer 20개
create view emp_dept_salgrade2 as 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 and d.loc='DALLAS'; select * from emp_dept_salgrade2;
설명: 일반 뷰는 데이터를 저장하고 있지 않으므로 다시 조인합니다. 그래서 20개의 버퍼를 읽어들이고 있습니다.
문제 2. 다음과 같이 일반뷰인 emp_dept_salgrade2를 drop 하고 다음과 같이 rewrite 힌트를 써서 아래의 조인문의 실행계획을 확인하시오.
설명: 위와 같이 하면 조인을 하지 않고 mview의 데이터를 읽습니다.drop view emp_dept_salgrade2; create materialized view emp_dept_salgrade3 enable query rewrite as 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 and d.loc='DALLAS';
문제 3. 다음과 같이 mveiw 를 생성하시오 !
create materialized view dept_sal build immediate -- mview 생성과 동시에 결과 데이터로 생성되는 옵션 refresh complete on demand -- mview 와 테이블간의 데이터 동기화를 요구할 때만 동기화 enable query rewrite -- 아래의 쿼리무ㄴ만 수행해도 mview의 데이터를 가져오게 함 as select d.dname, sum(e.sal) as sumsal from emp e, dept d where e.deptno = d.deptno group by d.dname; select * from dept_sal; select * from table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST')); select d.dname, sum(e.sal) as sumsal from emp e, dept d where e.deptno = d.deptno group by d.dname; select * from table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
※ 1. rewrite 힌트 : mview 에서 데이터를 가져와라 !
2. no_rewrite 힌트: 테이블에서 데이터를 가져와라 !
문제 4. 다음의 sql을 mview로 만들고 query rewrite가 될 수 있도록 하시오.(view 이름 emp_mview75)
create materialized view emp_mview75 enable query rewrite as select e.ename, d.loc, e.sal from emp e, dept d where e.deptno = d.deptno; select * from emp_mview75; select * from table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
34. 데이터가 작은 서브쿼리 수행
예제 34. 서브쿼리문에서 서브쿼리의 데이터가 작으면 서브쿼리부터 수행되게 해야해요.
Q1. JONES 보다 더 많은 월급을 받은 사원들의 이름과 월급을 출력하시오.
select ename, sal from emp where sal > (select sal from emp where ename='JONES');
- 서브쿼리부터 수행되는게 성능에 더 좋음(서브쿼리의 데이터가 1건이기 떄문)
Q2. 위의 SQL의 실행계획을 확인해서 서브쿼리부터 수행했는지 확인하시오.select ename, sal from emp where sal > (select sal from emp where ename='JONES'); select * from table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
실행계획을 보면 서브쿼리를 먼저 수행하고 나서 메인쿼리를 수행햇습니다.
Q3. 똑같은 결과가 나오는거면 조인이 더 속도가 빠른가요? 아니면 서브쿼리가 더 빠른가요 ? (증명하시오)1) JOIN 전 select ename, sal from emp where deptno = ( select deptno from dept where loc='DALLAS'); 2) JOIN 후 select e.ename, e.sal from emp e, dept d where e.deptno = d.deptno and d.loc='DALLAS';
-> 두개중에 어떤게 빠르디고 얘기할 수 없음 (왜냐하면 나는 서브쿼리 수행했지만 오라클이 조인으로 변경해서 수행할 수 있기 때문입니다. )
문제 1.아래의 sql의 실행계획을 확인해서 서브쿼리를 먼저 수행했는지 메인쿼리를 먼저수행했는지 확인하시오.
select ename, sal from emp where deptno = ( select deptno from dept where loc='DALLAS'); select * from table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
문제 2. 아래의 sql의 실행계획을 확인하시오.
select ename, sal from emp where deptno in ( select deptno from dept where loc='DALLAS'); select * from table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
설명: =을 in으로 변경했더니 join으로 수행 (서브쿼리로 수행하려면 in 이 아니라 =로 작성해야함)
문제 3. . 아래의 SQL이 메인 쿼리부터 수행되게하시오
select /*+ gather_plan_statistics */ ename, sal from emp where deptno = ( select deptno from dept where loc='DALLAS' );
답: select /*+ gather_plan_statistics */ ename, sal from emp where deptno = ( select /*+ no_push_subq */ deptno from dept where loc='DALLAS' );
※ 1. push_subq : 서브쿼리부터 수행해라 ~
2. no_push_subq : 메인 쿼리부터 수행해라 ~
문제 4. 위의 sql 을 서브쿼리부터 수행되게 튜닝하시오.
select /*+ gather_plan_statistics */ ename, sal from emp where deptno = ( select /*+ push_subq */ deptno from dept where loc='DALLAS' );
35. push_subq
예제 35. push_subq 와 no_push_subq 와의 짝꿍힌트를 알아야 해요 !
: push_subq 와 no_push_subq 와의 짝꿍힌트는 바로 no_unnest 힌트 입니다.
1. no_unnest : 조인으로 풀지 말고 서브쿼리로 수행해라 !
2. unnest : 서브쿼리를 조인으로 수행해라 !
no_unnest 를 써서 서브쿼리로 실행되게 해야 서브쿼리부터 수행되게 하라고 하는 push_subq힌트가 의미가 있고 no_push_subq를 써서 메인 쿼리부터 수행되게해라라는 의미가 있는 것임.
문제 1. 아래의 sql이 서브쿼리부터 수행되게 하시오.
select ename, sal from emp where deptno in(select deptno from dept where loc ='NEW YORK');
답) select /*+ gather_plan_statistics */ ename, sal from emp where deptno in ( select /*+ no_unnest push_subq */ deptno from dept where loc = 'NEW YORK' );
문제 2. 위의 sql을 메인쿼리부터 수행되게 하시오.
select ename, sal from emp where deptno in ( select /*+ no_unnest no_push_subq */ deptno from dept where loc = 'NEW YORK' );
문제 3. 아래 sql을 튜닝하시오.
튜닝전: select count(*) from sales200 where cust_id in ( select /*+ no_unnest no_push_subq */ cust_id from customers200 where cust_first_name='Abel' ); 튜닝후: select count(*) from sales200 where cust_id in ( select /*+ no_unnest push_subq */ cust_id from customers200 where cust_first_name='Abel' ); 튜닝후도 40초 넘게 걸리지만 main query 부터 수행하는것보다는 빠르게 결과가 출력이 됩니다.
위와 같이 튜닝 전이나 튜닝후나 결과가 느리게 출력된다면 서브쿼리로 수행하면 안되고 세미 조인으로 수행되게 해줘야됨.
36. semi join
예제 36. 서브쿼리를 세미조인으로 변경해서 수행되게 하세요 !
:서브쿼리 문장을 튜닝할 때 push_subq 와 no_push_subq 힌트로는 튜닝이 안될때 세미조인을 사용해야함
세미 조인은 기본적으로 메인쿼리의 테이블부터 먼저 드라이빙됨. 그렇지만 조인이기 때문에 조인방법중 아주 강력한 해쉬조인을 사용할 수 있음.
문제1. 아래 sql을 튜닝하시오.
1) 튜닝전 select count(*) from sales200 where cust_id in (select /*+ no_unnest push_suqb */ cust_id from customers200 where cust_first_name='Abel'); 2) 튜닝후 select count(*) from sales200 where cust_id in (select /*+ unnest hash_sj */ cust_id from customers200 where cust_first_name='Abel');
※ unnest힌트는 서브쿼리를 조인으로 수행해라 라는 힌트이고 세미조인의 종류가 3가지임
1. nl_sj nested loop semi 조인 ★ 2. hash_sj hash semi 조인 3. merger_sj sort merge semi 조인
문제 2. 아래 sql의 실행계획이 hash semi 조인이 되 튜닝하시오.
1) 튜닝전 select ename, sal from emp where deptno in(select /*+ no_unnest no_push_subq */ deptno from dept where loc ='DALLAS');
2) 튜닝후 select ename, sal from emp where deptno in(select /*+ unnest hash_sj */ deptno from dept where loc ='DALLAS');
37. swap_join_inputs
예제 37. 해쉬 세미 조인도 서브쿼리부터 수행되게 할 수 있다.
해쉬 세미조인은 원래 메인쿼리의 테이블을 해쉬 테이블로 구성할 수 밖에 없는거였는데 swap_join_inputs 힌트가 나오면서 서브쿼리의 테이블도 해쉬 테이블로 구성할 수 있게 되었습니다.
Q1. 아래의 sql을 해쉬 세미조인이 되게하는데 서브쿼리의 테이블이 해쉬 테이블이 되게하시오.1) 튜닝전 select ename, sal from emp where deptno in (select deptno from dept where loc ='DALLAS'); select * from table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
2) 튜닝후 select ename, sal from emp where deptno in (select /*+ unnest hash_sj swap_join_inputs(dept) */ deptno from dept where loc ='DALLAS'); select * from table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
문제1. 아래의 sql을 해쉬 세미조인이 되게하는데 서브쿼리의 테이블이 해쉬 테이블이 되게하시오.
1) 튜닝전 select count(*) from sales200 where cust_id in (select /*+ unnest hash_sj */ cust_id from customers200 where cust_first_name='Abel');
2) 튜닝후 select count(*) from sales200 where cust_id in (select /*+ unnest hash_sj swap_join_inputs( customers200) */ cust_id from customers200 where cust_first_name='Abel');
★ 마지막문제. 아래의 sql을 해쉬 세미조인이 되게 튜닝하시오. 작은 테이블이 해쉬테이블이 되게하시오.
1) 튜닝전 select count(*) from customers200 where cust_id in ( select /*+ no_unnest push_subq */ cust_id from sales200 where amount_sold between 1 and 10000); 2) 튜닝후 select count(*) from customers200 where cust_id in ( select /*+ unnest hash_sj swap_join_inputs(sales200) */ cust_id from sales200 where amount_sold between 1 and 10000); 답) select count(*) from customers200 where cust_id in ( select /*+ unnest hash_sj swap_join_inputs(customers200) */ cust_id from sales200 where amount_sold between 1 and 10000);
'빅데이터 분석(with 아이티윌) > SQL' 카테고리의 다른 글
[빅데이터분석] SQL 튜닝 문제_TIL 30 (0) | 2024.06.26 |
---|---|
[빅데이터분석] SQL 튜닝_TIL 29 (0) | 2024.06.25 |
[빅데이터분석] SQL 튜닝_TIL 27 (0) | 2024.06.20 |
[빅데이터분석] SQL 튜닝_TIL 26 (0) | 2024.06.19 |
[빅데이터분석] SQL 튜닝_TIL 25 (1) | 2024.06.18 |