조인 제거란 

 - 1:M 관계인 두 테이블을 조인하는 쿼리문에서 조인문을 제외한 어디에서도 1쪽 테이블을 

   참조하지 않는다면, 쿼리 수행 시 1쪽 테이블은 읽지 않아도 결과집합에 영향을 미치지 

   않기 때문에 옵티마이저는 이 특성을 이용해 M쪽 테이블만 읽도록 쿼리를 변환하는데, 

   이를 '조인 제거' 또는 '테이블 제거'라고 한다. 

 - 이러한 쿼리 변환은 오라클의 경우 10g 부터 작동하기 시작했지만 SQL Server 등에서는 

   이미 오래 전부터 적용돼 온 기능이다. 

 - 이 기능을 제어하는 파라미터는 아래와 같고, 힌트를 통해 쿼리 레벨에서 제어할 수도 있다. 

     ● eliminate_join, no_eliminate_join 

 - 조인 제거 기능이 작동하려면 아래와 같이 PK 와 FK 제약이 설정돼 있어야만 한다. 

 - FK가 설정 되어 있더라도 FK 쪽 컬럼이 null 허용 컬럼이면 결과가 틀리게 될 수 있다. 

   조인 컬럼 값이 null인 레코드는 조인에 실패해야 정상인데, 옵티마이저가 조인문을 

   함부로 제거하면 그 레코드들이 결과집합에 포함되기 때문이다. 그런 오류를 방지하기 위해

   옵티마이저가 내부적으로 e.deptno is not null 조건을 추가해 준다. 

 - 11g부터 PK와 FK 제약이 설정돼 있으면 아래와 같은 in 또는 exists 서브쿼리도 일반 조인문

   처럼 제거된다. 

   ( _optimizer_join_elimination_enabled 파라미터에 의해 영향을 받고, 

     eliminate_join, no_eliminate_join 힌트를 통한 제어도 가능 )  

조건절 이행이란 

 - 이 쿼리 변환을 한마디로 요약하면, [ (A=B) 이고 (A=C) 이다 ]는 추론을 통해 새로운 조건절을 

   내부적으로 생성해 주는 쿼리변환이다. [(A>B) 이고 (B>C)이면 (A>C)이다]와 같은 추론도 

   가능하다. 

 - 새로운 필터 조건이 추가되면서 조인 조건이 사라진다. 새로운 필터 조건으로 인하여 

   조인 조건이 필요가 없어졌다고 생각하고 옵티마이저가 중복 산정하는 것을 방지하기 위해 

   그렇게 처리하는 것이다. 

 - 9i에서는 사용자가 의도적으로 조인문을 한 버 기술하면 이 조인문이 다시 나타났는데, 

   10g부터는 아무리 여러 번 기술하더라도 그런 현상이 생기지 않는다. 

 - 만약 조건절 이행이 작용해 조인 조건이 사라지고 이로 인해 비용이 잘못 계산되는 문제가 

   생긴다면, 사용자가 명시적으로 새로운 필터조건을 추가하거나 조인문을 가공하는 방법을 

   사용해 볼 수 있다.  그러면 조건절이 사라지지 않고 그대로 남게 되며, 계산된 비용도 

   달라진다. 

      조인문 가공 방법 예시 ) d.deptno = e.deptno + 0


조건절 이행이 효과를 발휘하는 사례 

 - 내부적으로 조건절 이행이 여러 곳에서 일어나고 있다. 

 - 선분 이력을 between 조건으로 조회할 때는 인덱스 구성과 검색 범위에 따라 

   인덱스 스캔 효율에 많은 차이가 생긴다. 그에 따라 아래와 같은 쿼리에서도 범위를 

   더 제한적으로 줄일 수 있다. 

    SQL 예시) 

       select * 

        from 상품이력 a. 주문 b 

      where b.거래일자 between '20090101' and '20090131'

         and a.상품번호 = b.상품번호 

         and b.거래일자 between a.시작일자 and a.종료일자 

 - 위의 쿼리에서는 아래와 같이 조건절을 명시적으로 추가해 줌으로써 SQL을 튜닝하곤 했다. 

     ● 상품이력, 시작일자 <= '20090131' 

     ● 상품이력, 종료일자 >= '20090101'

 - 하지만 9i 부터는 옵티마이저가 이들 조건을 묵시적으로 추가하고 최적화를 수행한다. 

   ( 8i 버전을 사용하고 있다면 위와 같은 조건절을 명시적으로 추가해 주는 튜닝 기법이 

     여전히 유효하다 )


튜닝사례 1

 - 조인 조건은 아니지만 커럶 간 관계 정보를 통해 추가적인 조건절이 생성되었다. 

   옵티마이저에게 많은 정보를 제공할수록 SQL 성능이 더 좋아진다. 

   ( ip 사례 ) 


튜닝사례 2 

 - 최적의 조인순서를 결정하고 그 순서에 따라 조인문을 기술해주는 것이 매우 중요 

'ORACLE > SQLP' 카테고리의 다른 글

성능고도화 4-7. Or-expansion  (0) 2016.12.26
성능고도화 4-6. 조인제거  (0) 2016.12.26
성능고도화 4-4. 조건절 Pushing  (0) 2016.12.26
성능고도화 4-3. 뷰 Merging  (0) 2016.12.26
성능고도화 4-2 서브쿼리 Unnesting  (1) 2016.12.26

조건절 Pushing 

 - 뷰를 엑세스하는 쿼리를 최적화할 때 옵티마이저는 1차적으로 뷰 Merging을 고려한다. 

   하지만 아래와 같은 이유로 뷰 Merging에 실해할 수 있다. 

     ●  복합 뷰 Merging 기능이 비활성화 

     ● 사용자가 no_merge 힌트 사용 

     ● Non-mergeable views : 뷰 Merging 시행하면 부정확한 결과 가능성 

     ● 비용기반 쿼리 변환이 작동해 No Merging 선택 


조건절 Pushing 종류 

  ● 조건절 : 쿼리 블록 밖에 잇는 조건들을 쿼리 블록 안쪽으로 밀어 넣는 것을 말함 

  ● 조건절 pullup : 쿼리 블록 안에 있는 조건들을 쿼리 블록 밖으로 내오는 것을 말하며, 

                           그것을 다시 다른 쿼리 블록에 Pushdown 하는 데 사용함 

  ● 조인 조건 Pushdown : NL조인 수행 중에 드라이빙 테이블에서 읽은 값을 건건이 inner 쪽 

                                    뷰 쿼리 블록 안으로 밀어 넣는 것을 말함 

 

관련 힌트와 파라미터 

 - 조건절 Pushdown 과 Pullup은 항상 더 나은 성능을 보장하므로 별도의 힌트를 

   제공하지 않는다. 하지만, 조인 조건 Pushdown은 NL 조인을 전체로 하기 때문에 

   성능이 더 나빠질 수도 있다. 

 - 따라서 오라클은 조인 조건 Pushdown을 제어할 수 있도록 

   push_pred 와 no_push_pred 힌트를 제공한다.

 - 조인 조건 pushdown 기능이 10g에서 비용기반 쿼리 변환으로 바뀌었고, 이 때문에 

   9i에서 빠르게 수행되던 쿼리가 10g로 이행하면서 오히려 느려지는 현상이 종종 나타나고 있다.

  이때는 문제가 되는 쿼리 레벨에서 힌트를 이용해 파라미터를 false로 변경하면 된다.   

 - 10g에서 시스템 환경에 따라 이 기능이 문제를 일으켜 쿼리 결과가 틀리는 문제도 발생하는

   것으로 보고되고 있는데, 그때는 패치를 적용하거나 시스템 레벨 변경이 불가피하다. 


Non-pushable view 

 - 뷰안에 rownum, 분석함수를 사용하면 Non-mergeable, Non-pushable view가 되므로 

    주의해야 한다.


조건절 Pushdown 

 * 나만의 요약 정리 

   조건절 push down은 조인 조건을 통해서 한쪽테이블의 조인 조건인 커럶에 조건이 따로 

   붙을 때 해당 조건이 조인 조건을 타고 들어가서 다른 테이블에도 영향을 주는 것 

   (기본적으로 바깥에서 안으로 들어간다. ) 


GROUP BY 절을 포함한 뷰에 대한 조건절 Pushdown 

 - group by 절을 포함한 복합 뷰 Merging에 실패했을 때, 쿼리 블록 밖에 있는 조건절을 

   쿼리 블록 안쪽에 밀어 넣을 수 있다면 group by 해야할 데이터량을 줄일 수 있다. 

   인덱스 상황에 따라서 효과적인 인덱스 선택이 가능해지기도 한다. 

 

UNION 집합 연산자를 포함한 뷰에 대한 조건덜 Pushdown 

 * 나만의 요약정리

   이것도 기본적으로 바깥에서 안으로 적용 되는데 기본적인 것은 위의 조건절 Pushdown과 

   같지만 특이한 점이 있는 것은 바깥쪽에 조건이 조인 조건이 아니더라도 union all이 있는 

   뷰에서 해당 컬럼이 있을 시 동일하게 안쪽으로 조건절이 pushdown 된다.

    ( p.504 예시를 보면 이해하기 쉽다. ) 

 - union 집합 연산자를 포함한 뷰는 Non-mergeable view에 속해 뷰 Merging에 실패한다. 

   따라서 조건걸 Pushing을 통해서만 최적화가 가능하다 


조건절 Pullup 

  * 나만의 요약정리 

   일반적으로 조건절을 밖으로 빼냏어서 다시 조건절 pushdown 하기 위해서 사용된다. 

   즉 조인하려 2개의 view에서 한쪽에 조인 조건 컬럼에 검색 조건이 있고 조인 조건이 

   바깥에 있을 때, 안쪽에 있던 조인 조건 컬럼에 대한 또 다른 조건이 바깥쪽에  

   조인 조건을 타고 들어가서 다른 vies에 조인 조건 컬럼의 검색 조건이 적용된다.


조인 조건 Pushdown 

 -  조인 조건 Pushdown은 말 그대로 조인 조건절을 뷰 쿼리 블록 안으로 밀어 넣는 것으로서, 

    NL 조인 수행 중에 드라이빙 테이블에서 읽은 조인 컬럼 값을 Inner 쪽 뷰 쿼리 블록내에서

    참조할 수 있도록 하는 기능이다. 

 - 지금까지 보았던 조인문에서 조건절 Pushdown은 상수 조건이 조인 조건을 타고 전이된 것을 

   Pushing하는 기능이었던 반면, 조인 조건 Pushdown은 조인을 수행하는 중에 

   드라이빙 집합에서 얻은 값을 뷰 쿼리 블록 안에 실시간으로 Pushing하는 기능이다. 

   예시 ) 조인 조건은 deptno 이다. 그런데 다른 조건은 loc= 'CHICAGO'라고 한다면 

            loc가  'CHICAGO'인 deptno 값들이 들어가는 것이다. 

 - 실행계획상에서 'View Pushed Predicate' 오퍼레이션이 나타나는 것을 통해서도 

   조인 조건 Pushdown이 일어나는지 알 수 있다. 

 - 조인 조건 Pushdown을 제어하는 힌트 

     ● push_pred : 조인 조건 Pushdown을 유도한다. 

     ● no_push_pred : 조인 조건 Pushdown을 방지한다. 

 - 이를 제어하는 파라미터 

     ● _push_join_predite : 뷰 Merging에 실패한 뷰 안쪽으로 조인 조건을 Pushdown 하는 

                                    기능을 활성화한다.  

     ● _push_join_union_view : union all을 포함하는 Non-mergeable View 안쪽으로 

                                          조인 조건을 Pushdown 하는 기능을 활성한다. 

     ● _push_join_union_view2 : union을 포함하는 Non-mergeable view 안쪽으로 조인 조건을 

                                           Pushdown 하는 기능을 활성화한다.  

 - 위 항목은 10g 기준이며, 9i 에서는 _push_join_union_view2 파라미터가 없다. 

   9i에서 union all을 포함한 뷰에 대한 조인 조건 Pushdown은 작동하지만 

    union에는 작동하지 않는다는 뜻이다.  


Group by 절을 포함한 뷰에 대한 조인 조건 Pushdown 

 - group by를 포함하는 뷰에 대한 조인 조건 Pushdown 기능은 11g에 와서야 제공 시작 

   * 나의 의문증 

     여기서 궁금한 것은 10g에서 부터는 group by가 있어도 view merging이 나타난다. 

     그런데 view merging을 하지 않고 조인 조건 pushdown 하는게 더 나은 경우가 있는 건지를 

     잘 모르겠다. 

   * 스칼라 서브쿼리에서 집계 함수 

      스칼라 서브쿼리를 사용하면서 스칼라 서브쿼리의 select 절에서 집계함수를 사용하고 

      스칼라 서브쿼리에서 메인 쿼리와 조인을 하게 되면 조인 조건으로 group by 된 값들과 

      동일한 결과가 나타난다. 

      이 부분에 로직에 대해서 생각해보았는데 스칼라 서브쿼리는 캐싱 기능을 사용해야 하고 

      그렇게 되면 입력 값이 같다면 그 결고 값들도 같을 것이다. 그리고 입력된 값들만 

      뭉쳐야 하고 자동으로 group by 된 형태로 계산이 될 것이다. 

      이 부분에 대해서는 확실한 검증이 되지는 않았지만 테스트 결과로 보아서 

      스칼라 서브쿼리에서 집계함수는 메인 쿼리와 조인 조건으로 자동으로 group by 된 

      결과물을 가진다고 생각하고 나는 생각하고 있다. 


UNION 집합 연산을 포함한 뷰에 대한 조인 조건 Pushdown 

 - union 또는 union all을 포함한 뷰 쿼리 블록에 대한 조인 조건 Pushdown은 10g 이전부터 

   제공되던 기능이다. 

 - union all은 8.1.6부터, union은 10.1.0부터다. 

 - 9i에서 use_nl 힌트를 push_pred와 함께 사용하면 조인 조건 Pushdown 기능이 작동하지 않는 

   현상이 나타나므로 주의해야 한다. 

 - 이때는 push_pred 힌트만을 사용해야 하며, 조인 조건 pushdown은 NL조인을 전제로 하므로 

   굳이 use_nl 힌트를 사용할 필요는 없다. 


Outer 조인 뷰에 대한 조인 조건 Pushdown 

 - outer 조인에서 Inner 쪽 집합이 뷰 쿼리 블록일 때, 뷰 안에서 참조하는 테이블 개수에 따라 

   옵티마이져는 다음 2가지 방법 중 하나를 선택한다. 

    ( 뷰 안에서 참조하는 테이블 개수에 따라 ) 

    ● 뷰 안에서 참조하는 테이블이 단 하나일때, 뷰 Merging을 시도한다. 

    ● 뷰 내에서 참조하는 테이블이 두 개 이상일 때, 조인 조건식을 뷰 안쪽으로 Pushin하려고 

        시도한다. 

  * 좀 더 생각해 볼 점 

     p.513 에 있는 실행계획에서는 outer 조인이면 (+) 가 없는 쪽이 먼저 드라이빙 된다. 

     그런데 실행계획에서 가장 안쪽에 들어가 있는 것을 먼저 읽는다는 규칙을 생각하면 

     view 안에 테이블들을 먼저 읽은 것으로 생각된다. 

     그런데 사실 그 테이블을 먼저 읽으면 안된다. 쿼리에 대한 규칙 상이라면 그런데 

     이 부분을 규칙과도 연관되어서 해석해보자면 view안에 있는 테이블들을 먼저 읽어서 

     view를 먼저 정의 한다. 그러나 NL 조인 실행계획에서는 (+) 가 없는 쪽 테이블을 

     먼저 읽는 것으로 보아 NL 조인에서는 드라이빙 테이블이 다른 것으로 생각된다. 

     즉 view 안에 있는 테이블들을 먼저 읽어서 view를 규정하고 그 값을 기억한 후 

     NL 조인은 그에 따른 순선대로 진행된다는 것이 일단 지금 나의 생각이다.  

 - union 집합 연산자를 포함한 뷰에 대한 조인 조건 Pushdown은 10g부터 비용기반으로 

   작동하기 시작했지만 지금 설명한 Outer 조인 뷰에 대한 기능은 9i에서도 비용기반이었다. 

   'GROUP BY 절을 포함한 뷰에 대한 조인 조건 Pushdown'은 11g에 도입되면서부터 

   비용기반으로 작동한다. 

+ Recent posts