테이블 통계 

 - compute는 전수 검사, estimate는 표본 조사를 뜻한다. 

 - 통계정보를 수집할 때 이제는 analyze 명령어를 사용하지 말라는 것이 오라클의 

   공식적인 입장이다. 


인덱스 통계 

 - 인덱스 통계 사용시 analyze, dbms_stats 를 사용하는 방법 

 - 인덱스를 최초 생성하거나 재생성할 때 아래와 같이 compute statistic 옵셥을 주면 

    자동으로 인덱스 통계까지 수집된다. 

 - 인덱스는 이미 정렬돼 있으므로 통계정보 수집에 오랜 시간이 소요되지 않는다. 

 - 10g부터는 사용자가 이 옵션을 명시하지 않아도 오라클이 알아서 인덱스 통계까지 수집한다. 


컬럼 통계 

 - 테이블, 인덱스 통계를 제외하고 컬럼 통계만 수집하는 방법이다. 

 - size 옵션은 히스토그램의 최대 버킷 개수를 지정하는 옵셥으로서, 1부터 254까지 허용된다. 

   size를 명시하지 않으면 오라클이 75를 기본 값으로 사용하므로 히스토그램이 생성되지 않도록 

   하고 싶을 때는 size 옵션을 1로 명시해야 한다. 

 - 일부 컬럼에 대한 통계만 수집 가능하다. 

    ( 히스토그램 버킷 개수를 컬럼별로 따로 지정할 수도 있다. )

 - dbms_stats 패키지로 컬럼 통계만 따로 수집하느 방법은 없다. 

    ( 테이블 통계와 항상 같이 수집된다. )


시스템 통계

 - 시스템 통계가 포함하는 항목들 

     ● CPU 속도 

     ● 평균적인 Single Block I/O 속도 

     ● 평균적인 Multiblock I/O 속도 

     ● 평균적인 Multiblock I/O 개수 

     ● I/O 서브시스템의 최대 처리량 

     ● 병렬 Slave의 평균적인 처리량 

 - workload 시스템 통계 

     ● 9i에서 처음 도입된 workload 시스템 통계는, 애플리케이션으로부터 

         일정 시간 동안 발생한 시스템 부하를 측정, 보관함으로써 

          그 특성을 최적화 과정에 반영할 수 있게 한 기능이다.

     ● NoWorkload 시스템 통계는 오라클이 무작위로 I/O를 발생시켜 측정한 값이 반면 

         Workload 시스템 통계는 실제 애플리케이션에 발생하는 부하를 측정한 값이다. 


 - NoWorkload 시스템 통계 

    ● 관리자가 명시적으로 선택하지 않더라도 CPU 비용 모델이 기본 비용 모델로 사용되게 

        하려고 오라클 10g에서 NoWorkload 시스템 통계를 도입하였다. 

        CPU 비용 모델은 시스템 통계가 있을 때만 활성화되기 때문이다.

    ● Workload 시스템 통계를 수집하고 반영하는 순간 NoWorkload 시스템 통계는 무시된다. 

        Workload 시스템 통계가 수집되기 전까지는 공식을 이용해 추정된 값을 사용한다. 

    ● NoWorkload도 Workload와 마찬가지로 부하를 준 상태에서 측정된 값을 사용해야 

        시스템 통계로서 의미가 있다.

    ● Workload는 실제 애플리케이션에서 발생하는 부하를 기준으로 각 항목의 통계치를 

        측정하는 반면 NoWorkload는 모든 데이터파일 중에서 

        오라클이 무작위로 I/O를 발생시켜 통계를 수집한다.

    ●  NoWorkload 시스템 통계를 수집할 때는 dbms_stats_gather_system_stats 프로시저를 

         아무런 인자도 주지 않고 호출하거나 gathering_mode 인자를 'NOWORKLOAD' 로 

         지정하면 된다.

    ● 이 프로시저를 실행하는 동안 I/O 서브시스템에 약간의 부하가 발생하며, I/O 성능과 

        데이터베이스 크기에 따라 적게는 수초, 길게는 수 분이 소요될 수 있다.

부족한 옵티마이징 팩터 

 - 옵티마이저는 주어진 환경에서 최선을 다할 뿐 적절한 옵티마이징 팩터를 제공하는 것은 결국 

   사람의 몫 


부정화한 통계 

 - 샘플링 방식으로 통계를 수집할 때는 정확성 문제와 더불어 안정성 측면에도 관심을 기울여야 

    한다, 분포가 균등하지 않은 컬럼이라면 수집할 때마다 통계치가 바뀔 수 있어 실행계획을 

    불안정하게 만든다. 

 - 통계 수집 주기도 매우 중요하다. 


히스토그램의 한계 

 - 히스토그램 버킷 개수로 254개까지만 허용된다는 점도 옵티마이저에겐 주요한 제약사항이다. 


바인드 변수 사용 시 균등분포 가정 

 - 아무리 정확한 컬럼 히스토그램을 보유하더라도 바인드 변수를 사용한 SQL에는 무용지물이 

   되고 만다. 조건절에 바인드 변수를 사용하면 옵티마이저가 균등분포를 가정하고 

   비용을 계산하기 때문 


결합 선택도 산정의 어려움 

 - 조건절 컬럼이 서로 상관관계에 있으면 정확한 데이터 분포와 카디널리티를 산정하기 어려움 

 - 9i 부터는 동적 샘플링을 통해 이 문제를 해결하려고 시도, 소량의 데이터 샘플링을 통해 

    where 조건절에 사용된 두 개 이상 컬럼의 결합 분포를 구하는 기능으로, 동적 샘플링 레벨을 

    4 이상으로 설정할 때만 작동 

 - 11g부터는 사용자가 지정한 컬럼들에 대한 결합 선택도를 미리 수집해 두는 기능을 제공 


비현실적인 가정 

 - singleblock I/O 와 multiblock I/O 비용을 갖게 평가 

 - 캐싱 효과를 고려하지 않음 

 - 비용 평기시 디스크 I/O call 횟수만 더함

   * 옵티마이저는 메모리 자원에 대한 최악의 상황을 가정하고 비용을 산정하는 것인데, 

      이는 DW 환경에나 적합한 가정이다. 

 - 비현실적인 가정들을 보정할 수 있도록 오라클은 8i에서 아래 두 개의 파라미터를 제공 

     ● optimizer_index_caching 

     ● optimizer_index_cost_adj 


규칙에 의존하는 CBO 

 - 아무리 비용기반 옵티마이저라 하더라도 부분적으로는 규칙에 의존한다. 

 - 예를 들어, 원격 테이블이나 External 테이블에 대해서는 카디널리티, 평균 행 길이, 블록수 

   그리고 각종 인덱스 관련 통계항목들에 대해 고정된 상수 값을 사용한다. 

 - 9i 까지는 뷰 또는 서브쿼리를 만나면 무조건 쿼리 블록을 풀어 메인 쿼리와 Merging하려한다. 

 - 10g에서 대부분 쿼리 변환이 비용기반으로 개선 되었다. 


알파벳순 인덱스 선택 규칙 

 - CBO 에서, 두 대안 인덱스의 예상 비요이 같을 때 알파벳 순에서 앞선 것을 선택한다. 

 - 물리 데이터베이스 설계 단계에서 인덱스 명명 규칙을 정할 때 매우 중요한 시사점이다. 


하드웨어 성능 특성 

 - 오라클 9i부터 시스템 통계를 수집하는 기능이 도입되었다. 


동적 실시간 최적화 

 - 쿼리 최적화가 단일 SQL문 성능을 최적화하는 데 초점을 맞추는 반면, 동적 실시간 최적화는 

   수많은 SQL이 동시에 수행되는 환경에서 시스템 전체 최적화를 이루는 데 초점을 맞춘다. 

옵티마이저 행동에 영향을 미치는 요소 

 - SQL과 연산자 형태 

     ● 결과 같더라도 SQL을 어떤 형태로 작성했는지 또는 연산자를 사용했느지에 따라 

         옵티마이저가 다른 선택을 할 수 있고, 궁극적으로 쿼리 성능에 영향을 미친다. 

 - 인덱스, IOT, 클러스터링, 파티셔닝, MV 등 옵티마이징 팩터 

 - 제약 설정 : PK, FK, Not Null, Check 

     ● PK 제약과 옵티마이저 

          예시) 서브쿼리를 Unnesting 하고서 서브쿼리 테이블 기준으로 NL 조인하려 할 때, 

                  만약 고객 테이블에 PK 제약이 없다면 고객번호 중복을 제거한느 sort unique 

                  오퍼레이션을 먼저 수행해야 한다. 

     ● FK 제약과 옵티마이저 

          예시) 조인 제거 기능과 FK 제약이 있을 때만 작동하는 기능들이 여러 있는데, 

                  11g에 추가된 Reference 파티셔닝도 그 중 하나다. 

     ● Not Null 제약과 옵티마이저 

          예시) where 절 없이, group by 절을 사용하면 index가 있다면 index full scan 또는 

                  index fast full scan으로 빠르게 처리할 수 있다. 하지만 이 경우 not null 제약이 

                  있어야 하며 not null 제약이 없을 시 null 값이 입력될 가능성을 염두에 두고 

                  실행계획을 수립해야 하므로 테이블 전체를 스캔한다. 

     ● Check 제약과 옵티마이저 

          예시) check 제약이 있을 시 where 절에 조건에 따라 filter 작업이 발생하지 않는다. 

                 파티션 뷰에서 참조하는 테이블에 check 제약을 설정하면 파티션 pruning 기능 작동


  - 옵티마이저 힌트 

      ● 옵티마이저 힌트가 제대로 작동하지 않는 경우 

           - 문법적으로 맞지 않게 힌트를 기술 

           - 잘못된 참조 사용 

           - 의미적으로 맞지 않게 힌트를 기술 

           - 논리적으로 불가능한 엑세스 경로 

           - 버그 

      ● 옵티마이저 힌트에 관한 일반적인 사용 원칙은 다음과 같다. 

           - 가급적 힌트 사용을 자제하고, 옵티마이저가 스스로 좋은 선택을 할 수 있도록 돕는다. 

           - 옵티마이저가 잘못된 선택을 할 때만 힌트를 사용한다.   

  - 통계정보 : 오브젝트 통계, 시스템 통계 

      ● CBO의 모든 판단 기준은 통계정보에서 나온다. 

  - 옵티마이저 관련 파라미터

      ● 만약 옵티마이저의 갑작스런 변화를 원치 않는다면 optimizer_features_enable 파라미터

           를 이전 버전으로 설정하면 된다.  

  - DBMS 버전과 종류 

옵티마이저란 

 - 오라클은 10g 이후로 CBO만 지원함 


규칙기반 옵티마이저 

 - 휴리스틱 옵티마이저 

 - OLTP 환경의 중소형 데이터베이스 시스템이라면 RBO 규칙이 어느 정도 보편 타당성을 갖는다.

   그러나 RBO는 대용량 데이터를 처리하는데 있어 합리적이지 못할 때가 많다. 


비용기반 옵티마이저 

 - 전통적인 I/O 비용 모델에서의 I/O 요청 횟수만으로 비용을 평가하지 않고, 

    CPU 연산 비용까지 감안하여 수행 일량을 상대적인 시간 개념으로 환산해서 비용을 평가 

 - CBO가 실행계획을 수립할 때 판단 기준이 되는 비용은 어디까지나 예상치 

 - 오브젝트 통계뿐만 아니라 하드웨어적 특성을 반영한 시스템 통계정보까지 이용 


최적화 수행단계를 요약 

  ● 사용자가 던진 쿼리수행을 위해 , 후보군이 될만한 실행계획을 찾는다. 

  ● 데이터 딕셔너리에 미리 수집해 놓은 오브젝트 통계 및 시스템 통계정보를 이용해 

        각 실행계획의 예상비용을 산정한다. 

  ● 각 실행계획의 비용을 비교해서 최저비용을 갖는 하나를 선택한다. 

 

동적 샘플링 

 - 만약 테이블과 인덱스에 대한 통계정보가 없거나 너무 오래돼 신뢰할 수 없을 때 옵티마이저가

    동적으로 샘플링을 수행하도록 할 수 있다. 

 - 통계 정보가 생성되지 않으면 하드 파싱 할때마다 동적 샘플링을 위한 recursive call 발생 

    ( 성능이 좋지 못하다. ) 


CBO를 기준으로 SQL 처리 절차 요약 

   ● Query Transformer 

   ● Estimator 

   ● Plan Generator


옵티마이저 모드  

 - rule 

 - all_rows 

    ● DML 문자은 일부 데이터만 가공하고 멈출 수 없으므로 옵티마이저 모드에 상관없이 

        항상 all_rows 모드로 작동한다. 

    ● Select 문장도 union, minus 같은 집합 연산자나 for update 절을 사용시 

        all_rows 모드로 작동

    ● PL/SQL 내에서 수행되는 SQL도 힌트를 사용하거나 기본 모드가 rule인 경우를 제외하면 

        항상 all_rows 모드로 작동 

 - first_rows

    ● first_rows는 비용과 규칙을 혼합한 형태의 옵티마이저 모드다.  

    ● 얼마만큼 fetch 할지 지정하지 않았으므로 정확한 비용을 예측할 수 없고, 

         따라서 옵티마이저 내부적으로 정해진 규칙을 사용한다. 

    ●  first_rows는 이제 과거 버전과의 호환성을 위한 용도로만 남게 되었다.  

 - first_rows_n 

    ● n으로 지정할 수 있는 값은 1, 10, 100, 1000 네 가지며, 사용자가 지정한 n개 로우이상을 

        Fetch 한다면 오히려 더 많은 리소스를 사용하고 전체 수행 속도도 느려질 수 있다. 

        (파라미터 사용 시 ) 

    ● 힌트를 사용할 때는 괄호 안에 () 보다 큰 어떤 정수 값이라도 입력 가능하므로 

        파라미터를 이용 할 때보다 더 정밀하게 제어할 수 있다. 

    ● first_rows_n은 완전한 CBO 모드로 작동한다. 

 - choose 

    ● 엑세스되는 테이블 중 적어도 하나에 통계정보가 있다면 CBO, 그중에서도 all_rows 모드를 

        선택한다. 

    ● 9i 까지는 choose가 기본 설정이었으나 10g부터는 all_rows가 기본 옵티마이저 모드로 

        설정된다.

         ( 통계정보 없는 테이블을 발견하면 무조건 동적 샘플링이 일어나기 때문에 

           RBO로 작동할 일이 없어진 것이다. )


옵티마이저 모드 선택 

 - first_rows 모드가 효과적인 애플리케이션 아키텍처는 주로 2-Tier 환경의 클라이언트/서버 구조

   이 애플리케이션 구조의 특징은 전체 결과집합이 아무리 많아도 사용자가 스크롤을 통해 

   일부만 Fetch하다가 멈춘다는 점이다. 결과집합을 끝까지 Fetch 하거나 다른 쿼리를 수행하기 

   전까지 SQL 커서는 오픈된 상태를 유지한다. 

 - OLTP성 애플리케이션이라도 3-Tier 구조는 클라이언트와 서버 간 연겨을 지속하지 않는 

   환경이므로 오프 커서를 계속 유지할 수 없어 페이지 처리 기법을 주로 사용한다. 

   이는 대량의 데이터에서 일부만 fetch 하다 멈추는 것이 아니라 집합 자체를 소량으로 

   정의한다는 것이다. 

 - 애플리케이션 특성상 확실히 first_rows가 적합하다는 판단이 서지 않는다면 all_rows를 

   기본 모드로 선택하고, 필요한 쿼리 또는 세션 레벨에서 first_rows 모드로 전환할 것을 권고 

누적 매출 구하기 

 - 8i부터 제공되기 시작한 분석함수를 이용하면 간단함 

 - 분석함수를 이용할 수 없는 상황에서 부등호 조인과 group by를 이용해 구할 수 있음 


선분이력 끊기 

 - greate, least 함수를 기억하라 

   (max, min이 수직적인 계산이라면, greate, least 행으로의 계산으로 이해하면 쉽다. ) 


데이터 복제를 통한 소계 구하기 

 - 오라클 9i부터는 dual 테이블을 사용하면 편하다. 

  * grouping sets 에 대한 테스트 내용 

     - grouping sets를 사용하면서도 기본적으로 그룹함수가 사용되지 않음 컬럼들은 

       group by절에 모두 사용 되어야 한다. 

    

상호배타적 관계의 조인 

 - 어떤 엔터티가 두 개 이상의 다른 엔터티의 합집합과 관계를 갖는 것을 

   '상호배타적 관계' 라고 한다 . 

    ( 여기서는 합집합 관계를 갇는 것을 상호배타적이라고 해서 헷갈렸다. 

       아직도 이건 잘못된 정의로 쓴 것 같다. ) 

 - 상호배타적인 관계의 경우 2가지 방법으로 테이블을 구성할 수 있다. 

     두 개의 컬럼을 두어서 각각 해당 되는 컬럼에 값을 입력하고 나머지는 null 처리 방법 

       - 이 경우는 outer 조인으로 간단하게 쿼리를 작성할 수 있다. 

     한 개의 컬럼을 두어서 해당 컬럼에 '1,2' 와 같이 값을 나누어서 넣는 방법

       - 이 경우는 union all을 활용하거나 

       - where 절에 decode 함수나 outer 조인을 활용한다.


최종 출력 건에 대해서만 조인하기 

 - 조인을 완료하여 order by를 하고 rownum 를 뽑아서 필요한 부분만 처리하는 경우 

   부하가 심하다.

 - 해당 부분에 부하를 줄이기 위하여 하나의 테이블의 컬럼들로 order by를 진행할 수 있고 

    해당 테이블이 인덱스가 where 조건에서 사용 되고 그로인하여 필터링이 다량 되어 

    최종 결과를 크게 줄일 수 있을 때, 해당 테이블의 where절과 order by를 우선적으로 실행하고

    거에서 rownum 값을 뽑아서 다른 테이블들과 조인을 시도 하는 방법이 있다. 

      * 나의 생각 

          - 그러나 이런한 경우는 위와 같이 제약이 많이 때문에 잘 따져보고 사용해야 한다. 

 

징검다리 테이블 조인을 이용한 튜닝

 - from절에서 조인되는 테이블 개수를 늘려 성능을 향상시키는 사례 

 - 최종 결과 건수는 얼마 되지 않으면서, 필터 조건만으로 각 부분을 따로 읽으면 결과 

   건수가 아주 많을 때 튜닝하기가 가장 어렵다. NL 조인 과정에서 Random I/O 부하가 심하게 

   발생하기 때문이며, 어느 쪽으로 드라이빙하더라도 결과는 마찬가지다. 


인조 식별자 사용에 으한 조인 성능 이슈 

 - 인조 식별자를 잘못 사용하게 되면 인덱스에 조건절을 실제 쿼리사 사용하지 못하여 

    더 많은 스캔을 발생 시킬 수도 있다. 


인조 식별자를 둘 때 주의 사항 

 - 인조식별자를 두면 PK, FK가 단일 컬럼으로 구성되므로 테이블 간 연결 구조가 단순해지고, 

   제약조건을 위해 사용되는 인덱스 저장공간이 최소화되는 장점이 있다. 그리고 다중 컬럼으로

   조인할 때보다 아무래도 조인 연산을 위한 CPU 사용량이 조금 줄 수 있다. 

 - 하지만 조인 연산할 때의 CPU 사용량 감소느 ㄴ아주 미밈한 수준이고, 오히려 앞서 설명한 

   사례처럼 조인 연산 횟수와 블록 I/O증가로 더 많은 시스템 리소스를 낭비하기 쉽다.  

 - 논리적이 데이터 모델링 단계에서는 가급적 인조 식별자를 두지 않는 것이 좋다. 

 - 의미상 주어에 해당하는 속성들을 그대로 식별자로 사용했다가 나중에 물리 설계 단계에서 

   저장 효율과 엑세스 효율등을 고려해 결정하는 것이 바람직하다.  


점이력 조회 

 - 데이터 변경이 발생할 때마다 변경일자와 함께 새로운 이력 레코드를 쌓는 방식 

  * 점이력 조회시 주의사항 

     - 원래 스칼라 서브쿼리를 사용하게 되면 버퍼 pinning 효과가 사라진다. 

        그러나 그룹 함수를 사용하게 되면 buffer pinning효과가 나타난다. 

        하지만 이 경우도 그룹함수에 들어가는 컬럼이 가공되면 buffer pinning 효과가 사라진다. 

 - 만약 2개 이상의 컬럼을 읽어야 한다면 스칼라 서브쿼리 내에서 필요한 컬럼 문자열을 

   연겨랗고, 메인 쿼리에서 substr 함수로 잘라 쓰는 방법을 사용해야 한다. 

 - 컬럼이 많아지만 스칼라 서브쿼리에서 rowid 값만 취하고 고객별연체이력을 한 번 더 

   조인하는 방법을 생각해 볼 수 있다. 

   ( 이 경우도 where절로 인하여 해당 되는 row수가 크게 줄을 때 효과가 클 것이다. )

 - where 절에 스칼라 서브쿼리를 사용하여 해당 되는 row들을 찾는 경우 

   서브쿼리에서 조인 절을 사용하였다면 따로 조인 조건을 기술해줄 필요 없고 

   버퍼 pinning도 사용할 수 있을 것으로 생각된다. 


정해진 시점 기준으로 조회 

 - 같은 테이블을 두번 사용해서 한 번은 조건을 줄이고 (정해진 시점으로) 다시 자신의 테이블에 

    조인해서 필요한 컬럼들을 뽑아 낼 경우, 그냥 필요한 컬럼들을 한번에 붙여서 

    substr를 사용해서 필요한 컬럼들만 뽑아 낼 수 있다. 

 - 분석 함수(max, min () over ) 보다는 row_number , rank 함수가 성능성 유리한다고 한다. 

   (이유에 대한 부분은 나온 것은 없고 책에는 테스트를 통해서만 이야기 하였다. )  


선분이력 조인 

 - 과거/현재/미래의 임의 시점 조회 

     과거 시점 between and 사용 

     현재 시점 종료일자 = '99991231'

                   ( 미래일자를 미리 입력하는 경우 sysdate 와 between and 사용 )

     임의의 시점 조회시 

        - 바로 그 시점을 조회시에는 

            해당 시점의 컬럼 과 between을 사용해서 쿼리 

          현재 시점의 종목명과 상장주식수를 출력시 

            sysdate와 between 을 사용해서 쿼리 

 

선분이력 조인 튜닝 

 - 선분이력에서는 where 절에 입력되는 조건값에 따라서 인덱스의 순서도 중요하다. 

    ( where 절에 입력되는 조건 값에 따라서 시작일이나 종료일 중 무엇이 앞에 오는 것이 

       유리 할 지 달라진다. ) 

 - Between 조인 튜닝은 조회 대상이 많지 않을 때 

 - 일반 조인문으로는 index , rownum 힌트로 튜닝할 수 없지만 조인 컬럼이 unique 한 값으로 

   소량만 조인이 되는 것이라면 하나의 테이블에서 그 양을 줄여준 다음 스칼라 서브 쿼리를 

   통해 index, rownum힌트를 사용하여 튜닝하는 것도  하나의 방법이 될 수 있다.

   ( 스칼라 서브쿼리의 특징을 사용하여 튜닝하는 방법으로 생각 됨  )  

 - 위의 방법을 사용하여 여러개의 컬럼을 출력할 경우는 스칼라 서브 쿼리에서 rowid 를 

   출력하고 다시 한 번 해당 rowid를 통해서 자신과 조인하는 방법이 있다. 

   ( 스칼라 서브쿼리에서 한 번만 출력하고 substr로 잘라 쓰는 방법도 있다. ) 

 - 또한, where 절에 스칼라 서브 쿼리를 써서 rowid 조인을 할 수도 있다.  

   ( 이 부분은 쿼리 실행 해석이 좀 재미 있는 부분이다. ) 

   * 위의 3가지 경우는 모두 같은 조건이 성립되어야 원하는 성능 튜닝이 제대로 될 것이다. 


Between 조인 튜닝 - 조회 대상이 많지만 대상별 이력 레코드가 많지 않을 때 

 - 만약 전체 고객을 대상으로 한다면 Random 엑세스 위주의 NL 조인보다 아래처럼 해시 

   조인을 이용하는 것이 효과적이다. 


Between 조인 튜닝 - 대상별 이력 레코드가 많을 때 

 - 대상별 이력 레코드가 많을 때의 between 조인이 가장 튜닝하기 어렵다. 

   ( 해시 조인을 하더라도 해당 키 값에 중복되는 것들이 많아서 해시 버킷에 해시 체인들이 

     많이 들어가서 해시 버킷을 읽는데 시간이 오래걸린다.  )

 - 이럴 때 글쓴이는 첫번째, 두 개 이상 월에 걸치는 이력이 생기기 않도록 매월 말일 시점에 

   강제로 이력을 끊어주는 것이다. 

    ( 해시 체인을 스캔하는 비효율을 완전히 없앨 수는 없지만 최대 31개가 넘지 않도록 

     제한하려는 것이다. ) 

 - 두 개 이상 월에 걸치는 이력이 없도록 쿼리 시점에 선분이력을 변환해주는 것이다. 

   그런 다음 조인하는 방법은 앞에서와 같고, 마찬가지로 해시 체인을 스캔하는 양은 

   최대 31개로 제한될 것이다. 

   ( 이 방식을 사용하면 '일별상품거래'와 조인할 때는 빠르지만, '월도' 테이블과 조인하는 

    과정에서 오히려 병목이 생길 수 있다. ) 


조인에 실패한 레코드 읽기 

 - 그 in (' ', c.지역) , max(지역) 이 들어간 쿼리를 말한다. 

   ( 조인이 될 수 없는 값들이 맨 마지막 값으로 쿼리 되는 것 ) 

수정 가능 조인 뷰 활용 

 - 먼저 set 절에 여러 컬럼들은 해당 컬럼들이 한 테이블에 있는 컬럼들이라면 (,) 로 연결 하여 

    서브쿼리 사용 시 테이블을 한 번만 읽어서 쿼리 할 수 있다. 

 - 또한 update 해야 하는 테이블은 총 고객 수가 아주 많다면 exists 서브쿼리를 통해 

    해시 세미 조인으로 유도하는 것을 고려할 수 있다. 

 - set 절에 사용된 서브쿼리에는 캐싱 매커니즘이 작용하므로 distinct  value 개수가 적은 

    1쪽 집합을 읽어 M쪽 집합을 갱신할 때 효과적이다. 

    (즉 update 해야 할 테이블 M , 읽어야 할 테이블 1 일때 ) 

    (물론 exists 서브쿼리가 NL 세미 조인이나 필터방식으로 처리된다면 거기서도 캐싱 효과가 

     나타난다. ) 


수정 가능 조인 뷰 

 - 수정 가능 조인 뷰를 활용하면 참조 테이블과 두 번 조인하는 비효율을 없앨 수 있다. 

 - '조인 뷰'는 from 절에 두 개 이상 테이브을 가진 뷰를 가리킨다. 

 - '수정 가능 조인 뷰' 는 말 그대로 입력, 수정, 삭제가 허용되는 조인 뷰를 말한다. 

 - 단, 1쪽 집합과 조인되는 M쪽 집합에만 입력, 수정, 삭제가 허용된다.  

 - 1쪽 집합에 PK 제약을 설정하거나 Unique 인덱스를 생성해야 수정 가능 조인 뷰를 통한 

     입력/수정/삭제가 가능해진다. 

 - PK 제약을 설정하면 M쪽 집합은 '키-보존 테이블'이 되고, dept 테이블은 '비 키-보존 테이블'로 

     남는다. 


키 보존 테이블이란 

 - 조인된 결과집합을 통해서도 중복 값 없이 Unique하게 식별이 가능한 테이블 

 - 단적으로 말해 '키 보존 테이블'이란, 뷰에 rowid를 제공하는 테이블 

 -  *_UPDATABLE_COLUMNS 뷰를 통해 키 보존 테이블이 컬럼인지 아닌지 쉽게 확인할 수 있다.


수정가능 조인 뷰 제약 회피 

 - bypass_ujvc 힌트는 키 보본 테이블이 없더라도 update 수행이 가능하게 하는 힌트

 - update를 위해 참조하는 집합에 중복 레코드가 없을 때만 이 힌트를 사용해야 한다. 


Merge문 활용 

 - DW에서 가장 흔히 발생하는 오퍼레이션은, 기간계 시스템에서 가져온 신규 트랜잭션  

   테이터를 반영함으로써 두 시스템 간 데이터를 동기화시키는 작업 

 - bypass_ujvc 힌트가 필요할 때 아래와 같이 merge문으로 처리를 할 수도 있다. 

 - when not matched then 구문은 when matched then 에 해당 하지 않으며 

   있을 시 where 절에도 해당 하는 row들이 적용 된다. 

 - delete 절은 when matched then 절에서만 사용할 수 있다. 

   ( 또한 , update를 한 결과물로 제한이 되어서 실행 된다. ) 


다중 테이블 insert 활용 

 - 큰 테이블을 읽어서 여러 테이블에 insert 해야 할 때 큰 테이블을 한 번만 읽어서 

   여러 테이블에 insert를 효율적으로 할 수 있게 하는 기능 

 - insert first, insert all 이 있다. 

스칼라 서브쿼리 

 - 함수처럼 한 레코드당 정확히 하나의 값만을 리턴하는 서브쿼리를 '스칼라 서브쿼리' 라고 한다. 

 - 스칼라 서브쿼리는 주로 select-list 에서 사용되지만 몇 가지 예외사황을 뺀다면 컬럼이 

    올 수 있는 대부분 위치에서 사용 가능하다. 

 - 스칼라 서브쿼리를 이용해서 outer 조인문과 같은 결과를 낼 수도 있다. 

 - 스칼라 서브쿼리는 내부적으로 NL 조인과 수행하는 처리 경로도 동일하다. 

    ( 내부적으로 캐싱 기법이 작용한다는 점이 달라, 이를 이용한 튜닝이 자주 행해진다. ) 


스칼라 서브쿼리의 캐싱 효과 

 - 오라클은 스칼라 서브쿼리 수행횟수를 최소화하려고 그 입력 값과 출력 값을 내부 캐시에 

    저장해 둔다. 

 - 스칼라 서브쿼리가 수행될 때만 일단 '입력 값'을 캐시에서 찾아보고 거기 있으면 

    '저장된 출력 값' 을 리턴한다. 

 - 캐시에서 찾지 못할 때만 쿼리르 수행하며, 결과를 버리지 않고 캐시에 저장해 둔다.

    * 여기서 나의 해석을 더 하자면 

       - M:1 일 경우 1은 스칼라 서브쿼리의 테이블일 시 입력 값의 중복이 많기 때문에 

         동일한 결과 값을 사용할 수 있는 경우가 커서 활용도가 높다. 

 - 반복 수행되는 함수 때문에 쿼리 성능이 크게 저하될 때, 스칼라 서브쿼리를 덧씌움으로써 

   호출 횟수를 줄일 수 있다. 

    ( 그냥 사용자 정의 함수를 사용하게 되면 row 마다 함수가 호출 된다. 

       그러나 스칼라 서브 쿼리르 사용하게 되면 캐싱 기능을 사용할 수 있다. ) 

 - 입력 값과 출력 값을 빠르게 저장하고 찾기 위해 오라클은 해싱 알고리즘을 사용한다. 

 - 해시 충돌이 발생했을 때 기존 엔트리를 밀어내고 새로 수행한 입력 값과 출력값을 

    대체하는 것이 아닌 오라클은 기존 캐시 엔트리를 그대로 둔 채 스칼라 서브쿼리만 

    한 번 더 수행하고 만다. 따라서 해시 충동리 발생한 입력 값이 반복적으로 입력되면 

    스칼라 서브쿼리도 반복 수행된다. 

 - 결론적으로, 스칼라 서브쿼리의 캐싱 효과는 입력 값의 종류가 소수여서 해시 충돌 가능성이 

    적을 때랑야 효과가 있으며, 반대의 경우라면 캐시를 확인하는 비용 때문에 오히려 성능은 

    저하되고 CPU 사용률만 높게 만든다. 

 - 스칼라 서브쿼리를 사용하면 NL 조인에서 Inner 쪽 인덱스와 테이블에 나타나는 버퍼 Pinning

    효과도 사라진다는 사실을 기억할 필요가 있다.  


두 개 이상의 값을 리턴하고 싶을 때 

 - 구하고자 하는 값들을 모두 결합하고 바깥쪽 엑세스 쿼리에서 substr 함수로 분리하는 방법 

     ( || 을 사용해 in-line view 안에서 스칼라 서브쿼리르 한 번 사용하고 , 

       다시 바깥쪽에서 substr 를 이용해 잘라서 사용 ) 

 - 오브젝트 TYPE을 사용 , TYPE을 미리 선언해 두어야 하는 불편함이 있지만 

    SQL은 훨씬 깔끔해진다. 

Outer NL 조인 

 - Outer 기호(+)가 붙지 않은 테이블이 항상 드라이빙 테이블로 선택된다. 

    ( (+)가 붙은 쪽이 나머지를 null로 붙인다고 생각하면 편하다. ) 

 - outer 조인은 ERD 관계와 연관이 많기 때문에 ERD 표기법에 대한 이해가 필수이다. 

   ( ERD 표기법은 데이버베이스 프로그래머에게는 필수이기는 한다. ) 

   ( 엔터티 간 관계를 해석할 때도 카디널리티만 보지 말고 Optionality를 반드시 따져봐야한다. ) 

 - 혹시 있을지 모를 null 값을 두려워해 습관적으로 Outer 기호(+)를 붙인다면 성능상 

    불이익이 생길 수 있다. 


Outer 소트 머지 조인 

 - Outer 소트 머지 조인도 처리 방향이 한쪽으로 고정된다. 


Outer 해시 조인 

 - 9i에서 Outer 해시 조인을 수행해 보면, Outer 기호(+)가 붙지 않은 테이블이 항상 

     Build Input으로 선택된다. 

 - 해시 조인은 10g 부터 swap_join_input 힌트를 통해서 outer 조인의 순서 변경이 가능하다. 


9i 이전 버전에서 Outer 해시 조인 튜닝 

 - 테이블을 2번 사용하여 한 번은 최소한의 상태로 hash 조인 하고 조인 후 build input을 

     줄인 상태에서 한번 더 hash_join을 한다. 

 - 구간을 나눠 쿼리를 여러번 수행하는 방법도 생각해볼 수 있음 

   ( 해시 버킷당 엔트리 개수를 최소화 하면, 고객 테이브을 반복적으로 읽는 비효율에도 

     불구하고 더 바르게 수행될 수 있음 ) 


Full outer 조인 

 - Left Outer 조인 + Union All + Anti 조인 (Not Exists 필터) 이용 (9i 이전) 

 - ANSI Full outer 조인 ( 9i 이후 ) 

 - Native Hash Full Outer 조인 

    ● 11g 에서 'Native Hash Full Outer 조인'을 선보였고, 

        필요하면 10.2.0.4 버전에서도 Hidden 파라미터를 조정해 이 기능을 사용할 수 있음 

    ● 양쪽 테이블을 한 번씩 엑세스 한다는 것이 가장 큰 변화,  null인 레코드가 마지막에 

         출력된 것을 통해, 내부적으로 어떤 식으로 처리하는지 추정 가능 

 - Union All을 이용한 Full Outer 조인

    ● 두 테이블을 각각 한 번씩만 엑세스하였으며, 조인 대신 sort(또는 hash) group by 연산을 

        수행한다. 

       ( union all 을 사용할 때는 중복 제거가 없어 group by , sum 을 사용해야 한다.)  

소트 머지 조인과 해시 조인의 경우 

 - 소트 머지 조인의 경우 

    ● 소트 머지 조인은 PGA상에 정열된 집합을 통해 조인 엑세스가 일어나기 때문에 

          Random 엑세스 발생량보다는 소트 부하에 의해 성능이 결정된다. 

    ● 디스크 소트가 발생할 정도의 큰 테이블을 포함할 때는 큰 테이블을 드라이빙하는 것이 

          더 빠르지만, 

    ● 메모리 소트 방식으로 조인할 때는 작을 쪽 테이블을 드라이빙하는 것이 조금 더 빠르다. 

 - 해시 조인의 경우 

     해시 조인은 Hash Area에 build Input을 모두 채울 수 있느냐가 관건이므로 

       두말할 것도 없이 작은 쪽 테이블을 드라이빙하는 것이 유리하다. 

기본 메커니즘 

 - 둘 중 작은 집합(Build input)을 읽어 Hash Area에 해시 테이블을 생성하고, 

   반대쪽 큰 집합을 읽어 해시 테이블을 탐색하면서 조인하는 방식이다. 

 - 해시 조인은, NL 조인처럼 조인 과정에서 발생하는 Random 엑세스 부하가 없고 

   소트 조인처럼 조인 전에 미리 양쪽 집합을 정렬하는 부담도 없다. 

   (다만, 해시 테이블을 생성하는 비용이 수반된다. 

    따라서 Build Input이 작을 때라야 효과적이다.)

 - Build Input이 Hash Area 크기를 초과한다면 디스크에 썼다가 다시 읽어 들이는 과정을 

   거치기 때문에 성능이 많이 저하된다. 

 - 해시 키 값으로 사용되는 컬럼에 중복값이 거의 없을 때라야 효과적이다. 

 - 해시 테이블을 만드는 단계는 전체범위처리가 불가피하지만 반대쪽 Probe Input을 

   스캔하는 단계는 NL 조인처럼 부분범위처리가 가능하다. 

 - 해시 조인은 래치 획득 과정 없이 PGA에서 빠르게 데이터를 탐색한다. 

 

힌트를 이용한 조인 순서 및 Build Input 조정 

 - 테이블이 2개만 있을 시 ordered, leading 힌트와 use_hash 힌트만 가지고 build input 테이블 

    선택이 가능 

 - 테이블이 3개 이상일 시 swap_join_input 힌트를 통해서 Build input 테이블 순서 조절 가능


Build input이 Hash Area를 초과할 때 처리 방식 

 - Grace 해시 조인 

      파티션 단계 

         - 조인되는 양쪽 집합 모두 조인 컬럼에 해시 함수를 적용하고, 

            반환된 해시 값에 따라 동적으로 파티셔닝 실시

         - 파티션 단계에서 양쪽 집합을 모두 읽어 디스크 상의 Temp 공간에 일단 저장해야 하므로

           In-Memory 해시 조인보다 성능이 크게 떨어지게 된다. 

      조인 단계 

         - 파티션 단계가 완료되면 각 파티션 짝에 대해 하나씩 조인을 수행한다. 

           파티션하기 전 어느 쪽이 작은 테이블이었는지 상관없이 각 파티션 짝별로 작은 쪽 

           파티션을 Build Input으로 선택해 해시 테이블을 생성한다. 

         - 해시 테이블이 생성되고 나면 반대쪽 파티션 로우를 하나씩 읽으면서 해시 테이블을 

           탐색하며, 모든 파티션 짝에 대한 처리가 완료될 때까지 이런 과정을 반복한다.     


 - Hybrid 해시 조인 

      - 순서는 책에 있으나 크게 중요한 것인지 모르겠음 ( 다 쓰기 너무 길다...) 

      - 그래서 결국 중요한 것은 Grace 해시 조인과 비교해서 실제 조인에 성공할 만한 값들만 

         걸러네서 조인하는 것


 - Recursive 해시 조인 (= Nested-loops 해시 조인) 

      - 디스크에 기록된 파티션 짝끼리 조인을 수행하려고 '작은 파티션'을 메모리에 로드하는

        과정에서  또다시 가용 Hash Area를 초과하는 경우 추가 파티셔닝을 단계를 거치게 되는데,

        이를 'Recursive 해시 조인'이라고 한다. 

     -  Recursive 해시 조인을 'Multipass 해시 조인'이라고도 함 

        Onepass 해시 조인은 디스크 쓰기가 발생했지만 Multipass 오퍼레이션을 거치지 않은 경우

        Optimal 해시 조인dms 디스크를 전혀 사용하지 않은 In-Memory 해시 조인 

  

 - 비트- 벡터 필터링

     - 조인 성공 가능성이 없는 파티션 레코드는 아예 디스크에 기록되지 않게 하려는 것 

     - 조인 단계에서 실패할 수 밖에 없는 레코드를 디스크에 기록하지 않고 버린다면, 

        이를 다시 읽어 들여 조인하지 않아도 되므로 Grace Hash 조인의 성능을 

        크게 향상시킬 수 있다.

 Build input 해시 키 값에 중복이 많을 때 발생하는 비효율

     - 해시 알고리즘의 성능은 해시 충돌을 얼마나 최소화할 수 있느냐에 달렸다. 

       이를 방지하려면 그만큼 많은 해시 버킷을 할당해야 한다. 

     - 해시 버킷을 아무리 많이 할당하더라도 해시 테이블에 저장할 키 컬럼에 중복 값이 많다면 

       하나의 버킷에 많은 엔트리가 달릴 수 밖에 없다.   

     - 해시 조인에서 해시 키는 '=' 조인 컬러만으로 결정된다.


해시 조인 사용기준 

     ● 해시 조인 성능을 좌우하는 두가지 포인트 

         - 한 쪽 테이블이 Hash Area에 담길 정도로 충분히 작아야 함 

         - Build Input 해시 키 컬럼에 중복 값이 거의 없어야 함 

     ● 해시 조인을 효과적으로 사용하는 경우 

         - 조인 컬럼에 적당한 인덱스가 없어서 NL 조인이 비효율적일 때 

         - 조인 컬럼에 인덱스가 있더라도 NL 조인 드라이빙 집합에서 Inner 쪽 집합으로의 

            조인 엑세스량이 많아 Random 엑세스 부하가 심할 때 

         - 소트 머지 조인하기에는 두 테이블이 너무 커 소트 부하가 심할 때 

            ( 우선 등치 조건과 sort가 필요 없다는 것은 기본일 듯 ) 

         - 수행빈도가 낮고 쿼리 수행 시간이 오래 걸리는 대용량 테이블을 조인할 때 

     ● 해시 조인의 남용 시 발생할 수 있는 문제들 

         - 수행빈도가 높은 쿼리에 해시 조인을 사용하면 CPU와 메모리 사용률을 크게 증가 시킴

         - 래치 경합이 발생해 시스템 동시성을 떨어뜨리게 된다. 

            ( SQL memory manager workare 

              , row cache objects , simulator hash latch, cache buffers chains ) 

         - 단적으로 말해 OLTP 환경에서는 해시 조인을 쓰는 것은 가급적 자제해야 함 

+ Recent posts