SQL 튜닝의 시작 (2013년)
서브쿼리에대한 기본 내용 이해하기 0 0 99,999+

by 구루비스터디 서브쿼리 subquery [2018.07.14]


서브쿼리란

  • WHERE에 사용되는 SELECT쿼리
  • 일반적으로 절차적 SQL이므로 작성이 쉬움
  • SQL 작성이 쉽다는 이유 하나만으로 서브쿼리를 남용 할 경우, DB 성능 저하가 발생할 수 있음
  • SQL에 서브쿼리가 여러 개 존재할 경우 OPTIMIZER가 COST 값을 잘 못 계산하고 부적절한 실행계획으로 실행 될 수 있음
  • 서브쿼리를 이용할 경우 성능이 좋아지는 SQL도 존재 하지만 조인으로 작성하게 되면 OPTIMIZER에게 정확한 판단을 할 수 있도록 도와 성능 문제를 예방 할 수 있음
  • 서브쿼리가 어떻게 동작하는지 잘 숙지하여 서브쿼리를 이용하여야 함. (무조건 조인이 좋다는건 아님)


서브쿼리의 사용 패턴에 대해 알아 보자


사용패턴[1]


SELECT *
  FROM emp
 WHERE sal > (SELECT avg(sal)
                FROM emp)


  • 위와 같은 서브쿼리의 결과는 반드시 1이어야 함. 한건 이상일 경우 에러 발생
  • 위의 패턴은 서브쿼리 부터 먼저 수행된 후, Main SQL의 컬럼 값과 비교하는 형태로 수행되는 것이 일반적


사용패턴[2]


SELECT c1, c2, c3
  FROM SUBQUERY_T2 t2
 WHERE c2 = 'A'
   AND EXISTS (SELECT /*+ NO_UNNEST */ 'X'
                 FROM SUBQYER_T1 tw
                WHERE t1.c5 = t2.c2)



  • EXISTS나 IN연산자(또는 NOT EXISTS, NOT IN)를 사용한 경우로 서브쿼리의 결과가 여러건 추출 될 수 있음
  • 서브쿼리 내 Main SQL과 연결조건인 T1.C5 = T2.C2가 존재(EXISTS, NOT EXTS의 경우)하여, Main SQL에서 추출한 값을 상속받아 값이 존재하는지 체크하는 방식.
  • 반대로 서브쿼리가 먼저 수행된 Main SQL에 값을 전달할 수도 있음.
  • 서브쿼리에서 추출되는 데이터가 중복 값이 많더라도, Main SQL의 데이터를 증가시키지 않음


테스트[1]. 서브쿼리의 기본적인 특성 알아보기


SELECT /*+ QB_NQME(B)*/col1
  FROM (SELECT LEVEL col1
          FROM DUAL CONNECT BY LEVEL<=3) a
 WHERE a.col1 IN (SELECT /*+ QB_NAME(A) */col1
          FROM (SELECT LEVEL col1
                  FROM DUAL CONNECT BY LEVEL <=3
                UNION ALL
                SELECT LEVEL col1
                  FROM DUAL CONNECT BY LEVEL <=3
                UNION ALL
                SELECT LEVEL col1
                  FROM DUAL CONNECT BY LEVEL <=3 ) )
---------------------------------------------------------------------------------------------  
| Id  | Operation                        | Name     | Rows  | Bytes | Cost (%CPU)| Time     |  
---------------------------------------------------------------------------------------------  
|   0 | SELECT STATEMENT                 |          |     1 |    26 |     9  (12)| 00:00:01 |  
|*  1 |  HASH JOIN SEMI                  |          |     1 |    26 |     9  (12)| 00:00:01 |  
|   2 |   VIEW                           |          |     1 |    13 |     2   (0)| 00:00:01 |  
|*  3 |    CONNECT BY WITHOUT FILTERING  |          |       |       |            |          |  
|   4 |     FAST DUAL                    |          |     1 |       |     2   (0)| 00:00:01 |  
|   5 |   VIEW                           | VW_NSO_1 |     3 |    39 |     6   (0)| 00:00:01 |  
|   6 |    VIEW                          |          |     3 |    39 |     6   (0)| 00:00:01 | 
|   7 |     UNION-ALL                    |          |       |       |            |          | 
|*  8 |      CONNECT BY WITHOUT FILTERING|          |       |       |            |          | 
|   9 |       FAST DUAL                  |          |     1 |       |     2   (0)| 00:00:01 | 
|* 10 |      CONNECT BY WITHOUT FILTERING|          |       |       |            |          | 
|  11 |       FAST DUAL                  |          |     1 |       |     2   (0)| 00:00:01 | 
|* 12 |      CONNECT BY WITHOUT FILTERING|          |       |       |            |          | 
|  13 |       FAST DUAL                  |          |     1 |       |     2   (0)| 00:00:01 | 
---------------------------------------------------------------------------------------------  


QUERY BLOCK A의 결과 값
COL1
1
2
3
1
2
3
1
2
3


QUERY BLOCK B의 결과 값
COL1
1
2
3


  • 위의 조건을 보면 9건이 추출(서브쿼리가 9건 이므로)될꺼라고 생각하나 결과는 3건만 추출됨 --> 서브쿼리에서는 데이터가 UNIQUE 처리되므로 3건만 처리.
  • 위의 이유로 조인을 서브쿼리로 변경시 유의해야 함.


테스트[2]. 서브쿼리를 조인 (뷰)으로 수행되도록 유도


SELECT /*+ ORDERED QB_NQME(B)*/col1
  FROM (SELECT LEVEL col1
          FROM DUAL CONNECT BY LEVEL<=3) a
 WHERE a.col1 IN (SELECT /*+ QB_NAME(A) */col1
          FROM (SELECT LEVEL col1
                  FROM DUAL CONNECT BY LEVEL <=3
                UNION ALL
                SELECT LEVEL col1
                  FROM DUAL CONNECT BY LEVEL <=3
                UNION ALL
                SELECT LEVEL col1
                  FROM DUAL CONNECT BY LEVEL <=3 ) ) ;
 
----------------------------------------------------------------------------------------------
| Id  | Operation                         | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                  |          |     1 |    26 |    10  (20)| 00:00:01 |
|*  1 |  HASH JOIN                        |          |     1 |    26 |    10  (20)| 00:00:01 |
|   2 |   VIEW                            | VW_NSO_1 |     3 |    39 |     6   (0)| 00:00:01 |
|   3 |    HASH UNIQUE                    |          |     1 |    39 |            |          |
|   4 |     VIEW                          |          |     3 |    39 |     6   (0)| 00:00:01 |
|   5 |      UNION-ALL                    |          |       |       |            |          |
|*  6 |       CONNECT BY WITHOUT FILTERING|          |       |       |            |          |
|   7 |        FAST DUAL                  |          |     1 |       |     2   (0)| 00:00:01 |
|*  8 |       CONNECT BY WITHOUT FILTERING|          |       |       |            |          |
|   9 |        FAST DUAL                  |          |     1 |       |     2   (0)| 00:00:01 |
|* 10 |       CONNECT BY WITHOUT FILTERING|          |       |       |            |          |
|  11 |        FAST DUAL                  |          |     1 |       |     2   (0)| 00:00:01 |
|  12 |   VIEW                            |          |     1 |    13 |     2   (0)| 00:00:01 |
|* 13 |    CONNECT BY WITHOUT FILTERING   |          |       |       |            |          |
|  14 |     FAST DUAL                     |          |     1 |       |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------- 


  • ID 2를 확인하면 VIEW라는 오퍼레이션이 존재 하여 뷰로 변경되었음
  • ID 3번의 HASH UNIQUE 오퍼레이션을 통해 중복값을 제거
"데이터베이스 스터디모임" 에서 2013년에 "SQL튜닝의시작 " 도서를 스터디하면서 정리한 내용 입니다.

- 강좌 URL : http://www.gurubee.net/lecture/3780

- 구루비 강좌는 개인의 학습용으로만 사용 할 수 있으며, 다른 웹 페이지에 게재할 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^^

- 구루비 강좌는 서비스 제공을 위한 목적이나, 학원 홍보, 수익을 얻기 위한 용도로 사용 할 수 없습니다.

댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입